View Issue Details

IDProjectCategoryView StatusLast Update
4542Composrwikipublic2024-11-03 21:44
Reportermythus Assigned ToGuest  
PrioritynormalSeverityfeature 
Status newResolutionopen 
Summary4542: Previous, Up, Next Wiki Page Links/Buttons
DescriptionSo, I have this problem.... I have a large wiki with lots of categories and lots of pages in said categories. When I go into a category to look at a page, I sometimes want to go to the next page following it. Or sometimes to the previous page. To do so, I typically have to go back to the gategory tree listing and click on the next page there. What I propose is to have a set of buttons at the bottom of the page. These buttons would go to the previous page or next page in the tree listing order. The Up button would bring you to the front page of the category. This would help with navigating wiki pages and taking in information listed there.

Ideally it should be a dynamic box that the website designer could add to the bottom of the page (or top if desired I guess), much like the table of contents.
Steps To ReproduceTry checking out my site wiki just to see why I'd want this...?
Additional InformationThis is ideal for a Wiki, but it could also be useful for catalogues.
TagsHas Patch, Roadmap: Over the horizon
Attach Tags
Attached Files
set_nav.diff (24,234 bytes)   
diff --git a/sources/global3.php b/sources/global3.php
index 5df8027..5038a2d 100644
--- a/sources/global3.php
+++ b/sources/global3.php
@@ -582,6 +582,47 @@ function attach_to_screen_footer($data)
     $EXTRA_FOOT->attach($data);
 }
 
+/**
+ * Get header text.
+ *
+ * @return string Header text
+ */
+function get_header_text()
+{
+    global $ZONE, $SHORT_TITLE, $DISPLAYED_TITLE;
+    if ($ZONE === null) {
+        load_zone_data();
+    }
+    if ($SHORT_TITLE === null) { // Take from either zone header or screen title
+        if ($DISPLAYED_TITLE !== null) {
+            $_displayed_title = $DISPLAYED_TITLE->evaluate();
+        }
+        if (($DISPLAYED_TITLE !== null) && (strip_tags($_displayed_title) != '')) {
+            $value = strip_html(preg_replace('#<a[^<>]*>.*</a>#U', '', $_displayed_title)); // The regexp is to remove possible FRACTIONAL_EDIT.tpl link
+        } else {
+            if ($ZONE !== null) {
+                $value = get_translated_text($ZONE['zone_header_text']);
+            }
+        }
+    } else { // Take from short title
+        $comcodeless = strip_comcode($SHORT_TITLE); // This is not HTML
+
+        // Strip 'Welcome to' off if it's there
+        $value = cms_preg_replace_safe('#' . preg_quote(do_lang('WELCOME_TO_STRIPPABLE') . ' ' . get_site_name(), '#') . '([^\-]+\s*-\s*)?#', '', $comcodeless);
+
+        // Strip site name off it it's there (it'll be put on in the templates, so we don't want it twice)
+        $stub = get_site_name() . ' - ';
+        if (substr($value, strlen($stub)) == $stub) {
+            $value = substr($value, strlen($stub));
+        }
+        if ($value == get_site_name()) {
+            $value = '';
+        }
+    }
+    $value = trim($value);
+    return $value;
+}
+
 /**
  * Add some metadata for the request.
  *
diff --git a/sources/symbols.php b/sources/symbols.php
index 591305a..f5ab4a8 100644
--- a/sources/symbols.php
+++ b/sources/symbols.php
@@ -1741,37 +1741,7 @@ function ecv_PAGE($lang, $escaped, $param)
  */
 function ecv_HEADER_TEXT($lang, $escaped, $param)
 {
-    global $ZONE, $SHORT_TITLE, $DISPLAYED_TITLE;
-    if ($ZONE === null) {
-        load_zone_data();
-    }
-    if ($SHORT_TITLE === null) { // Take from either zone header or screen title
-        if ($DISPLAYED_TITLE !== null) {
-            $_displayed_title = $DISPLAYED_TITLE->evaluate();
-        }
-        if (($DISPLAYED_TITLE !== null) && (strip_tags($_displayed_title) != '')) {
-            $value = strip_html(preg_replace('#<a[^<>]*>.*</a>#U', '', $_displayed_title)); // The regexp is to remove possible FRACTIONAL_EDIT.tpl link
-        } else {
-            if ($ZONE !== null) {
-                $value = get_translated_text($ZONE['zone_header_text']);
-            }
-        }
-    } else { // Take from short title
-        $comcodeless = strip_comcode($SHORT_TITLE); // This is not HTML
-
-        // Strip 'Welcome to' off if it's there
-        $value = cms_preg_replace_safe('#' . preg_quote(do_lang('WELCOME_TO_STRIPPABLE') . ' ' . get_site_name(), '#') . '([^\-]+\s*-\s*)?#', '', $comcodeless);
-
-        // Strip site name off it it's there (it'll be put on in the templates, so we don't want it twice)
-        $stub = get_site_name() . ' - ';
-        if (substr($value, strlen($stub)) == $stub) {
-            $value = substr($value, strlen($stub));
-        }
-        if ($value == get_site_name()) {
-            $value = '';
-        }
-    }
-    $value = trim($value);
+    $value = get_header_text();
 
     if ($escaped !== array()) {
         apply_tempcode_escaping($escaped, $value);
@@ -5052,6 +5022,8 @@ function ecv_SMART_LINK_STRIP($lang, $escaped, $param)
                 $value .= '<br /><em>' . do_lang('LINKS_STRIPPED') . '</em>';
             }
         }
