Cluster

Summary: Group-clustering recipe.
Version: 2017-06-21
Prerequisites: PmWiki 2.2.56
Status: beta
Maintainer: Kathryn Andersen
Download: cluster.phpΔ
Discussion: Cluster-Talk

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

NameDescriptionDefault
$ClusterSeparatorDefine the character used to separate the different parts of a group name'-'
$ClusterMaxLevelsDefine the maximum number of levels to split group names by7
$ClusterBreadCrumbSeparatorDefine the string used to separate the different parts of a breadcrumb-trail in the $BreadCrumb page-variable (See Using the BreadCrumb below)' - '
$ClusterEnableBreadCrumbNameIf 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
$ClusterEnableTitlesIf 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
$ClusterEnableSpacesIf 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
$ClusterEnableNameClusteringIf true, this will cluster on page-names as well as group-names; if false, only groups are clustered.false
$GroupTitlePathFmtThe 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 '-'
ExamplesCurrent 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):[[***.]]
ExamplesCurrent page is Kingdom-Animal.
  +-Feline (child-group):[[-Feline.]]
    +-Cat (grandchild):[[-Feline.Cat]]

Page Variables

A "breadcrumb-trail" which links to all the intermediate groups related to this group-cluster. See Using the BreadCrumb below.
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.
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.
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:)

Options:
NameDescriptionDefault
startWhere 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
lengthLength 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
separatorSeparator to use between breadcrumb elements$ClusterBreadCrumbSeparator
titleUse titles as labels (if we are returning links)$ClusterEnableBreadCrumbTitles
spaceUse spaced names as labels$ClusterEnableBreadCrumbSpaced
nameInclude 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
noindexIf 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
returnWhat 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
pagenameTo make this even more flexible, you can pass in the pagename to use, rather than using the current page name.current page
Note: The start and length options are based on that of the substr PHP function.

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'''

Kingdom? - Animal? - 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'''

Kingdom? > Animal? > 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 ]]

Kingdom? - Animal? - 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)';
Arguments:
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
    • Functions:
      • Generic ClusterPageName function which can be used to create PageVariables for skins.
  • (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)
  • (2007-02-12) Added $GroupTitle and $GroupTitlespaced.
  • (2007-02-11) initial version

See Also

Contributors

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.