Cluster
Questions answered by this recipe
How can I cluster my wiki-groups so they behave like a hierarchy of groups?
Description
Group-clustering recipe.
This recipe enables wiki-pages to be clustered, so that they can share and inherit common information:
- group headers
- group footers
- group attributes
- config files
- CSS style sheets
This also provides useful page-variables, and link short-cuts.
Activation
To activate this recipe, download cluster.phpΔ and put it into your cookbook directory.
Add the following line to your local/config.php
:
include_once("$FarmD/cookbook/cluster.php");
Configuration variables
These can be set in local/config.php
Name | Description | Default |
$ClusterSeparator | Define the character used to separate the different parts of a group name | '-' |
$ClusterMaxLevels | Define the maximum number of levels to split group names by | 7 |
$ClusterBreadCrumbSeparator | Define the string used to separate the different parts of a breadcrumb-trail in the $BreadCrumb page-variable (See Using the BreadCrumb below) | ' - ' |
$ClusterEnableBreadCrumbName | If true, this will append the name of the page to the $BreadCrumb page-variable; if false, it will only make a breadcrumb from the group (See Using the BreadCrumb below) | true |
$ClusterEnableTitles | If true, this will use titles for the labels of the links in (:clusterslice:) and $BreadCrumb page-variable; if false, it will use names (See Using the BreadCrumb below) | false |
$ClusterEnableSpaces | If true, this will use spaced names for the labels of the links in (:clusterslice:) and $BreadCrumb page-variable; if false, it will use names (See Using the BreadCrumb below) | false |
$ClusterEnableNameClustering | If true, this will cluster on page-names as well as group-names; if false, only groups are clustered. | false |
$GroupTitlePathFmt | The path to search for group titles. | array('$Group.GroupAttributes', '$Group.GroupHeader', '$Group.GroupFooter', '$Group.Group', "\$Group.$DefaultName") |
How The Clustering Works
Let's use the common Kingdom-Animal-Canine
example. Suppose you have a group called Kingdom
,
another group called Kingdom-Animal
, another called Kingdom-Animal-Canine
, and another called Kingdom-Plant
.
Pages
Group headers, group footers and group attributes pages will be searched for from the specific to the general. The first page found will be the one that is used.
For example, if Kingdom-Animal-Canine.GroupHeader
and Kingdom.GroupHeader
exist, then if one is in the
Kingdom-Animal-Canine group
, then Kingdom-Animal-Canine.GroupHeader
will be used, but if one is in the
Kingdom-Plant
group then Kingdom.GroupHeader
will be used.
Likewise for GroupFooter
and GroupAttributes
.
Configuration files
Configuration files are also searched for from the specific to the general, and, again, the first one found will be the one used.
Suppose local/Kingdom.php
and local/Kingdom-Animal-Canine.Dog.php
exist.
When on the Kingdom-Animal.HomePage
page, the Kingdom.php
config will be used.
When on the Kingdom-Animal-Canine.Dog
page, the Kingdom-Animal-Canine.Dog.php
config file will be used -- and the Kingdom.php
config file will NOT be used.
If you want to "inherit" the settings from Kingdom.php
, then you would add
include_once("$LocalDir
/Kingdom.php");
to the Kingdom-Animal-Canine.Dog.php
file.
CSS style sheets
CSS style sheets follow the same pattern, but all stylesheets are included, since Cascading Style Sheets have inheritance built-in.
Suppose pub/css/local.css
, pub/css/Kingdom.css
and pub/css/Kingdom-Animal-Canine.css
exist.
On the Kingdom-Animal.HomePage
page, the CSS sheets will be local.css
and Kingdom.css
.
On the Kingdom-Animal-Canine.HomePage
page, the CSS sheets will be local.css
, Kingdom.css
and Kingdom-Animal-Canine.css
.
Name-based clustering
If $ClusterEnableNameClustering is true, then clustering is done on page names as well as group names. This uses the same separator ('-' by default) as for group-based clustering.
For example, if you have these pages:
Kingdom-Animal-Canine.Dog
Kingdom-Animal-Canine.Dog-Terrier
Kingdom-Animal-Canine.Dog-Terrier-WestHighland
When name-based clustering is enabled, you could, for example, have a special "GroupHeader
" for Terriers
; it would be called
Kingdom-Animal-Canine.Dog-Terrier-GroupHeader
and would affect the pages
Kingdom-Animal-Canine.Dog-Terrier
and
Kingdom-Animal-Canine.Dog-Terrier-WestHighland
Link Shortcuts
Three kinds of link-shortcuts are provided, when making [[links]]
:
- absolute '*'
- relative ancestor '^'
- relative descendant '-'
Examples | Current page is Kingdom-Animal-Canine . |
Kingdom (grandparent): | [[^^.]] or [[*.]] |
+-Plant (uncle): | [[^^-Plant.]] or [[*-Plant.]] |
| +-Grass (cousin): | [[^^-Plant.Grass]] |
+-Animal (parent): | [[^.]] |
+-Canine (self): | [[-]] |
| +-Terrier (child-page): | [[Terrier]] |
+-Feline (sibling): | [[^-Feline.]] |
+-Cat (nephew): | [[^-Feline.Cat]] |
Absolute link (to Canine): | [[***.]] |
Examples | Current page is Kingdom-Animal . |
+-Feline (child-group): | [[-Feline.]] |
+-Cat (grandchild): | [[-Feline.Cat]] |
Page Variables
- $BreadCrumb
- A "breadcrumb-trail" which links to all the intermediate groups related to this group-cluster. See Using the BreadCrumb below.
- $BreadCrumbTitle
- A "breadcrumb-trail" which links to all the intermediate groups related to this group-cluster; this uses page titles instead of names in the links. See Using the BreadCrumb below.
- $BreadCrumbNoTitle
- A "breadcrumb-trail" which links to all the intermediate groups related to this group-cluster; this uses names in the links. See Using the BreadCrumb below.
- $BreadCrumbDepth
- The number of levels in the current group (the same as $g0).
- $ClusterSep
- The cluster separator.
- $ClusterBreadCrumbSep
- The BreadCrumb separator.
- $ClusterMaxLevels
- The same as the $ClusterMaxLevels configuration setting.
- $ClusterSideBar
- The name of the sidebar page that should be associated with this page. See Using the SideBar below.
- $ClusterRightBar
- The name of the right-bar page that should be associated with this page. See Using the SideBar below.
- $g0
- The number of levels in the given group
- $g01
- The number of levels in the given group, plus one.
- $g1
- The first part of the group name
- $g7
- The 7th part of the group name (if it exists)
- $n0
- The number of levels in the given page name ($PageName, not $FullName)
- $n1
- The first part of the page name
- $n7
- The 7th part of the page name (if it exists)
- $GroupTitle
- The title of a group (as in GroupTitle) but also if no $Title has been given for a group, it takes the last part of the group name as the group title.
- $GroupTitlespaced
- as above, but spaced
Directives
To enable more fine-grained control of both links and titles, the (:clusterslice:)
directive is provided.
Cluster-slice provides a "slice" of the "clustered" page-name, with options to return links, names or titles of the segments of the cluster.
(:clusterslice:) |
(:clusterslice:) |
Name | Description | Default |
start | Where to start. If start is non-negative, the returned slice will start at the start'th position in the cluster, counting from zero. If start is negative, the returned slice will start at the start'th segment from the end of the cluster. | 0 |
length | Length of the slice. If length is given and is positive, the slice returned will contain at most length segments beginning from start (depending on the length of the cluster). If the cluster is less than or equal to start segments long, an empty string will be returned. If length is given and is negative, then that many segments will be omitted from the end of the slice (after the start position has been calculated). If start denotes a position beyond this truncation, an empty string will be returned. | 0 |
separator | Separator to use between breadcrumb elements | $ClusterBreadCrumbSeparator |
title | Use titles as labels (if we are returning links) | $ClusterEnableBreadCrumbTitles |
space | Use spaced names as labels | $ClusterEnableBreadCrumbSpaced |
name | Include the name part of the page as part of the slice. There are three possible values for this: 'false', 'simple' and 'clustered'. The 'clustered' option treats the name as clustered; the 'simple' option treats the name as one simple segment, and 'false' doesn't include the name at all. | $ClusterEnableNameClustering -> 'clustered', $ClusterEnableBreadCrumbName -> 'simple', otherwise false |
noindex | If the name is included, if noindex is true, then the name will not be included if the page is an index-page, that is, the HomePage of a group. | $ClusterEnableBreadCrumbName |
return | What to return. There are three possible values for this: 'links', 'groups' or 'names'. The 'links' option returns links, the 'groups' option returns the group names, and the 'names' option returns what the groups are called (the labels). | links |
pagename | To make this even more flexible, you can pass in the pagename to use, rather than using the current page name. | current page |
Using the BreadCrumb
The $BreadCrumb page-variable creates a breadcrumb-trail.
To use it, put in your Group.GroupHeader
page:
{*$BreadCrumb}
This will display the breadcrumb trail at the top of pages in that group.
So what is a breadcrumb-trail? Here's an example.
If you are on the Kingdom-Animal-Feline.Cat
page, the breadcrumb trail that is put into the $BreadCrumb variable would be:
[[Kingdom/|Kingdom]] - [[Kingdom-Animal/|Animal]] - [[Kingdom-Animal-Feline/|Feline ]] - '''Cat''' |
The ' - ' used as a separator can be changed by setting $ClusterBreadCrumbSeparator to the desired separator. For example:
$ClusterBreadCrumbSeparator = ' > ';
This would give
[[Kingdom/|Kingdom]] > [[Kingdom-Animal/|Animal]] > [[Kingdom-Animal-Feline/|Feline ]] > '''Cat''' |
If $ClusterEnableBreadCrumbName is false, then the name of the page is not included in the breadcrumb.
[[Kingdom/|Kingdom]] - [[Kingdom-Animal/|Animal]] - [[Kingdom-Animal-Feline/|Feline ]] |
The $BreadCrumbTitle page-variable creates a breadcrumb-trail where instead of using the name of the group, it uses the title in the link label.
To use it, put in your Group.GroupHeader
page:
{*$BreadCrumbTitle}
The $BreadCrumbNoTitle page-variable creates a breadcrumb-trail where it always uses the name in the link label.
To use it, put in your Group.GroupHeader page:
{*$BreadCrumbNoTitle}
Using the SideBar
In order to take advantage of the SideBar
and RightBar
clustering, you need to edit your skin template.
A normal skin template would have something like this:
<!--wiki:{$Group}.SideBar {$SiteGroup}.SideBar-->
What Cluster does is give you an additional page-variable, so you can go
<!--wiki:{$ClusterSideBar} {$Group}.SideBar {$SiteGroup}.SideBar-->
instead.
The $ClusterSideBar page-variable will be set to the appropriate side-bar for
the current group, doing the search in the same kind of way that
GroupHeader
or GroupFooter
etc are searched for.
That is, you create a Side Bar for the Top Level Group of the cluster, it will be applied to all "Child" groups through the Custer.
So, for example, if you create Kingdom.SideBar
, it will be used not only for all pages in the "Kingdom
" group, but all pages in the "Kingdom-Animal
" and
"Kingdom-Plant
" groups.
Likewise the $ClusterRightBar page-variable should be used in skins that have a RightBar; for example:
<!--wiki:{$ClusterRightBar} {$Group}.RightBar {$SiteGroup}.RightBar-->
Functions
For skin writers, there is a function ClusterPageName
which can be used to make general page variables to find suitable "clustered" pages in the same way that $ClusterSideBar does.
The page-variable declaration for $ClusterSideBar is:
$FmtPV['$ClusterSideBar'] = 'ClusterPageName($group, "SideBar", $name)';
- group
- The group (group-cluster) to look for matching pages.
- sought_name
- The name of the page to look for.
- curr_name
- (optional) The name (name-cluster) to look for matching pages. This is only used if $ClusterEnableNameClustering is true.
So, if, for example, you want to make a $ClusterMenuBar variable, which looks for MenuBar pages, you would use the following:
$FmtPV['$ClusterMenuBar'] = 'ClusterPageName($group, "MenuBar", $name)';
To make sure that this still works even if Cluster isn't installed, you would do something like this:
if (function_exists('ClusterPageName')) { $FmtPV['$ClusterMenuBar'] = 'ClusterPageName($group, "MenuBar", $name)'; }
Limitations
This does not give Kingdom/Animal/Canine
style links; it is only limited to clustering configuration and pages. It does not
provide a full "hierarchical groups" facility.
Tips
Page Lists
The $gN page variables are very useful in making hierarchical menus to use in your sidebar. The following is an example of one. (Note that this pagelist template is usable in PmWiki version 2.2beta36 or greater.)
fmt=#hgtitle2
Two-level list of groups (showing title), giving nested hierarchical groups of top two levels.
[[#hgtitle2]] (:template defaults order=group list=group:) (:template first {=$g1}:) (:if equal "{=$g0}" "1" ):) *[[{=$Group}.|{=$GroupTitle}]] (:ifend:) (:template first {=$g2}:) (:if ( equal "{=$g1}" "{*$g1}" and equal "{=$g0}" "2" ) :) **[[{=$Group}.|{=$GroupTitle}]] (:ifend:) [[#hgtitle2end]]
Note that the "group" list used by this is a custom $SearchPatterns
pattern.
#make a group-only Group.Group/Group.HomePage search pattern $SearchPatterns['group'][] = '/([-\w]+)\.\1$|\.' . "$DefaultName" . '$/';
fmt=#currentclusterdesc
This uses the $g01 variable. Devised by Kathryn Andersen and Feral.
[[#currentclusterdesc]] (:template defaults order=group list=group:) (:template first {=$Group}:) (:if equal "{=$g0}" "{*$g01}" :) *[[{=$Group}|{=$GroupTitlespaced}]] (:if ( equal "{=$g0}" "{*$g01}" and !equal "{{=$Group}$Description}" "" ) :) ->'-%smaller italic small-caps%Description:%% {{=$Group}$Description}-' (:ifend:) [[#currentclusterdesc]]
fmt=#clustertrail1
Dynamic group trail of form <<prev.Title | $n1 | next.Title>>; useful for name-based clustering.
[[#clustertrail1]] (:if equal {*$FullName} {=$FullName}:)<<[[{<$FullName}|{<$Title}]] | [[{=$Group}.{=$n1}|{=$n1}]] | [[{>$FullName}|{>$Title}]]>>(:ifend:) [[#clustertrail1end]]
Notes
This is very similar to Cookbook.Hg because we have used each others' code. See Hierarchical Groups for differences.
Release Notes
- 2017-06-21: changed Markup_e() to Markup() for compatibilty with PHP 7.2 (HansB)
- (2014-05-15) use the Markup_e() function which is compatible with PHP 5.5 (Petko)
- (2007-06-26) bug fixes with ClusterSlice function.
- (2007-06-17) bug fixes with ClusterPageName function.
- (2007-05-27) Large rewrite:
- Now optionally clusters on names as well as groups.
- changed internals partly based on Feral's ClusterSlice, but the arguments are based on "substr".
- The (:breadcrumb:) and (:clustergrouptitle:) have both been replaced by the (:clusterslice:) directive.
- (2007-04-27) Bug fix for link shortcut markup.
- (2007-03-25) Merged in a number of Feral's and HansB's improvements:
- Configuration Variables:
- added $ClusterEnableBreadCrumbName, $ClusterEnableBreadCrumbTitles, $ClusterEnableBreadCrumbSpaced, $GroupTitlePathFmt (was there before, but now documented)
- Page Variables:
- renamed $SideBar and $RightBar to $ClusterSideBar and $ClusterRightBar
- added $BreadCrumbTitle, $BreadCrumbNoTitle, $BreadCrumbDepth
- Directives:
- added
(:breadcrumb:)
and(:clustergrouptitle:)
directives
- added
- Functions:
- Generic ClusterPageName function which can be used to create PageVariables for skins.
- Configuration Variables:
- (2007-03-16) Merged in Feral's fixes. Adapted Feral's improvements:
- (:clusterbreadcrumbs:) became the $BreadCrumbs page variable
- added $ClusterBreadCrumbSeparator to make the breadcrumb more flexible
- $ClusterRightBar became the $RightBar page variable; I didn't see a need for $clustersrightbarname (see Using the SideBar above)
- (:clusterbreadcrumbs:) became the $BreadCrumbs page variable
- (2007-02-12) Added $GroupTitle and $GroupTitlespaced.
- (2007-02-11) initial version
See Also
Contributors
- Feral
- HansB
- Petko Markup_e() function PITS:01319.
Comments
See discussion at Cluster-Talk
User notes? : If you use, used or reviewed this recipe, you can add your name. These statistics appear in the Cookbook listings and will help newcomers browsing through the wiki.