Advanced Category Action
Installed as an WikkaBetaFeatures alpha feature on this server as of 2005-06-12.See also:
official
background and history
supporting code
related
This is the development page for a "more advanced" version of the category action. The version presented on this page is a further development of the beta version already running on this site (with as its main feature a more structural and readable columnar layout), borrows ideas from NilsLindenberg's PageAndCategoryDivisionInACategory, while adding new functionality and flexibility.official
background and history
supporting code
related
In fact, it takes the possibilities of the current category system to its practical limits in what it is capable of for category navigation. While a CategorySystemOverhaul category system overhaul definitely will be needed to support new functionality, this should help to tide us over until we have designed and implemented a new system.
Improvements and new functionality
Features
- Choose output format with the type parameter:
category context:
- cols produces a columnar output, with columns constructed as left-floated divs
- list produces an unordered list
page context:
- related is a special option that displays "related pages": pages in the same categories as the current page; this automatically sets some other options, such as list-format output and showing only pages
- Choose what to show with the show parameter:
- all shows both subcategories and pages, but separated
- categories shows subcategories only
- pages shows member pages only
- members shows all, but without separation between subcategories and pages (automatic when catnames is off (0) and the operation of the 1.1.6.0. category action)
- Select a category:
category context:
- choose what category to show members of with the cat parameter; default is the current category page
page context:
- choose a single category to show related pages for with the cat parameter (the current page must actually be a member of that category); default is all categories the current page belongs to
- Template pages (meant only to clone from and thus not real "content") are filtered out by default (except for a category for templates); override this with the inctpl parameter
- The assumption is that categories are named with 'Category' at the start; if not, this can be indicated with the catnames parameter; when 0, this will automatically select show = 'members' and disable other features like "related pages" that depend on this naming convention
- Output is wrapped in a div with class "categorycols", "categorylist" or "categoryrel" as an easy hook for styling; use the class parameter to add extra classes
- The automatically-generated (main) heading (also with an id) can be overridden with the head parameter
- Backwards-compatible with the version 1.1.6.0 parameters (page, col and compact); the new parameters override the old ones if both are present
Syntax
{{category [cat="categoryname"] [show="all|categories|pages|members"] [type="cols|list|related"] [cols="n"] [catnames="0|1"] [inctpl="0|1"] [class="class"] [head="main heading"]}}Use cases
later
The code
This code completely replaces the current ./actions/category.php file (make a backup if you want to try it out). Although it uses some new parameters it is still completely compatible with the current (1.1.6.0) category action; the new parameters take precence over the old ones though.
Note that this is still 'somewhat' beta code and currently contains (commented out) some debug code; this will be cleaned up later.
- <?php
- /**
- * Generates a list of pages belonging to the specified or top-level category.
- *
- * If no category page is specified, the current page is assumed to be a category (or the 'base' page
- * for the 'related pages' feature).
- *
- * Features:
- * - specify whether to show subcategories, pages, or both; separate or all together as "members"
- * - specify whether to format the output as columns or as a "flat" list
- * - list 'related pages': pages other than the current one that are in the same category or categories
- * (output will always be in the form of one or more lists)
- * - specify a class name as 'hook' for styling
- * - lists are generated as type 'menu' so they can be given "menu" styling
- * - specify a (main) heading to override the built-in defaults
- *
- * There are two possible "contexts" for the action:
- * - A category (either the current category page or specified with the cat parameter): used to list
- * members of that category. The action can be <b>used</b> on any page; if not a category page, the cat
- * parameter must be specified in order to list category members (otherwise the action will revert to
- * list the content of the top-level category).
- * - a content page for the 'related pages' feature. To list related pages, specify type='related' and
- * (optionally) the category to list (other) member pages of. If no category is specified, all
- * categories of the current page are considered; if a category is specified, it will be considered
- * only if it is actually one of the categories the current page belongs to.
- *
- * Several features are dependent on a naming convention where each category ("category page") starts
- * with 'Category'. While it's (technically) possible to have categories without this naming convention,
- * this makes features like separating subcategories from content pages in a listing impossible.
- * The default is to assume the naming convention is used; if not, override this behavior with a
- * catnames="0" parameter: the action will then ignore any contradictory parameters and revert to just
- * listing 'members' of the current or requested category.
- *
- * Template pages (created specifically to clone from) are normally not "real" content pages; the default
- * is to filter these pages (so only actual content pages are listed), which will only work if the
- * naming convention for them is followed by ending the page name with 'Template'. This behavior can
- * be overridden by specifying an inctpl="1" parameter. One exception: page names that start with
- * 'Category' and end with 'Template' are considered a proper category, intended to contain templates:
- * all members of such a 'template category' are considered even when the 'inctpl' parameter is set to 0.
- *
- * Note: the list view (type='list' or type='related') is nice for a sidebar while the columnar view
- * (type='cols') is more suited as content for a category page.
- *
- * Syntax:
- * {{category [cat="categoryname"] [show="all|categories|pages|members"] [type="cols|list|related"] [cols="n"] [catnames="0|1"] [inctpl="0|1"] [class="class"] [head="main heading"]}}
- *
- * Old (version 1) parameters are supported for compatibility but new ones take precedence.
- *
- * @todo - possible? use a single list also for columns, using CSS to visually split up into columns - JW 2005-01-19
- *
- * @package Actions
- * @subpackage SystemContent
- * @name Category
- *
- * @author {@link http://wikka.jsnx.com/JsnX JsnX}
- * @author {@link http://wikka.jsnx.com/NilsLindenberg NilsLindenberg} (separation into subcategories and pages)
- * @author {@link http://wikka.jsnx.com/JavaWoman JavaWoman} (complete rewrite, filtering, table-less columns and 'related pages' functionality)
- * @copyright Copyright © 2005, Marjolein Katsma
- * @license http://www.gnu.org/copyleft/lesser.html GNU Lesser General Public License
- * @since Wikka 1.0.0
- * @version 2.5beta
- *
- * @input string $cat optional: category to list members of; default: current page or CategoryCategory
- * You can specify '/' as a shortcut for CategoryCategory
- * @input string $show optional: all|categories|pages|members; default: all; or members if $catnames
- * is 0.
- * @input string $type optional: cols|list|related; default: cols.
- * - cols: multi-column display, list everything. (Useful for category pages)
- * - list: single list, strips 'Category' from category (page) names.
- * (Useful for sidebar)
- * - related: "related pages": list(s) with pages in the same categories as
- * the current page, or in requested category only; excluding the current page.
- * (useful for sidebar)
- *
- * @input integer $catnames optional: consider only category names (pages) that start with 'Category' as a
- * category (the name test is case-insensitive); default: 1
- * set to 0 if there is no naming convention for categories!
- * @input integer $inctpl optional: include "template" pages (1) or not (0); default: 0.
- * @input integer $cols optional: number of columns to use; default: 1 (only relevant for type"cols")
- * @input integer $class optional: class(es) to determine styling of the output list or columns
- * @input string $head optional: override of built-in main heading; you can specify a '%s' placeholder
- * for the requested category in a category members listing (type="cols|list")
- *
- * @input string $page DEPRECATED (optional): superseded by $cat (synonym)
- * @input integer $compact DEPRECATED (optional): use columns (0) or list (1); superseded by $type
- * @input integer $col DEPRECATED (optional): superseded by $cols
- *
- * @output string list of pages belonging to the specified or top-level category, formatted as a list
- * or columns of items
- *
- * @uses CheckMySQLVersion()
- * @uses getCatMembers()
- * @uses Link()
- * @uses makeId()
- * @uses makeMemberList()
- * @uses makeMemberCols()
- */
- // ----------------- utility functions ------------------
- {
- function buildMemberList($member,&$list,$cat,$bIncTpl)
- {
- global $wakka;
- if (
- ('CategoryCategory' == $member) || # do not list top-level category as member
- ($member == $cat) # do not list requested category as member
- )
- {
- #echo '(Members) Filtered: '.$member.'<br/>';
- // do nothing
- }
- else
- {
- }
- }
- }
- {
- function buildCatList($member,&$list,$cat,$type)
- {
- global $wakka;
- if (
- ('CategoryCategory' == $member) || # do not list top-level category as member
- ($member == $cat) # do not list requested category as member
- )
- {
- #echo '(Cats) Filtered: '.$member.'<br/>';
- // do nothing
- }
- else
- {
- if ('cols' == $type)
- {
- }
- elseif ('list' == $type)
- {
- }
- }
- }
- }
- {
- function buildPageList($member,&$list,$cat,$bIncTpl,$type,$page)
- {
- global $wakka;
- if (
- ('related' == $type && $member == $page) # current page filter
- )
- {
- #echo '(Pages) Filtered: '.$member.'<br/>';
- // do nothing
- }
- else
- {
- if ('related' == $type)
- {
- #echo "adding member $member for cat $cat<br/>";
- }
- else
- {
- #echo 'adding '.$member.'<br/>';
- }
- }
- }
- }
- // ----------------- constants and variables ------------------
- // constants
- // set defaults
- $lCat = NULL; # no predefined default
- $lShow = 'all'; # both categories and pages
- $lType = 'cols'; # default display type
- $bCatNames = TRUE; # only pages starting with 'Category' are considered category pages
- $bIncTpl = FALSE; # do not show template pages or treat a template as a category
- $lCols = 1; # one column for columnar layout
- $lClass = ''; # no class
- $lHead = NULL; # specified heading (may contain place holder for category)
- // initializations
- $bCatDefined = FALSE;
- // User-interface strings
- // --------------------------- processsing --------------------------
- // --------------- get parameters ----------------
- {
- foreach ($vars as $param => $value)
- {
- switch ($param)
- {
- // 1 - context
- case 'cat':
- if ($this->existsPage($value)) $tCat = $value;
- break;
- case 'page':
- if ($this->existsPage($value)) $tPage = $value;
- break;
- // 2 - what
- case 'show':
- break;
- // 3 - display type
- case 'type':
- #
- #if (isset($tType)) echo '(valid) input type: '.$tType.'<br/>';
- #
- break;
- case 'compact':
- if ($value === (string)(int)$value) $tCompact = (int)$value;
- break;
- // 4 - filters
- case 'catnames':
- if ($value == 0) $tCatNames = FALSE;
- #
- #if (isset($tCatNames)) echo 'CatNames FALSE<br/>';
- #
- break;
- case 'inctpl':
- if ($value == 1) $tIncTpl = TRUE;
- #
- #if ($value == 1) echo 'include template pages<br/>';
- #
- break;
- // 5 - presentation
- case 'cols':
- break;
- case 'col':
- break;
- case 'class':
- break;
- case 'head':
- }
- }
- }
- // ------------- process parameters --------------
- // filters
- #
- #if ($bCatNames) echo 'catnames on<br/>'; else echo 'catnames not on: cannot distinguish pages from categories!!<br/>';
- #
- // determine which category/categories to look at
- {
- $bCatDefined = TRUE;
- $tempCat = ('/' == $tCat) ? 'CategoryCategory' : $tCat; # '/' = shortcut for top-level category
- }
- {
- $bCatDefined = TRUE;
- $tempCat = ('/' == $tPage) ? 'CategoryCategory' : $tPage; # '/' = shortcut for top-level category
- }
- else
- {
- $tempCat = $this->tag; # fallback current page for no specified category
- }
- // derive display type
- {
- if ('related' == $tType && (!$bCatNames))
- {
- $lType = 'list'; # fallback if category not defined or we have no naming convention
- }
- else
- {
- $lType = $tType;
- }
- }
- {
- if (0 == $tCompact)
- {
- $lType = 'cols';
- }
- elseif (1 == $tCompact)
- {
- $lType = 'list';
- }
- }
- //else default 'cols'
- // final category (or array of categories) to consider
- // verify whether we have a valid category (page)
- {
- $lCat = 'CategoryCategory'; # fallback category
- }
- // find categories to consider for 'related'
- elseif ($bCatNames && 'related' == $lType)
- {
- if (!$bCatDefined)
- {
- #
- #$relstart = getmicrotime();
- #
- // replace every character not allowed in a page name by a space and build word array from the page
- // collect category names
- foreach ($aWords as $word)
- {
- // a word is only a category name if 'Category' is followed by something (case-insensitive check)
- }
- #
- /*
- printf('Page categories found in %.6f seconds<br/>',(getmicrotime() - $relstart));
- echo 'categories for the current page:<pre>';
- print_r($aCats);
- echo '</pre>';
- */
- #
- }
- else
- {
- // one category defined: use (only) that IF the specified category occurs on the page!
- }
- }
- else
- {
- $lCat = $tempCat;
- }
- // include templates? may depend on Category selected
- {
- $bIncTpl = $tIncTpl;
- }
- {
- $bIncTpl = TRUE; # do show templates in a 'template category'
- }
- // else default FALSE (don't show templates)
- // derive what to show
- if ($bCatNames) # assume naming convention
- {
- if ('related' == $lType) # overrides 'show' parameter for type 'related'
- {
- $lShow = 'pages';
- }
- {
- $lShow = $tShow;
- }
- // else default 'all'
- }
- else # cannot distinguish between pages and categories!
- {
- $lShow = 'members';
- }
- $bShowMixed = ('members' == $lShow);
- $bShowCats = ('all' == $lShow || 'categories' == $lShow) ? TRUE : FALSE;
- $bShowPages = ('all' == $lShow || 'pages' == $lShow) ? TRUE : FALSE;
- // --- presentation parameters
- // columns
- if ('cols' == $lType)
- {
- {
- $lCols = $tCols;
- }
- {
- $lCols = $tCol;
- }
- }
- // class
- // main heading override
- {
- $lHead = $tHead;
- }
- else
- {
- switch ($lType)
- {
- case 'cols':
- $lHead = HD_COL;
- break;
- case 'list':
- $lHead = HD_LST;
- break;
- case 'related':
- $lHead = HD_REL;
- break;
- }
- }
- // ---------------- gather data ------------------
- // get the category content
- if ('related' == $lType)
- {
- foreach ($aCats as $cat)
- {
- #
- #echo '- Looking at category: '.$cat.'<br/>';
- #
- $results = $this->getCatMembers($cat);
- if ($results)
- {
- foreach ($results as $cpage)
- {
- $member = $cpage['tag'];
- #
- #echo 'considering member: '.$member.'<br/>';
- #
- buildPageList($member,$pagelist,$cat,$bIncTpl,$lType,$this->tag);
- }
- }
- }
- }
- else
- {
- #
- #echo '- Looking at category: '.$lCat.'<br/>';
- #
- $results = $this->getCatMembers($lCat);
- // gather what we show AS content of the requested category
- if ($results)
- {
- foreach ($results as $cpage)
- {
- $member = $cpage['tag'];
- #
- #echo 'considering member: '.$member.'<br/>';
- #
- if ($bShowMixed)
- {
- buildMemberList($member,$memberlist,$lCat,$bIncTpl);
- }
- if ($bShowCats)
- {
- buildCatList($member,$catlist,$lCat,$lType);
- }
- if ($bShowPages)
- {
- buildPageList($member,$pagelist,$lCat,$bIncTpl,$lType,$this->tag);
- }
- }
- }
- }
- // ------------------ output ---------------------
- // show resulting list(s) of items belonging to selected category/categories
- $str ='';
- switch ($lType)
- {
- case 'cols':
- $attrClass = ('' != $lClass) ? ' class="categorycols '.$lClass.'"' : ' class="categorycols"';
- $head = ($bCatNames) ? sprintf($lHead,preg_replace('/^Category/','',$lCat)) : sprintf($lHead,$lCat);
- // start wrapper
- $str .= '<div'.$attrClass.'>'."\n";
- $str .= ' <h5 id="'.$this->makeId('hn','category members').'">'.$head.'</h5>'."\n";
- // members (undifferentiated)
- if ($bShowMixed)
- {
- $str .= $this->makeMemberCols($memberlist,$lCols,$hd,'category members',sprintf(COL_NONE_FOUND,$lCat));
- }
- // categories
- if ($bShowCats)
- {
- $str .= $this->makeMemberCols($catlist,$lCols,$hd,'category members',sprintf(COL_NO_CATS_FOUND,$lCat));
- }
- // pages
- if ($bShowPages)
- {
- $str .= $this->makeMemberCols($pagelist,$lCols,$hd,'category members',sprintf(COL_NO_PAGES_FOUND,$lCat));
- }
- // end wrapper
- $str .= "</div>\n";
- break;
- case 'list':
- $attrClass = ('' != $lClass) ? ' class="categorylist '.$lClass.'"' : ' class="categorylist"';
- // start wrapper
- $str .= '<div'.$attrClass.'>'."\n";
- $str .= ' <h5 id="'.$this->makeId('hn','category members').'">'.$head.'</h5>'."\n";
- // members (undifferentiated)
- if ($bShowMixed)
- {
- $hd = LST_MEMBERS_FOUND;
- $str .= $this->makeMemberList($memberlist,$hd,'category members','memberlist',LST_NONE_FOUND,'menu');
- }
- // categories
- if ($bShowCats)
- {
- $hd = LST_CATS_FOUND;
- $str .= $this->makeMemberList($catlist,$hd,'category members','catlist',LST_NO_CATS_FOUND,'menu');
- }
- // pages
- if ($bShowPages)
- {
- $hd = LST_PAGES_FOUND;
- $str .= $this->makeMemberList($pagelist,$hd,'category members','pagelist',LST_NO_PAGES_FOUND,'menu');
- }
- // end wrapper
- $str .= "</div>\n";
- break;
- case 'related':
- $attrClass = ('' != $lClass) ? ' class="categoryrel '.$lClass.'"' : ' class="categoryrel"';
- $head = $lHead;
- // start wrapper
- $str .= '<div'.$attrClass.'>'."\n";
- $str .= ' <h5 id="'.$this->makeId('hn','related pages').'">'.$head.'</h5>'."\n";
- // data lists
- {
- foreach ($pagelist as $cat => $memberlist)
- {
- $str .= $this->makeMemberList($memberlist,$hd,'related pages','rellist',REL_NONE_FOUND,'menu');
- }
- }
- else
- {
- $str .= ' <p>'.REL_NONE_FOUND.'</p>'."\n";
- }
- // end wrapper
- $str .= "</div>\n";
- break;
- }
- echo $str;
- ?>
Supporting code
As can be seen from the docblock (and the rest of the code) this new category action requires several bits of supporting code, already posted on other pages:
Constants
The code makes use of a number of constants. See ArrayToList for the code and how to add it where.
getCatMembers()
This is used to build a list of members belonging to a particular category - see CompatibilityCode for the code and where to insert it.
makeId()
Used here to generate a unique id for headings; see GenerateUniqueId for the code and where to insert it. See also AdvancedFormatter for a bit of background an context for this method.
makeMemberCols()
Used to build a columnar display of category members - see ArrayToColumns for the code and further details.
makeMemberList()
Used to build a list of category members - see ArrayToList for the code and further details.
Minor code modifications
Thought I'd toss these changes up here just in case anyone else is trying to make similar adjustments.
I changed the code so it would remove "Category" when it appears in front of a category name for show="all|categories|pages" (it doesn't work yet for show="members", though I would like to adjust that as well):
line 139:
to:
Then, to put spaces in CamelCase words in category displays, I made these changes to the code (requires UrlBeautify changes):
line 117:
to:
lines 139 and 143:
to:
lines 167 and 172:
to:
--MovieLady
CategoryDevelopmentActions