+    } elseif (isset($param[0])) {
+        $value = cms_strip_tags($param[0], '<a>', false);
     }
 
     if ($escaped !== array()) {
diff --git a/data_custom/set_raw_page.php b/data_custom/set_raw_page.php
new file mode 100644
index 0000000..894cfbb
--- /dev/null
+++ b/data_custom/set_raw_page.php
@@ -0,0 +1,44 @@
+<?php
+
+// Fixup SCRIPT_FILENAME potentially being missing
+$_SERVER['SCRIPT_FILENAME'] = __FILE__;
+
+// Find Composr base directory, and chdir into it
+global $FILE_BASE, $RELATIVE_PATH;
+$FILE_BASE = (strpos(__FILE__, './') === false) ? __FILE__ : realpath(__FILE__);
+$FILE_BASE = dirname($FILE_BASE);
+if (!is_file($FILE_BASE . '/sources/global.php')) {
+    $RELATIVE_PATH = basename($FILE_BASE);
+    $FILE_BASE = dirname($FILE_BASE);
+} else {
+    $RELATIVE_PATH = '';
+}
+if (!is_file($FILE_BASE . '/sources/global.php')) {
+    $FILE_BASE = $_SERVER['SCRIPT_FILENAME']; // this is with symlinks-unresolved (__FILE__ has them resolved); we need as we may want to allow zones to be symlinked into the base directory without getting path-resolved
+    $FILE_BASE = dirname($FILE_BASE);
+    if (!is_file($FILE_BASE . '/sources/global.php')) {
+        $RELATIVE_PATH = basename($FILE_BASE);
+        $FILE_BASE = dirname($FILE_BASE);
+    } else {
+        $RELATIVE_PATH = '';
+    }
+}
+@chdir($FILE_BASE);
+
+global $FORCE_INVISIBLE_GUEST;
+$FORCE_INVISIBLE_GUEST = false;
+global $EXTERNAL_CALL;
+$EXTERNAL_CALL = false;
+global $CSRF_TOKENS;
+$CSRF_TOKENS = true;
+global $STATIC_CACHE_ENABLED;
+$STATIC_CACHE_ENABLED = true;
+global $IN_SELF_ROUTING_SCRIPT;
+$IN_SELF_ROUTING_SCRIPT = true;
+if (!is_file($FILE_BASE . '/sources/global.php')) {
+    exit('<!DOCTYPE html>' . "\n" . '<html lang="EN"><head><title>Critical startup error</title></head><body><h1>Composr startup error</h1><p>The second most basic Composr startup file, sources/global.php, could not be located. This is almost always due to an incomplete upload of the Composr system, so please check all files are uploaded correctly.</p><p>Once all Composr files are in place, Composr must actually be installed by running the installer. You must be seeing this message either because your system has become corrupt since installation, or because you have uploaded some but not all files from our manual installer package: the quick installer is easier, so you might consider using that instead.</p><p>ocProducts maintains full documentation for all procedures and tools, especially those for installation. These may be found on the <a href="http://compo.sr">Composr website</a>. If you are unable to easily solve this problem, we may be contacted from our website and can help resolve it for you.</p><hr /><p style="font-size: 0.8em">Composr is a website engine created by ocProducts.</p></body></html>');
+}
+require($FILE_BASE . '/sources/global.php');
+
+require_code('sets');
+set_raw_page_script();
diff --git a/themes/default/templates_custom/NEWS_ENTRY_SCREEN.tpl b/themes/default/templates_custom/NEWS_ENTRY_SCREEN.tpl
new file mode 100644
index 0000000..cc8b3a7
--- /dev/null
+++ b/themes/default/templates_custom/NEWS_ENTRY_SCREEN.tpl
@@ -0,0 +1,107 @@
+{+START,INCLUDE,SET_NAV}
+	SET=site:news
+	VALID_NODE_TYPES=news
+	SKIP=
+	PAGE_LINK=site:news:view:{ID}
+{+END}
diff --git a/themes/default/templates_custom/GALLERY_ENTRY_SCREEN.tpl b/themes/default/templates_custom/GALLERY_ENTRY_SCREEN.tpl
new file mode 100644
index 0000000..3c73caa
--- /dev/null
+++ b/themes/default/templates_custom/GALLERY_ENTRY_SCREEN.tpl
@@ -0,0 +1,190 @@
+	{+START,INCLUDE,SET_NAV}
+		SET=site:galleries
+		VALID_NODE_TYPES=image,video
+		SKIP=
+		PAGE_LINK=site:galleries:{MEDIA_TYPE}:{ID}
+	{+END}
diff --git a/themes/default/templates_custom/DOWNLOAD_SCREEN.tpl b/themes/default/templates_custom/DOWNLOAD_SCREEN.tpl
new file mode 100644
index 0000000..dc6a167
--- /dev/null
+++ b/themes/default/templates_custom/DOWNLOAD_SCREEN.tpl
@@ -0,0 +1,227 @@
+{+START,INCLUDE,SET_NAV}
+	SET=site:downloads
+	VALID_NODE_TYPES=download
+	SKIP=
+	PAGE_LINK=site:downloads:entry:{ID}
+{+END}
diff --git a/themes/default/templates_custom/COMCODE_PAGE_SCREEN.tpl b/themes/default/templates_custom/COMCODE_PAGE_SCREEN.tpl
new file mode 100644
index 0000000..5eb9255
--- /dev/null
+++ b/themes/default/templates_custom/COMCODE_PAGE_SCREEN.tpl
@@ -0,0 +1,71 @@
+{+START,IF,{$NOR,{IS_PANEL},{BEING_INCLUDED}}}
+	{+START,INCLUDE,SET_NAV}
+		SET=:
+		VALID_NODE_TYPES=comcode_page,root
+		SKIP=site:,adminzone:
+		PAGE_LINK={$ZONE}:{+START,IF,{$NEQ,{NAME},start}}{NAME}{+END}
+	{+END}
+{+END}
diff --git a/themes/default/templates_custom/CNS_TOPIC_SCREEN.tpl b/themes/default/templates_custom/CNS_TOPIC_SCREEN.tpl
new file mode 100644
index 0000000..b9371f8
--- /dev/null
+++ b/themes/default/templates_custom/CNS_TOPIC_SCREEN.tpl
@@ -0,0 +1,10 @@
+{+START,INCLUDE,SET_NAV}
+	SET=forum:forumview
+	VALID_NODE_TYPES=topic
+	SKIP=
+	PAGE_LINK=forum:topicview:id={ID}
+{+END}
diff --git a/themes/default/templates_custom/CALENDAR_EVENT_SCREEN.tpl b/themes/default/templates_custom/CALENDAR_EVENT_SCREEN.tpl
new file mode 100644
index 0000000..3b01911
--- /dev/null
+++ b/themes/default/templates_custom/CALENDAR_EVENT_SCREEN.tpl
@@ -0,0 +1,10 @@
+{+START,INCLUDE,SET_NAV}
+	SET=site:calendar
+	VALID_NODE_TYPES=event
+	SKIP=
+	PAGE_LINK=site:calendar:view:{ID}
+{+END}
diff --git a/themes/default/javascript_custom/sets.js b/themes/default/javascript_custom/sets.js
new file mode 100644
index 0000000..61a83e6
--- /dev/null
+++ b/themes/default/javascript_custom/sets.js
@@ -0,0 +1,181 @@
+if (typeof window.set_cached_pages == 'undefined') {
+    window.set_cached_pages = {};
+    window.set_nav_locked = false;
+
+    window.addEventListener('popstate', _goto_set_page_history);
+
+    window.addEventListener('DOMContentLoaded', function() {
+        save_set_state(window.location.href);
+
+        // Code for swiping...
+
+        // https://gist.github.com/SleepWalker/da5636b1abcbaff48c4d
+        var pageWidth = window.innerWidth || document.body.clientWidth;
+        var horizontalThreshold = Math.floor(0.3 * pageWidth);
+        var verticalThreshold = 20; // Low, as we want to give up/down precedence to make it not swipe when scrolling
+        var touchstartX = 0;
+        var touchstartY = 0;
+        var touchendX = 0;
+        var touchendY = 0;
+
+        var limit = Math.tan(45 * 1.5 / 180 * Math.PI);
+        var gestureZone = document.getElementById('page_content');
+
+        gestureZone.addEventListener('touchstart', function(event) {
+            touchstartX = event.changedTouches[0].screenX;
+            touchstartY = event.changedTouches[0].screenY;
+        }, false);
+
+        gestureZone.addEventListener('touchend', function(event) {
+            touchendX = event.changedTouches[0].screenX;
+            touchendY = event.changedTouches[0].screenY;
+            handleGesture(event);
+        }, false);
+
+        function handleGesture(e) {
+            var navigate_to = null;
+
+            var x = touchendX - touchstartX;
+            var y = touchendY - touchstartY;
+            var xy = Math.abs(x / y);
+            var yx = Math.abs(y / x);
+            if (Math.abs(y) > verticalThreshold) {
+                // Up/down has precedence, as that's just scrolling the page
+                if (xy <= limit) {
+                    if (y < 0) {
+                        console.log("swipe up");
+                    } else {
+                        console.log("swipe down");
+                    }
+                }
+            } else if (Math.abs(x) > horizontalThreshold) {
+                if (yx <= limit) {
+                    if (x < 0) {
+                        navigate_to = document.getElementById('set_nav_next');
+                        console.log("swipe left");
+                    } else {
+                        navigate_to = document.getElementById('set_nav_prev');
+                        console.log("swipe right");
+                    }
+                }
+            } else {
+                console.log("tap");
+            }
+            if ((navigate_to !== null) && (navigate_to.href)) {
+                try {
+                    goto_set_page(navigate_to.href, true);
+                }
+                catch (e) {}
+            }
+        }
+    });
+}
+
+function goto_set_page(url, swipe)
+{
+    if (window.set_nav_locked) {
+        return;
+    }
+
+    if ((typeof window.set_cached_pages[url] == 'undefined') || (window.set_cached_pages[url] === false)) {
+        window.set_nav_locked = true;
+
+        _load_set_page(url, function(html, title, body_classname) {
+            window.set_nav_locked = false;
+
+            var page_content = document.getElementById('page_content');
+            if (page_content) {
+                set_inner_html(page_content,'<div aria-busy="true"><div class="spaced"><div class="ajax_loading vertical_alignment"><img id="loading_image" src="'+'{$IMG_INLINE*;,loading}'.replace(/^https?:/,window.location.protocol)+'" alt="{!LOADING;^}" /> <span class="vertical_alignment">{!LOADING;^}<\/span><\/div><\/div><\/div>');
+            }
+
+            _goto_set_page(url, html, title, body_classname, swipe);
+            save_set_state(url);
+        });
+        return false;
+    }
+
+    _goto_set_page(url, window.set_cached_pages[url].html, window.set_cached_pages[url].title, window.set_cached_pages[url].body_classname, swipe);
+    save_set_state(url);
+    return false;
+}
+
+function _goto_set_page(url, html, title, body_classname, swipe)
+{
+    if (!swipe) {
+        window.scrollTo(0, 0);
+    }
+
+    var page_content = document.getElementById('page_content');
+
+    set_inner_html(page_content, html);
+    document.title = title;
+    document.body.className = body_classname;
+
+    window.setTimeout(function() {
+        var prev=document.getElementById('set_nav_prev');
+        if (prev) cache_set_page(prev.href);
+
+        var next=document.getElementById('set_nav_next');
+        if (next) cache_set_page(next.href);
+    },0);
+
+    change_menu_highlighting(url);
+}
+
+function change_menu_highlighting()
+{
+    var elements = document.getElementsByClassName('menu');
+    for (var i = 0; i < elements.length; i++) {
+        menu_active_selection(elements[i].id,true);
+    }
+}
+
+function save_set_state(url)
+{
+    var page_content = document.getElementById('page_content');
+
+    var html = get_inner_html(page_content);
+    var title = document.title;
+    var body_classname = document.body.className;
+
+    window.history.pushState({url: url, html: html, title: title, body_classname: body_classname}, title, url);
+}
+
+function _goto_set_page_history(event)
+{
+    if (!event.state) {
+        window.history.back();
+        return;
+    }
+    var url = event.state.url;
+    var html = event.state.html;
+    var title = event.state.title;
+    var body_classname = event.state.body_classname;
+    _goto_set_page(url, html, title, body_classname);
+}
+
+function cache_set_page(url)
+{
+    if (typeof window.set_cached_pages[url] != 'undefined') {
+        // Already cached
+        return;
+    }
+
+    _load_set_page(url);
+}
+
+function _load_set_page(url, then)
+{
+    var _url = '{$FIND_SCRIPT;,set_raw_page}?url=' + window.encodeURIComponent(url);
+
+    do_ajax_request(_url, function(ajax_result) {
+        var html = ajax_result.responseText;
+        var title = ajax_result.getResponseHeader('x-title');
+        var body_classname = ajax_result.getResponseHeader('x-body-classname');
+
+        window.set_cached_pages[url] = {html: html, title: title, body_classname: body_classname};
+        if (then) {
+            then(html, title, body_classname);
+        }
+    });
+}
diff --git a/sources_custom/sets.php b/sources_custom/sets.php
new file mode 100644
index 0000000..6d88cf3
--- /dev/null
+++ b/sources_custom/sets.php
@@ -0,0 +1,98 @@
+<?php
+
+function set_raw_page_script()
+{
+    $url = get_param_string('url', false, true);
+    $page_link = url_to_page_link($url);
+    list($zone, $map) = page_link_decode($page_link);
+
+    require_code('urls2');
+    set_execution_context($map, $zone, 'set_raw_page');
+
+    $page = isset($map['page']) ? $map['page'] : get_zone_default_page($zone);
+
+    process_url_monikers($page);
+
+    prepare_for_known_ajax_response();
+    header('Content-type: text/plain; charset=' . get_charset());
+
+    // Check permissions
+    $zones = $GLOBALS['SITE_DB']->query_select('zones', array('*'), array('zone_name' => $zone), '', 1);
+    if (!array_key_exists(0, $zones)) {
+        warn_exit(do_lang_tempcode('MISSING_RESOURCE'));
+    }
+    if (($zones[0]['zone_name'] != '') && (get_value('windows_auth_is_enabled') !== '1') && ((get_session_id() == '') || (!$GLOBALS['SESSION_CONFIRMED_CACHE'])) && (!is_guest()) && ($zones[0]['zone_require_session'] == 1) && (!is_guest())) {
+        access_denied('ZONE_ACCESS_SESSION');
+    }
+    if (!has_actual_page_access(get_member(), $page, $zone)) {
+        access_denied('ZONE_ACCESS');
+    }
+
+    // Closed site
+    $site_closed = get_option('site_closed');
+    if (($site_closed == '1') && (!has_privilege(get_member(), 'access_closed_site')) && (!$GLOBALS['IS_ACTUALLY_ADMIN'])) {
+        @exit(get_option('closed'));
+    }
+
+    header('x-title: ' . get_header_text() . ' - ' . get_site_name()); // Can't use ndash because the unicode contains an ASCII new line and complains
+    header('x-zone: ' . $zone);
+    header('x-page: ' . $page);
+    header('x-body-classname: website_body zone_running_' . $zone . ' page_running_' . $page);
+
+    safe_ini_set('ocproducts.xss_detect', '0');
+
+    // Load page
+    $output = request_page($page, true, $zone);
+    $output->handle_symbol_preprocessing();
+
+    $out = new Tempcode();
+    $out->attach(symbol_tempcode('CSS_TEMPCODE'));
+    $out->attach(symbol_tempcode('JS_TEMPCODE'));
+    $out->attach($output);
+    $out->attach(symbol_tempcode('JS_TEMPCODE', array('footer')));
+
+    echo $out->evaluate();
+}
+
+function set_navigation($page_link_current, $offset_diff, $type)
+{
+    global $SITEMAP_SET;
+    if (!isset($SITEMAP_SET)) {
+        return 'No set loaded';
+    }
+
+    static $cache = array();
+
+    if ($page_link_current === null) {
+        $page_link_current = url_to_page_link(get_self_url_easy());
+    }
+
+    if (isset($cache[$page_link_current][$offset_diff][$type])) {
+        return $cache[$page_link_current][$offset_diff][$type];
+    }
+
+    if (!isset($SITEMAP_SET['index'][$page_link_current])) {
+        return '';
+    }
+    $current_pos = $SITEMAP_SET['index'][$page_link_current];
+
+    $set = $SITEMAP_SET[$type];
+
+    if (!isset($set[$current_pos + $offset_diff])) {
+        return '';
+    }
+    $_sequential = $set[$current_pos + $offset_diff];
+
+    if ($type == 'page_links') {
+        $sequential = page_link_to_url($_sequential);
+    } else {
+        $sequential = $_sequential;
+        if (($type == 'titles') && (trim($sequential) == '')) {
+            $sequential = '(Unnamed)';
+        }
+    }
+
+    $cache[$page_link_current][$offset_diff][$type] = $sequential;
+
+    return $sequential;
+}
diff --git a/sources_custom/hooks/systems/symbols/SET_LOAD.php b/sources_custom/hooks/systems/symbols/SET_LOAD.php
new file mode 100644
index 0000000..f3df10d
--- /dev/null
+++ b/sources_custom/hooks/systems/symbols/SET_LOAD.php
@@ -0,0 +1,54 @@
+<?php
+
+class Hook_symbol_SET_LOAD
+{
+    public function run($param)
+    {
+        if (!isset($param[0])) {
+            return '';
+        }
+
+        disable_php_memory_limit();
+
+        $page_link = $param[0];
+
+        $valid_node_types = empty($param[1]) ? null : explode(',', $param[1]);
+        $skip = empty($param[2]) ? array() : explode(',', $param[2]);
+
+        global $SITEMAP_SET;
+
+        // Read from cache
+        $set = has_caching_for('block') ? get_cache_entry('set_' . $page_link, serialize($param), CACHE_AGAINST_PERMISSIVE_GROUPS, 60 * 24) : null;
+
+        if ($set === null) {
+            // Not in cache
+            require_code('sitemap');
+            $node = retrieve_sitemap_node($page_link);
+            $page_links = array();
+            $titles = array();
+            $this->get_page_links($node, $skip, $valid_node_types, $page_links, $titles);
+            $SITEMAP_SET = array('index' => array_flip($page_links), 'page_links' => $page_links, 'titles' => $titles);
+
+            // Cache
+            require_code('caches2');
+            put_into_cache('set_' . $page_link, 60 * 24, serialize($param), null, null, permissive_groups_cache_signature(), null, '', $SITEMAP_SET);
+        } else {
+            $SITEMAP_SET = $set;
+        }
+
+        return '';
+    }
+
+    protected function get_page_links($node, $skip, $valid_node_types, &$page_links, &$titles)
+    {
+        if ((!in_array($node['page_link'], $skip)) && (($valid_node_types === null) || (in_array($node['content_type'], $valid_node_types)))) {
+            $page_links[] = $node['page_link'];
+            $titles[] = $node['title'];
+        }
+        if (isset($node['children'])) {
+            foreach ($node['children'] as $subnode) {
+                $this->get_page_links($subnode, $skip, $valid_node_types, $page_links, $titles);
+            }
+        }
+    }
+}
diff --git a/sources_custom/hooks/systems/symbols/SET_NEXT_PAGE_LINK.php b/sources_custom/hooks/systems/symbols/SET_NEXT_PAGE_LINK.php
new file mode 100644
index 0000000..e836c83
--- /dev/null
+++ b/sources_custom/hooks/systems/symbols/SET_NEXT_PAGE_LINK.php
@@ -0,0 +1,12 @@
+<?php
+
+class Hook_symbol_SET_NEXT_PAGE_LINK
+{
+    public function run($param)
+    {
+        $page_link_current = isset($param[0]) ? $param[0] : null;
+
+        require_code('sets');
+        return set_navigation($page_link_current, 1, 'page_links');
+    }
+}
diff --git a/sources_custom/hooks/systems/symbols/SET_NEXT_TITLE.php b/sources_custom/hooks/systems/symbols/SET_NEXT_TITLE.php
new file mode 100644
index 0000000..074350c
--- /dev/null
+++ b/sources_custom/hooks/systems/symbols/SET_NEXT_TITLE.php
@@ -0,0 +1,12 @@
+<?php
+
+class Hook_symbol_SET_NEXT_TITLE
+{
+    public function run($param)
+    {
+        $page_link_current = isset($param[0]) ? $param[0] : null;
+
+        require_code('sets');
+        return set_navigation($page_link_current, 1, 'titles');
+    }
+}
diff --git a/sources_custom/hooks/systems/symbols/SET_PREV_PAGE_LINK.php b/sources_custom/hooks/systems/symbols/SET_PREV_PAGE_LINK.php
new file mode 100644
index 0000000..d5fee56
--- /dev/null
+++ b/sources_custom/hooks/systems/symbols/SET_PREV_PAGE_LINK.php
@@ -0,0 +1,12 @@
+<?php
+
+class Hook_symbol_SET_PREV_PAGE_LINK
+{
+    public function run($param)
+    {
+        $page_link_current = isset($param[0]) ? $param[0] : null;
+
+        require_code('sets');
+        return set_navigation($page_link_current, -1, 'page_links');
+    }
+}
diff --git a/sources_custom/hooks/systems/symbols/SET_PREV_TITLE.php b/sources_custom/hooks/systems/symbols/SET_PREV_TITLE.php
new file mode 100644
index 0000000..79f09f1
--- /dev/null
+++ b/sources_custom/hooks/systems/symbols/SET_PREV_TITLE.php
@@ -0,0 +1,12 @@
+<?php
+
+class Hook_symbol_SET_PREV_TITLE
+{
+    public function run($param)
+    {
+        $page_link_current = isset($param[0]) ? $param[0] : null;
+
+        require_code('sets');
+        return set_navigation($page_link_current, -1, 'titles');
+    }
+}
diff --git a/sources_custom/hooks/systems/decache/sets.php b/sources_custom/hooks/systems/decache/sets.php
new file mode 100644
index 0000000..a26c7aa
--- /dev/null
+++ b/sources_custom/hooks/systems/decache/sets.php
@@ -0,0 +1,30 @@
+<?php
+
+class Hook_decache_sets
+{
+    function decache($cached_for, $identifier)
+    {
+        switch ($cached_for) {
+            case 'main_news':
+                decache('set__site:news');
+                break;
+
+            case 'main_image_slider':
+                decache('set__site:galleries');
+                break;
+
+            case 'side_cns_private_topics':
+                decache('set__forum:topicview');
+                break;
+
+            case 'side_calendar':
+                decache('set__site:calendar');
+                break;
+
+            case 'main_comcode_page_children':
+                decache('set__');
+                break;
+        }
+    }
+}
+
set_nav.diff (24,234 bytes)   
Time estimation (hours)2
Sponsorship open

Sponsor

Date Added Member Amount Sponsored

Relationships

related to 2076 Not AssignedGuest Horizontal swiping on mobile mode 

Activities

Chris Graham

2021-01-03 03:33

administrator   ~6888

I was feeling generous, and new features aren't going to be considered any time soon, so here's a block that does what you want.

For it to just go horizontally across parents of the same parent category:
[block multi_level="0"]wiki_nav[/block]

For it to do full tree traversal:
[block multi_level="1"]wiki_nav[/block]

Or you can put it in the WIKI_PAGE_SCREEN.tpl template like:
{$BLOCK,block=wiki_nav,multi_level=1}

Full tree traversal is less efficient as it has to load the whole tree, but I did cache the tree so it shouldn't be too bad. That's why there's an upon_query hook, to flush out the cached tree if the Wiki+ tree is changed.

The block looks at the current ID in the URL to see where the user is currently at. So it only is going to work when used directly within Wiki+. Not that you'd want to place it outside of Wiki+.
wiki_nav.zip (2,076 bytes)

Guest

2021-01-03 03:59

reporter   ~6889

You are absolutely awesome Chris. Do you have a "buy me a beer" function yet? I am glad to see it as a block, TBH. That makes it all the more versitile IMO. I of course just went and put it in the TPL, dressed it up in a div so I could center it.

It works, of course. The odd thing is the back and forward seem to be going based on page name in alphabetical order, instead of ID #, despite looking at your php code and I swear you have it wrote to go by ID #. No biggie though, it still works for what I want it to do. Maybe I can brush off an old php book and learn a little if it bothers me too much. It is at least keeping the pages in their categories.

Thanks again Chris, you rock.

Chris Graham

2021-01-03 04:09

administrator   ~6890

Here's my Patreon if you like:
https://www.patreon.com/composr

It's ordering by the the_order field in the wiki_children table.
I am wondering if somehow that field became defunct intentionally, or if I forgot - because I can't see it being used in the code.

I'll take a look tomorrow.

Chris Graham

2021-01-03 16:46

administrator   ~6892

Last edited: 2021-01-03 16:46

Ok, so it's not meant to be ID order, it's meant to be the the_order field, which is different from any IDs.

And it's not, because of a bug in the sitemap code I just found, hot-fix in this issue:
https://compo.sr/tracker/view.php?id=4546

The block code did use the_order, but that was only actually in effect if multi_level=0.

Chris Graham

2021-01-03 16:47

administrator   ~6893

Oh and the block cache needs emptying after applying that hotfix.

mythus

2021-01-04 00:53

developer   ~6894

It works beautifully sir. Thank you so much!

Chris Graham

2021-03-15 18:49

administrator   ~7016

Last edited: 2023-02-21 02:01

I made a Composr-wide implementation of next/previous for a client, and have a patch. I haven't compared to this issue, but there is a lot of similarity. I've tagged this issue as v12, although I'm more thinking about the patch I made for the client than what I posted here as I like the idea of a consistent out-of-the-box Composr-wide solution that also supports mobile swiping (2076).

EDIT: Patch is now outdated, would need refreshing.

mythus

2024-11-03 21:44

developer   ~9556

Just to update this no longer works in Composr v11

Add Note

View Status
Note
Upload Files
Maximum size: 32,768 KiB

Attach files by dragging & dropping, selecting or pasting them.
You are not logged in You are not logged in. This means you will not get any e-mail notifications. And if you reply, we will not know for sure you are the original poster of the issue.

Issue History

Date Modified Username Field Change
2021-01-02 01:04 mythus New Issue
2021-01-03 03:33 Chris Graham File Added: wiki_nav.zip
2021-01-03 03:33 Chris Graham Note Added: 0006888
2021-01-03 03:59 Guest Note Added: 0006889
2021-01-03 04:09 Chris Graham Note Added: 0006890
2021-01-03 16:46 Chris Graham Note Added: 0006892
2021-01-03 16:46 Chris Graham Note Edited: 0006892
2021-01-03 16:47 Chris Graham Note Added: 0006893
2021-01-04 00:53 mythus Note Added: 0006894
2021-03-15 18:47 Chris Graham Tag Attached: Roadmap: v12
2021-03-15 18:48 Chris Graham Relationship added related to 2076
2021-03-15 18:49 Chris Graham Note Added: 0007016
2021-07-30 20:46 Chris Graham Time estimation (hours) => 2
2021-11-01 20:12 Chris Graham Tag Attached: Has Patch
2021-11-02 03:38 Chris Graham File Added: set_nav.diff
2021-11-03 01:25 Chris Graham File Deleted: set_nav.diff
2021-11-03 01:25 Chris Graham File Added: set_nav.diff
2023-02-21 02:01 Chris Graham Note Edited: 0007016
2024-03-26 00:58 PDStig Tag Renamed Roadmap: v12 => Roadmap: Over the horizon
2024-11-03 21:44 mythus Note Added: 0009556