Add top-level keywords, which allows for hierarchial categorization of wiki pages.
This commit is contained in:
parent
1a77fce843
commit
55d2e510d6
8 changed files with 489 additions and 1 deletions
|
|
@ -5,6 +5,8 @@
|
|||
- fixed #11520: Wiki Locked
|
||||
- fixed Missing Template variables for the Wiki Page view template.
|
||||
- added #10944: Wiki Keyword Page
|
||||
- added #10946: Wiki - Hierarchical Keyword Report
|
||||
- added #10945: Wiki - Top-level keyword list
|
||||
|
||||
7.9.2
|
||||
- added: Workflow to extend recurring Calendar events 2 years from the
|
||||
|
|
|
|||
BIN
docs/upgrades/packages-7.9.3/default-wiki-front-page.wgpkg
Normal file
BIN
docs/upgrades/packages-7.9.3/default-wiki-front-page.wgpkg
Normal file
Binary file not shown.
|
|
@ -31,6 +31,7 @@ my $quiet; # this line required
|
|||
my $session = start(); # this line required
|
||||
|
||||
reindexSiteForDefaultSynopsis( $session );
|
||||
addTopLevelWikiKeywords( $session );
|
||||
|
||||
finish($session); # this line required
|
||||
|
||||
|
|
@ -44,6 +45,24 @@ finish($session); # this line required
|
|||
# print "DONE!\n" unless $quiet;
|
||||
#}
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
sub addTopLevelWikiKeywords {
|
||||
my $session = shift;
|
||||
print "\tAdding top level keywords page to WikiMaster... " unless $quiet;
|
||||
|
||||
my $sth = $session->db->read('DESCRIBE `WikiMaster`');
|
||||
while (my ($col) = $sth->array) {
|
||||
if ($col eq 'topLevelKeywords') {
|
||||
print "Skipped.\n" unless $quiet;
|
||||
return;
|
||||
}
|
||||
}
|
||||
$session->db->write('ALTER TABLE WikiMaster ADD COLUMN topLevelKeywords LONGTEXT');
|
||||
|
||||
print "Done.\n" unless $quiet;
|
||||
}
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Reindex the site to clear out default synopsis
|
||||
sub reindexSiteForDefaultSynopsis {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ use WebGUI::International;
|
|||
use WebGUI::Utility;
|
||||
use HTML::Parser;
|
||||
use URI::Escape;
|
||||
use WebGUI::Form;
|
||||
use Clone qw/clone/;
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
|
|
@ -43,12 +45,29 @@ sub appendFeaturedPageVars {
|
|||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 appendKeywordPageVars ( var )
|
||||
|
||||
Append the template variables to C<var> for keyword (catagory) pages.
|
||||
|
||||
=cut
|
||||
|
||||
sub appendKeywordPageVars {
|
||||
my ( $self, $var ) = @_;
|
||||
my $session = $self->session;
|
||||
my $topKeywords = $self->getTopLevelKeywordsList;
|
||||
my $keywordHierarchy = $self->getKeywordHierarchy( $topKeywords, );
|
||||
$var->{keywords_loop} = $self->getKeywordVariables( $keywordHierarchy );
|
||||
return $var;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 appendMostPopular ($var, [ $limit ])
|
||||
|
||||
=head3 $var
|
||||
|
||||
A hash reference of template variables. An array reference containing the most popular wiki pages
|
||||
in order of popularity.
|
||||
in order of popularity will be appended to it.
|
||||
|
||||
=head3 $limit
|
||||
|
||||
|
|
@ -425,6 +444,13 @@ sub definition {
|
|||
label=>$i18n->get('filter code'),
|
||||
hoverHelp=>$i18n->get('filter code description'),
|
||||
},
|
||||
topLevelKeywords =>{
|
||||
fieldType => "keywords",
|
||||
defaultValue => '',
|
||||
tab => 'properties',
|
||||
label => $i18n->get('top level keywords'),
|
||||
hoverHelp => $i18n->get('top level keywords description'),
|
||||
},
|
||||
);
|
||||
|
||||
push @$definition,
|
||||
|
|
@ -461,6 +487,108 @@ sub getFeaturedPageIds {
|
|||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 getKeywordHierarchy ( $keywords, $seen )
|
||||
|
||||
Starting with the top level keywords, return the hierarchy of keywords as a recursive arrayref of hashrefs.
|
||||
The traversal is left-most, depth first.
|
||||
|
||||
The hierarchy data structure that looks like this:
|
||||
|
||||
[
|
||||
{
|
||||
title => 'title', # same as the keyword, since this is a keyword (category) page
|
||||
url => 'url', # url from the keyword page, via getUrl so it contains the gateway URL
|
||||
# If a keyword page does not exist for the keyword, this key/value pair will not be present.
|
||||
children => [ # Array reference of sub-categories referenced by this category
|
||||
{ # If there are no children, this key/value pair will not be present
|
||||
...
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
=head3 $keywords
|
||||
|
||||
An array reference of keywords. If this is blank, then it will use the top level keywords from
|
||||
itself as a default.
|
||||
|
||||
=head3 $seen
|
||||
|
||||
A hash reference that keeps track of which keywords have already been seen. This prevents
|
||||
infinite loops from happening during the traversal.
|
||||
|
||||
=cut
|
||||
|
||||
sub getKeywordHierarchy {
|
||||
my ( $self, $keywords, $seen ) = @_;
|
||||
my $session = $self->session;
|
||||
my $hierarchy = [];
|
||||
$keywords ||= $self->getTopLevelKeywordsList;
|
||||
$seen ||= {};
|
||||
KEYWORD: foreach my $keyword (sort @{ $keywords }) {
|
||||
my $page = $self->getLineage(['children'], {
|
||||
returnObjects => 1,
|
||||
whereClause => 'assetData.title = '.$session->db->quote($keyword),
|
||||
limit => 1,
|
||||
includeOnlyClasses => [qw/WebGUI::Asset::WikiPage/],
|
||||
})->[0];
|
||||
if (! $page) {
|
||||
push @{ $hierarchy }, { title => $keyword, url => '', };
|
||||
next KEYWORD;
|
||||
}
|
||||
my $datum = {
|
||||
title => $keyword, ##Note, same as keyword
|
||||
url => $page->getUrl,
|
||||
};
|
||||
##Prevent recursion if seen again
|
||||
if (! $seen->{$keyword}++) {
|
||||
my $children = $self->getKeywordHierarchy(WebGUI::Keyword::string2list($page->get('keywords')), $seen, );
|
||||
if (@{ $children } ) {
|
||||
$datum->{children} = $children;
|
||||
}
|
||||
}
|
||||
push @{ $hierarchy }, $datum;
|
||||
}
|
||||
return $hierarchy;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 getKeywordVariables ( $hierarchy, $level )
|
||||
|
||||
Take a data structure representing a hierarchy of keywords, and append template variables
|
||||
to them similar to a Navigation so you can build useful things with them.
|
||||
|
||||
=head3 $hierarchy
|
||||
|
||||
A data structure similar to that produced by getKeywordHierarchy
|
||||
|
||||
=head3 $level
|
||||
|
||||
The current level in any part of the hierarchy.
|
||||
|
||||
=cut
|
||||
|
||||
sub getKeywordVariables {
|
||||
my ( $self, $hierarchy, $level ) = @_;
|
||||
$level ||= 0;
|
||||
my $variables = [];
|
||||
|
||||
KEYWORD: foreach my $member (@{ $hierarchy }) {
|
||||
my $varBlock = clone $member;
|
||||
$varBlock->{level} = $level;
|
||||
$varBlock->{indent_loop} = [ map { { indent => $_ } } 1..$level ];
|
||||
delete $varBlock->{children};
|
||||
push @{$variables}, $varBlock;
|
||||
if ( exists $member->{children} ) {
|
||||
push @{$variables}, @{ $self->getKeywordVariables($member->{children}, $level+1) };
|
||||
}
|
||||
}
|
||||
return $variables;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 getRssFeedItems ()
|
||||
|
||||
Returns an array reference of hash references. Each hash reference has a title,
|
||||
|
|
@ -520,6 +648,19 @@ sub getTemplateVars {
|
|||
return $var;
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
=head2 getTopLevelKeywordsList ( )
|
||||
|
||||
Return the top level keywords as an array reference.
|
||||
|
||||
=cut
|
||||
|
||||
sub getTopLevelKeywordsList {
|
||||
my ( $self ) = @_;
|
||||
return WebGUI::Keyword::string2list($self->get('topLevelKeywords'));
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 prepareView
|
||||
|
|
@ -610,6 +751,7 @@ sub view {
|
|||
$self->appendSearchBoxVars($var);
|
||||
$self->appendRecentChanges($var, $self->get('recentChangesCountFront'));
|
||||
$self->appendMostPopular($var, $self->get('mostPopularCountFront'));
|
||||
$self->appendKeywordPageVars($var);
|
||||
return $self->processTemplate($var, undef, $template);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -102,6 +102,26 @@ our $HELP = {
|
|||
{ 'name' => 'recentChangesLabel variable', },
|
||||
{ 'name' => 'addPageUrl', },
|
||||
{ 'name' => 'addPageLabel', },
|
||||
{ 'name' => 'keywords_loop',
|
||||
'variables' => [
|
||||
{ 'name' => 'title',
|
||||
'description' => 'keyword title',
|
||||
},
|
||||
{ 'name' => 'url',
|
||||
'description' => 'keyword url',
|
||||
},
|
||||
{ 'name' => 'level',
|
||||
'description' => 'keyword level',
|
||||
},
|
||||
{ 'name' => 'indent_loop',
|
||||
'variables' => [
|
||||
{ 'name' => 'indent',
|
||||
'description' => 'keyword indent',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
fields => [],
|
||||
related => [],
|
||||
|
|
|
|||
|
|
@ -42,6 +42,18 @@ our $I18N = {
|
|||
context => q|Hover help for edit wobject screen|,
|
||||
},
|
||||
|
||||
'top level keywords' => {
|
||||
message => q|Top Level Keywords|,
|
||||
lastUpdated => 0,
|
||||
context => q|Label for edit wobject screen|,
|
||||
},
|
||||
|
||||
'top level keywords description' => {
|
||||
message => q|These keywords provide the root for the hierarchial keyword display.|,
|
||||
lastUpdated => 0,
|
||||
context => q|Hover help for edit wobject screen|,
|
||||
},
|
||||
|
||||
'content filter' => {
|
||||
message => q|Use Content Filter?|,
|
||||
lastUpdated => 0,
|
||||
|
|
@ -523,6 +535,43 @@ listing of pages that are related to a specific keyword?| },
|
|||
lastUpdated => 0,
|
||||
context => q{Label for link to unsubscribe from e-mail notifications},
|
||||
},
|
||||
|
||||
'keywords_loop' => {
|
||||
message => q{A loop containing all the top level keywords, links to their keyword pages, and all sub pages below them.},
|
||||
lastUpdated => 0,
|
||||
context => q{Help for template variable},
|
||||
},
|
||||
|
||||
'keyword title' => {
|
||||
message => q{The name of a keyword.},
|
||||
lastUpdated => 0,
|
||||
context => q{Help for template variable},
|
||||
},
|
||||
|
||||
'keyword url' => {
|
||||
message => q{The URL to the keyword page for that keyword. If no page exists, this variable will be empty.},
|
||||
lastUpdated => 0,
|
||||
context => q{Help for template variable},
|
||||
},
|
||||
|
||||
'keyword level' => {
|
||||
message => q{The depth of this keyword. Top-level keywords for the wiki are level 0.},
|
||||
lastUpdated => 0,
|
||||
context => q{Help for template variable},
|
||||
},
|
||||
|
||||
'indent_loop' => {
|
||||
message => q{A loop that runs 1 time for each level.},
|
||||
lastUpdated => 0,
|
||||
context => q{Help for template variable},
|
||||
},
|
||||
|
||||
'keyword indent' => {
|
||||
message => q{The loop iterator for the indent_loop.},
|
||||
lastUpdated => 0,
|
||||
context => q{Help for template variable},
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
1;
|
||||
|
|
|
|||
|
|
@ -4676,6 +4676,12 @@ Users may override this setting in their profile.
|
|||
context => 'Message shown to the user when data is being loaded, typically via AJAX, like in the Survey.'
|
||||
},
|
||||
|
||||
'Go' => {
|
||||
message => 'Go',
|
||||
lastUpdated => 0,
|
||||
context => 'Label for buttons that take you someplace else'
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
1;
|
||||
|
|
|
|||
250
t/Asset/Wobject/WikiMaster.t
Normal file
250
t/Asset/Wobject/WikiMaster.t
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
# vim:syntax=perl
|
||||
#-------------------------------------------------------------------
|
||||
# WebGUI is Copyright 2001-2009 Plain Black Corporation.
|
||||
#-------------------------------------------------------------------
|
||||
# Please read the legal notices (docs/legal.txt) and the license
|
||||
# (docs/license.txt) that came with this distribution before using
|
||||
# this software.
|
||||
#------------------------------------------------------------------
|
||||
# http://www.plainblack.com info@plainblack.com
|
||||
#------------------------------------------------------------------
|
||||
|
||||
# Test the featured page of the Wiki
|
||||
#
|
||||
#
|
||||
|
||||
use FindBin;
|
||||
use strict;
|
||||
use lib "$FindBin::Bin/../../lib";
|
||||
use Test::More;
|
||||
use Test::Differences;
|
||||
use Test::Deep;
|
||||
use Data::Dumper;
|
||||
use WebGUI::Test; # Must use this before any other WebGUI modules
|
||||
use WebGUI::Session;
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Init
|
||||
my $session = WebGUI::Test->session;
|
||||
my $import = WebGUI::Asset->getImportNode( $session );
|
||||
|
||||
my @childCoda = (undef, undef, { skipAutoCommitWorkflows => 1, skipNotification => 1, } );
|
||||
my @revCoda = (undef, { skipAutoCommitWorkflows => 1, skipNotification => 1, } );
|
||||
|
||||
my $wiki
|
||||
= $import->addChild( {
|
||||
className => 'WebGUI::Asset::Wobject::WikiMaster',
|
||||
topLevelKeywords => 'criminals,inmates,staff',
|
||||
url => 'testwiki',
|
||||
title => 'testwiki',
|
||||
}, @childCoda );
|
||||
|
||||
my $wikitag = WebGUI::VersionTag->getWorking( $session );
|
||||
$wikitag->commit;
|
||||
WebGUI::Test->addToCleanup($wikitag);
|
||||
$wiki = $wiki->cloneFromDb;
|
||||
|
||||
my %page_set = ();
|
||||
|
||||
foreach my $keywords (qw/staff inmates criminals/) {
|
||||
$page_set{$keywords} = $wiki->addChild({
|
||||
className => 'WebGUI::Asset::WikiPage',
|
||||
title => $keywords,
|
||||
}, @childCoda);
|
||||
}
|
||||
|
||||
my $tag_set1 = WebGUI::VersionTag->getWorking($session);
|
||||
$tag_set1->commit;
|
||||
WebGUI::Test->addToCleanup($tag_set1);
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Tests
|
||||
|
||||
plan tests => 11; # Increment this number for each test you create
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
is $wiki->get('topLevelKeywords'), 'criminals,inmates,staff', 'checking wiki setup';
|
||||
cmp_deeply($wiki->getTopLevelKeywordsList, [qw/criminals inmates staff/], 'getTopLevelKeywordList returns keywords');
|
||||
|
||||
cmp_deeply(
|
||||
$wiki->getKeywordHierarchy(),
|
||||
[
|
||||
superhashof({ title => 'criminals', }),
|
||||
superhashof({ title => 'inmates', }),
|
||||
superhashof({ title => 'staff', }),
|
||||
],
|
||||
"getKeywordHierarchy, simple setup",
|
||||
);
|
||||
|
||||
my $hierarchy = $wiki->getKeywordHierarchy();
|
||||
my $variables = $wiki->getKeywordVariables($hierarchy);
|
||||
|
||||
cmp_deeply(
|
||||
$hierarchy->[0],
|
||||
{
|
||||
title => 'criminals',
|
||||
url => '/testwiki/criminals',
|
||||
},
|
||||
"getKeywordVariables, does not alter the original hierarchy passed in",
|
||||
);
|
||||
|
||||
cmp_deeply(
|
||||
$variables,
|
||||
[
|
||||
{
|
||||
title => 'criminals',
|
||||
url => '/testwiki/criminals',
|
||||
level => 0,
|
||||
indent_loop => [],
|
||||
},
|
||||
{
|
||||
title => 'inmates',
|
||||
url => '/testwiki/inmates',
|
||||
level => 0,
|
||||
indent_loop => [],
|
||||
},
|
||||
{
|
||||
title => 'staff',
|
||||
url => '/testwiki/staff',
|
||||
level => 0,
|
||||
indent_loop => [],
|
||||
},
|
||||
],
|
||||
"... variables",
|
||||
);
|
||||
|
||||
$wiki->update({topLevelKeywords => 'criminals,criminals,inmates,staff'});
|
||||
|
||||
is $wiki->get('topLevelKeywords'), 'criminals,criminals,inmates,staff', 'checking wiki setup 2';
|
||||
cmp_deeply($wiki->getTopLevelKeywordsList, [qw/criminals criminals inmates staff/], 'getTopLevelKeywordList returns keywords, even with duplicates');
|
||||
|
||||
cmp_deeply(
|
||||
$wiki->getKeywordHierarchy(),
|
||||
[
|
||||
superhashof({ title => 'criminals', }),
|
||||
superhashof({ title => 'criminals', }),
|
||||
superhashof({ title => 'inmates', }),
|
||||
superhashof({ title => 'staff', }),
|
||||
],
|
||||
"getKeywordHierarchy, simple setup, duplicates listed",
|
||||
);
|
||||
|
||||
$wiki->update({topLevelKeywords => 'criminals,inmates,staff'});
|
||||
$page_set{criminals}->update({keywords => 'red,andy'});
|
||||
$page_set{inmates}->update({keywords => 'brooks,heywood'});
|
||||
$page_set{staff}->update({keywords => 'norton,hadley'});
|
||||
|
||||
foreach my $title (qw/red andy brooks heywood norton hadley/) {
|
||||
$page_set{$title} = $wiki->addChild({
|
||||
className => 'WebGUI::Asset::WikiPage',
|
||||
title => $title,
|
||||
}, @childCoda);
|
||||
}
|
||||
|
||||
my $tag_set2 = WebGUI::VersionTag->getWorking($session);
|
||||
$tag_set2->commit;
|
||||
WebGUI::Test->addToCleanup($tag_set2);
|
||||
|
||||
cmp_bag(
|
||||
$wiki->getKeywordHierarchy(),
|
||||
[
|
||||
{
|
||||
title => 'criminals', url => '/testwiki/criminals',
|
||||
children => bag(
|
||||
superhashof({ title => 'red', }),
|
||||
superhashof({ title => 'andy', }),
|
||||
),
|
||||
},
|
||||
{
|
||||
title => 'inmates', url => '/testwiki/inmates',
|
||||
children => bag(
|
||||
superhashof({ title => 'heywood', }),
|
||||
superhashof({ title => 'brooks', }),
|
||||
),
|
||||
},
|
||||
{
|
||||
title => 'staff', url => '/testwiki/staff',
|
||||
children => bag(
|
||||
superhashof({ title => 'norton', }),
|
||||
superhashof({ title => 'hadley', }),
|
||||
),
|
||||
},
|
||||
],
|
||||
"getKeywordHierarchy: simple hierarchy",
|
||||
);
|
||||
|
||||
##Check depth-first display, and try to make a keyword loop
|
||||
$page_set{andy}->update({keywords => 'criminals,inmates'});
|
||||
$page_set{brooks}->update({keywords => 'criminals'});
|
||||
|
||||
my $tag_set3 = WebGUI::VersionTag->getWorking($session);
|
||||
$tag_set3->commit;
|
||||
WebGUI::Test->addToCleanup($tag_set3);
|
||||
|
||||
cmp_bag(
|
||||
$wiki->getKeywordHierarchy(),
|
||||
[
|
||||
superhashof({
|
||||
title => 'criminals',
|
||||
children => bag(
|
||||
superhashof({
|
||||
title => 'andy',
|
||||
children => bag(
|
||||
superhashof({
|
||||
title => 'inmates',
|
||||
children => bag(
|
||||
superhashof({ title => 'heywood', }),
|
||||
superhashof({
|
||||
title => 'brooks',
|
||||
children => bag(
|
||||
superhashof({ title => 'criminals', }),
|
||||
),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
superhashof({ title => 'criminals', }),
|
||||
),
|
||||
}),
|
||||
superhashof({ title => 'red', }),
|
||||
),
|
||||
}),
|
||||
superhashof({
|
||||
title => 'inmates',
|
||||
}),
|
||||
superhashof({
|
||||
title => 'staff',
|
||||
children => bag(
|
||||
superhashof({ title => 'norton', }),
|
||||
superhashof({ title => 'hadley', }),
|
||||
),
|
||||
}),
|
||||
],
|
||||
"getKeywordHierarchy: complex hierarcy, depth-first display and loop handling",
|
||||
);
|
||||
|
||||
cmp_deeply(
|
||||
$wiki->getKeywordVariables([
|
||||
{
|
||||
title => 'title 0', url => 'url 0',
|
||||
children => [ {
|
||||
title => 'title 1', url => 'url 1',
|
||||
children => [ {
|
||||
title => 'title 2', url => 'url 2',
|
||||
}, ],
|
||||
}, ],
|
||||
},
|
||||
]),
|
||||
[
|
||||
{ title => 'title 0', url => 'url 0', level => 0, indent_loop => [], },
|
||||
{ title => 'title 1', url => 'url 1', level => 1, indent_loop => [{indent => 1}], },
|
||||
{ title => 'title 2', url => 'url 2', level => 2, indent_loop => [{indent => 1,}, {indent => 2,},], },
|
||||
],
|
||||
'getKeywordVariables: checking deeply'
|
||||
);
|
||||
|
||||
$page_set{criminals}->update({keywords => 'red,andy,tommy'});
|
||||
|
||||
#vim:ft=perl
|
||||
Loading…
Add table
Add a link
Reference in a new issue