diff --git a/docs/upgrades/templates-7.1.1/default-wiki-master.tmpl b/docs/upgrades/templates-7.1.1/default-wiki-master.tmpl new file mode 100644 index 000000000..fb7bcccd9 --- /dev/null +++ b/docs/upgrades/templates-7.1.1/default-wiki-master.tmpl @@ -0,0 +1,16 @@ +#WikiMasterTmpl00000001 +#create +#namespace:WikiMaster +#url:default-wiki-master +#title:Default Wiki Master +#menuTitle:Default Wiki Master + +

+

+ +

Other actions

+ diff --git a/docs/upgrades/templates-7.1.1/default-wiki-page-edit.tmpl b/docs/upgrades/templates-7.1.1/default-wiki-page-edit.tmpl new file mode 100644 index 000000000..77c18fbca --- /dev/null +++ b/docs/upgrades/templates-7.1.1/default-wiki-page-edit.tmpl @@ -0,0 +1,12 @@ +#WikiPageEditTmpl000001 +#create +#namespace:WikiPage_edit +#url:default-wiki-page-edit +#title:Default Wiki Page Edit +#menuTitle:Default Wiki Page Edit + + +

Title:
+Content:

+ + diff --git a/docs/upgrades/templates-7.1.1/default-wiki-page-history.tmpl b/docs/upgrades/templates-7.1.1/default-wiki-page-history.tmpl new file mode 100644 index 000000000..985be58a5 --- /dev/null +++ b/docs/upgrades/templates-7.1.1/default-wiki-page-history.tmpl @@ -0,0 +1,15 @@ +#WikiPHTmpl000000000001 +#create +#namespace:WikiPage_pageHistory +#url:default-wiki-page-history +#title:Default Page History +#menuTitle:Default Page History + + diff --git a/docs/upgrades/templates-7.1.1/default-wiki-page-list.tmpl b/docs/upgrades/templates-7.1.1/default-wiki-page-list.tmpl new file mode 100644 index 000000000..c512d2932 --- /dev/null +++ b/docs/upgrades/templates-7.1.1/default-wiki-page-list.tmpl @@ -0,0 +1,13 @@ +#WikiPLTmpl000000000001 +#create +#namespace:WikiMaster_pageList +#url:default-wiki-page-list +#title:Default Page List +#menuTitle:Default Page List + +

+
    + +
  • +
    +
diff --git a/docs/upgrades/templates-7.1.1/default-wiki-page.tmpl b/docs/upgrades/templates-7.1.1/default-wiki-page.tmpl new file mode 100644 index 000000000..06d2b2d35 --- /dev/null +++ b/docs/upgrades/templates-7.1.1/default-wiki-page.tmpl @@ -0,0 +1,25 @@ +#WikiPageTmpl0000000001 +#create +#namespace:WikiPage +#url:default-wiki-page +#title:Default Wiki Page +#menuTitle:Default Wiki Page + + +

This page

    +
  • + +
  • +
  • (Protected page)
  • +
    + +
  • +
    + + +
  • + +
  • +
    +
    +
diff --git a/docs/upgrades/templates-7.1.1/default-wiki-recent-changes.tmpl b/docs/upgrades/templates-7.1.1/default-wiki-recent-changes.tmpl new file mode 100644 index 000000000..d4b9f1098 --- /dev/null +++ b/docs/upgrades/templates-7.1.1/default-wiki-recent-changes.tmpl @@ -0,0 +1,11 @@ +#WikiRCTmpl000000000001 +#create +#namespace:WikiMaster_recentChanges +#url:default-wiki-recent-changes +#title:Default Recent Changes +#menuTitle:Default Recent Changes + +
    +
  • : +editeddeleted by
  • +
diff --git a/docs/upgrades/upgrade_7.1.0-7.1.1.pl b/docs/upgrades/upgrade_7.1.0-7.1.1.pl index ee1f33b0d..dcfcbd1bc 100644 --- a/docs/upgrades/upgrade_7.1.0-7.1.1.pl +++ b/docs/upgrades/upgrade_7.1.0-7.1.1.pl @@ -21,6 +21,7 @@ my $quiet; # this line required my $session = start(); # this line required fixSurvey(); +addWikiAssets($session); finish($session); # this line required @@ -31,7 +32,73 @@ sub fixSurvey { $session->db->write("alter table Survey_questionResponse change response response text"); } +sub addWikiAssets { + my $session = shift; + print "\tAdding wiki assets.\n" unless $quiet; + $session->db->write($_) for(<<'EOT', + CREATE TABLE `WikiMaster` ( + `assetId` varchar(22) character set utf8 collate utf8_bin NOT NULL, + `revisionDate` bigint(20) NOT NULL, + `groupToEditPages` varchar(22) character set utf8 collate utf8_bin NOT NULL, + `groupToAdminister` varchar(22) character set utf8 collate utf8_bin NOT NULL, + `richEditor` varchar(22) character set utf8 collate utf8_bin NOT NULL + default 'PBrichedit000000000002', + `defaultPage` varchar(22) character set utf8 collate utf8_bin NULL, + `masterTemplateId` varchar(22) character set utf8 collate utf8_bin NOT NULL + default 'WikiMasterTmpl00000001', + `pageTemplateId` varchar(22) character set utf8 collate utf8_bin NOT NULL + default 'WikiPageTmpl0000000001', + `pageEditTemplateId` varchar(22) character set utf8 collate utf8_bin NOT NULL + default 'WikiPageEditTmpl000001', + `recentChangesTemplateId` varchar(22) character set utf8 collate utf8_bin NOT NULL + default 'WikiRCTmpl000000000001', + `pageHistoryTemplateId` varchar(22) character set utf8 collate utf8_bin NOT NULL + default 'WikiPHTmpl000000000001', + `pageListTemplateId` varchar(22) character set utf8 collate utf8_bin NOT NULL + default 'WikiPLTmpl000000000001', + PRIMARY KEY (`assetId`, `revisionDate`) + ) ENGINE=MyISAM DEFAULT CHARSET=utf8; +EOT + <<'EOT', + CREATE TABLE `WikiPage` ( + `assetId` varchar(22) character set utf8 collate utf8_bin NOT NULL, + `revisionDate` bigint(20) NOT NULL, + `content` mediumtext, + `storageId` varchar(22) character set utf8 collate utf8_bin NULL, + `views` bigint(20) NOT NULL default 0, + PRIMARY KEY (`assetId`, `revisionDate`) + ) ENGINE=MyISAM DEFAULT CHARSET=utf8; +EOT + <<'EOT', + CREATE TABLE `WikiMaster_titleIndex` ( + `assetId` varchar(22) character set utf8 collate utf8_bin NOT NULL, + `pageId` varchar(22) character set utf8 collate utf8_bin NOT NULL, + `title` varchar(255) NOT NULL, + PRIMARY KEY (`assetId`, `pageId`) + ); +EOT + # Don't want protection to be versioned, so put it in a + # separate table. + <<'EOT', + CREATE TABLE `WikiPage_protected` ( + `assetId` varchar(22) character set utf8 collate utf8_bin NOT NULL, + PRIMARY KEY (`assetId`) + ); +EOT + <<'EOT', + CREATE TABLE `WikiPage_extraHistory` ( + `assetId` varchar(22) character set utf8 collate utf8_bin NOT NULL, + `userId` varchar(22) character set utf8 collate utf8_bin NOT NULL, + `dateStamp` bigint(20) NOT NULL, + `actionTaken` varchar(255) NOT NULL default '' + ); +EOT + ); + + my $config = $session->config; + $config->addToArray('assets', 'WebGUI::Asset::Wobject::WikiMaster'); +} # ---- DO NOT EDIT BELOW THIS LINE ---- diff --git a/lib/WebGUI/Asset/WikiPage.pm b/lib/WebGUI/Asset/WikiPage.pm new file mode 100644 index 000000000..03c09629b --- /dev/null +++ b/lib/WebGUI/Asset/WikiPage.pm @@ -0,0 +1,350 @@ +package WebGUI::Asset::WikiPage; + +# ------------------------------------------------------------------- +# WebGUI is Copyright 2001-2006 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 +# ------------------------------------------------------------------- + +use strict; +use Tie::IxHash; +use WebGUI::International; +use WebGUI::Utility; +use base 'WebGUI::Asset'; + +#------------------------------------------------------------------- +sub definition { + my $class = shift; + my $session = shift; + my $definition = shift; + my $i18n = WebGUI::International->new($session, "Asset_WikiPage"); + + my %properties; + tie %properties, 'Tie::IxHash'; + %properties = + ( + storageId => { fieldType => 'image', + defaultValue => undef }, + content => { fieldType => "HTMLArea", + defaultValue => undef }, + ); + + push @$definition, + { + assetName => $i18n->get('assetName'), + icon => 'wikiPage.gif', + autoGenerateForms => 1, + tableName => 'WikiPage', + className => 'WebGUI::Asset::WikiPage', + properties => \%properties, + }; + + return $class->SUPER::definition($session, $definition); +} + + +#------------------------------------------------------------------- +sub getWiki { + my $self = shift; + my $parent = $self->getParent; + return undef unless defined $parent and $parent->isa('WebGUI::Asset::Wobject::WikiMaster'); + return $parent; +} + +#------------------------------------------------------------------- +# BUGGO: how to handle this? +sub duplicate { + my $self = shift; + my $newAsset = $self->SUPER::duplicate(@_); + return $newAsset; +} + + +#------------------------------------------------------------------- +# TODO +sub indexContent { + my $self = shift; + my $indexer = $self->SUPER::indexContent; +} + +#------------------------------------------------------------------- +sub purge { + my $self = shift; + $self->getWiki->updateTitleIndex([$self], from => 'purge'); + $self->session->db->write("DELETE FROM WikiPage_protected WHERE assetId = ?", [$self->getId]); + $self->session->db->write("DELETE FROM WikiPage_extraHistory WHERE assetId = ?", [$self->getId]); + return $self->SUPER::purge; +} + +#------------------------------------------------------------------- +sub purgeRevision { + my $self = shift; + $self->getWiki->updateTitleIndex([$self], from => 'purgeRevision'); + return $self->SUPER::purgeRevision; +} + +#------------------------------------------------------------------- +sub preparePageTemplate { + my $self = shift; + return $self->{_pageTemplate} if $self->{_pageTemplate}; + $self->{_pageTemplate} = + WebGUI::Asset::Template->new($self->session, $self->getWiki->get('pageTemplateId')); + $self->{_pageTemplate}->prepare; + return $self->{_pageTemplate}; +} + +sub processPageTemplate { + my $self = shift; + my $content = shift; + my $func = shift || $self->session->form->process('func'); + my $var = {}; + my $template = $self->preparePageTemplate; + + $var->{content} = $content; + $var->{nonexistentPage} = $self->{_nonexistent}; + $var->{canEdit} = $self->canEdit; + $var->{couldEdit} = $self->couldEdit; + $var->{canProtect} = $self->canProtect; + $var->{isProtected} = $self->isProtected; + $var->{inEdit} = isIn($func, qw/edit add/); + $var->{inView} = isIn($func, qw/view/) || !defined $func; + $var->{inHistory} = isIn($func, qw/pageHistory/); + $self->_addFuncTemplateVars($var); + + return $self->processTemplate($var, undef, $template); +} + +sub prepareView { + my $self = shift; + $self->SUPER::prepareView; + $self->preparePageTemplate; +} + +# Buggo, semi-duplication with WikiMaster; move this into a common utility routine somewhere +sub _addFuncTemplateVars { + my $self = shift; + my $var = shift; + my @funcs = @_; + my $i18n = WebGUI::International->new($self->session, 'Asset_WikiPage'); + my %specialFuncs = (); + my $revision = $self->session->form->process('revision'); + my $revisionSuffix = defined($revision)? ";revision=$revision" : ''; + @funcs = (qw/view edit pageHistory protect unprotect/) unless @funcs; + + foreach my $func (@funcs) { + $var->{$func.'.url'} = $self->getUrl($specialFuncs{$func} + || "func=$func$revisionSuffix"); + $var->{$func.'.text'} = $i18n->get("func $func link text"); + } +} + +sub view { + my $self = shift; + my $var = {}; + my $title = $self->get('title'); + my $content = $self->getWiki->autolinkHtml($self->get('content')); + return $self->getWiki->processMasterTemplate($title, $self->processPageTemplate($content, 'view')); +} + +sub www_view { + my $self = shift; + return $self->session->privilege->noAccess unless $self->canView; + $self->update({ views => $self->get('views')+1 }); + # TODO: This should probably exist, as the CS has one. +# $self->session->http->setCacheControl($self->getWiki->get('visitorCacheTimeout')) +# if ($self->session->user->userId eq '1'); + $self->session->http->sendHeader; + $self->prepareView; + return $self->getWiki->processStyle($self->view); +} + +#------------------------------------------------------------------- +sub isProtected { + my $self = shift; + return $self->{_isProtected} if exists $self->{_isProtected}; + ($self->{_isProtected}) = $self->session->db->quickArray("SELECT COUNT(assetId) FROM WikiPage_protected WHERE assetId = ?", [$self->getId]); + return $self->{_isProtected}; +} + +sub couldEdit { + my $self = shift; + my $userId = shift || $self->session->user->userId; + return 0 if $self->{_nonexistent}; + return 0 unless $self->getWiki->canEditPages($userId); + return 1; +} + +sub canEdit { + my $self = shift; + my $userId = shift || $self->session->user->userId; + return 0 if $self->isProtected and not $self->getWiki->canAdminister($userId); + return $self->couldEdit($userId); +} + +sub canProtect { + my $self = shift; + my $userId = shift || $self->session->user->userId; + return 0 if $self->{_nonexistent}; + return $self->getWiki->canAdminister($userId); +} + +sub processPropertiesFromFormPost { + my $self = shift; + my $ret = $self->SUPER::processPropertiesFromFormPost(@_); + $self->update({ groupIdView => $self->getWiki->get('groupIdView'), + groupIdEdit => $self->getWiki->get('groupIdEdit') }); + $self->getWiki->updateTitleIndex([$self], from => 'edit'); + return $ret; +} + +sub www_edit { + my $self = shift; + return $self->session->privilege->insufficient unless $self->canEdit; + + my $template = WebGUI::Asset::Template->new($self->session, $self->getWiki->get('pageEditTemplateId')); + my $var = {}; + my $newPage = 0; + $template->prepare; + + if ($self->session->form->process('func') eq 'add') { + # New page. + $newPage = 1; + $var->{'form.header'} = join '', + (WebGUI::Form::formHeader($self->session, + { action => $self->getWiki->getUrl('func=addPageSave') }), + WebGUI::Form::hidden($self->session, { name => 'class', value => ref $self })); + } else { + # Editing a page. + $newPage = 0; + $var->{'form.header'} = join '', + (WebGUI::Form::formHeader($self->session, + { action => $self->getUrl('func=editSave') })); + } + + $var->{'form.title'} = WebGUI::Form::text + ($self->session, { name => 'title', maxlength => 255, + size => 40, value => $self->get('title') }); + $var->{'form.content'} = WebGUI::Form::HTMLArea + ($self->session, { name => 'content', richEditId => $self->getWiki->get('richEditor'), + value => $self->get('content') }); + $var->{'form.submit'} = WebGUI::Form::submit + ($self->session, { value => 'Save' }); + $var->{'form.footer'} = WebGUI::Form::formFooter($self->session); + $self->_addFuncTemplateVars($var); + + my $title = "Editing ".(defined($self->get('title'))? $self->get('title') : 'new page'); + + return $self->getWiki->processStyle($self->getWiki->processMasterTemplate($title, $self->processPageTemplate($self->processTemplate($var, undef, $template), 'edit'))); +} + +sub www_editSave { + my $self = shift; + return $self->session->privilege->insufficient unless $self->canEdit; + + # TODO: refactor: duplication with A::W::Matrix::www_editListingSave + my $oldTag = WebGUI::VersionTag->getWorking($self->session, 1); + my $newTag = WebGUI::VersionTag->create + ($self->session, { name => (sprintf "%s edit of %s - %s", + $self->getWiki->get('title'), $self->get('title'), + $self->session->user->username), + workflowId => 'pbworkflow000000000003' }); + $newTag->setWorking; + + my $newSelf = $self->addRevision; + my $error = $newSelf->processPropertiesFromFormPost; + if (ref $error eq 'ARRAY') { + $self->session->stow->set('editFormErrors', $error); + $newTag->rollback; + $oldTag->setWorking if defined $oldTag; + return $self->www_edit; + } + + $newSelf->updateHistory('edited'); + $newTag->requestCommit; + $newTag->clearWorking; + $oldTag->setWorking if defined $oldTag; + + return $newSelf->www_view; +} + +#------------------------------------------------------------------- +sub www_pageHistory { + my $self = shift; + my $ago = WebGUI::International->new($self->session, 'Asset')->get('ago'); + my $i18n = WebGUI::International->new($self->session, 'Asset_WikiPage'); + + # Buggo. What to do about this query? + my @history = @{$self->session->db->buildArrayRefOfHashRefs("SELECT h.userId AS userId, u.username AS username, h.dateStamp AS dateStamp, h.actionTaken AS action FROM assetHistory AS h LEFT JOIN users AS u ON h.userId = u.userId WHERE h.assetId = ? AND h.actionTaken IN ('edited', 'trashed') UNION SELECT h.userId AS userId, u.username AS username, h.dateStamp AS dateStamp, h.actionTaken AS action FROM WikiPage_extraHistory AS h LEFT JOIN users AS u ON h.userId = u.userId WHERE h.assetId = ? ORDER BY dateStamp DESC", [$self->getId, $self->getId])}; + my $dt = $self->session->datetime; + my $time = $dt->time; + + foreach my $entry (@history) { + $entry->{date} = $dt->epochToHuman($entry->{dateStamp}); + $entry->{dateInterval} = sprintf '%s %s %s', + ($dt->secondsToInterval($time - $entry->{dateStamp}), $ago); + + $entry->{isDelete} = ($entry->{action} eq 'trashed'); + $entry->{isEdit} = ($entry->{action} eq 'edited'); + $entry->{isProtect} = ($entry->{action} eq 'protected'); + $entry->{isUnprotect} = ($entry->{action} eq 'unprotected'); + if ($entry->{isEdit}) { + $entry->{viewUrl} = $self->getUrl('func=view;revision='.$entry->{dateStamp}); + $entry->{editUrl} = $self->getUrl('func=edit;revision='.$entry->{dateStamp}); + } + + $entry->{actionN} = $i18n->get('actionN '.$entry->{action}); + } + + if ($history[-1]{action} eq 'edited') { + my $entry = $history[-1]; + $entry->{action} = 'created'; + $entry->{isEdit} = 0; + $entry->{isCreate} = 1; + $entry->{actionN} = $i18n->get('actionN created'); + } + + my $template = WebGUI::Asset::Template->new($self->session, $self->getWiki->get('pageHistoryTemplateId')); + $template->prepare; + + my $var = {}; + $var->{'ph.entries'} = \@history; + + return $self->getWiki->processStyle($self->getWiki->processMasterTemplate('History of "'.$self->get('title').'"', $self->processPageTemplate($self->processTemplate($var, undef, $template), 'pageHistory'))); +} + +#------------------------------------------------------------------- +sub updateWikiHistory { + my $self = shift; + my $action = shift; + my $userId = shift || $self->session->user->userId; + $self->session->db->write("INSERT INTO WikiPage_extraHistory (assetId, userId, dateStamp, actionTaken) VALUES (?, ?, ?, ?)", [$self->getId, $userId, $self->session->datetime->time, $action]); +} + +sub www_protect { + my $self = shift; + return $self->session->privilege->insufficient unless $self->canProtect; + return $self->www_view if $self->isProtected; + + $self->session->db->write("DELETE FROM WikiPage_protected WHERE assetId = ?", [$self->getId]); + $self->session->db->write("INSERT INTO WikiPage_protected (assetId) VALUES (?)", [$self->getId]); + $self->{_isProtected} = 1; + $self->updateWikiHistory('protected'); + return $self->www_view; +} + +sub www_unprotect { + my $self = shift; + return $self->session->privilege->insufficient unless $self->canProtect; + return $self->www_view if !$self->isProtected; + + $self->session->db->write("DELETE FROM WikiPage_protected WHERE assetId = ?", [$self->getId]); + $self->{_isProtected} = 0; + $self->updateWikiHistory('unprotected'); + return $self->www_view; +} + +1; diff --git a/lib/WebGUI/Asset/Wobject/WikiMaster.pm b/lib/WebGUI/Asset/Wobject/WikiMaster.pm new file mode 100644 index 000000000..2ff108fa8 --- /dev/null +++ b/lib/WebGUI/Asset/Wobject/WikiMaster.pm @@ -0,0 +1,389 @@ +package WebGUI::Asset::Wobject::WikiMaster; + +#------------------------------------------------------------------- +# WebGUI is Copyright 2001-2006 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 +#------------------------------------------------------------------- + +use strict; +use Tie::IxHash; +use WebGUI::International; +use WebGUI::Utility; +use HTML::Parser; +use base 'WebGUI::Asset::Wobject'; + +#------------------------------------------------------------------- +sub definition { + my $class = shift; + my $session = shift; + my $definition = shift; + my $i18n = WebGUI::International->new($session, 'Asset_WikiMaster'); + + # BUGGO: duplication with Collaboration; move this into WebGUI::Asset::RichEdit + my $richEditorOptions = $session->db->buildHashRef("select distinct(assetData.assetId), assetData.title from asset, assetData where asset.className='WebGUI::Asset::RichEdit' and asset.assetId=assetData.assetId order by assetData.title"); + + my %properties; + tie %properties, 'Tie::IxHash'; + %properties = + ( + groupToEditPages => { fieldType => 'group', + defaultValue => ['7'], + tab => 'security', + hoverHelp => $i18n->get('groupToEditPages hoverHelp'), + label => $i18n->get('groupToEditPages label') }, + + groupToAdminister => { fieldType => 'group', + defaultValue => ['3'], + tab => 'security', + hoverHelp => $i18n->get('groupToAdminister hoverHelp'), + label => $i18n->get('groupToAdminister label') }, + + richEditor => { fieldType => 'selectBox', + options => $richEditorOptions, + defaultValue => 'PBrichedit000000000001', + tab => 'display', + hoverHelp => $i18n->get('richEditor hoverHelp'), + label => $i18n->get('richEditor label') }, + + # BUGGO: how do we get this to only be able to specify pages underneath + # the asset? There's no lineage option for this field type. + defaultPage => { fieldType => 'asset', + className => 'WebGUI::Asset::WikiPage', + tab => 'display', + hoverHelp => $i18n->get('defaultPage hoverHelp'), + label => $i18n->get('defaultPage label') }, + + masterTemplateId => { fieldType => 'template', + namespace => 'WikiMaster', + defaultValue => 'WikiMasterTmpl00000001', + tab => 'display', + hoverHelp => $i18n->get('masterTemplateId hoverHelp'), + label => $i18n->get('masterTemplateId label') }, + + pageTemplateId => { fieldType => 'template', + namespace => 'WikiPage', + defaultValue => 'WikiPageTmpl0000000001', + tab => 'display', + hoverHelp => $i18n->get('pageTemplateId hoverHelp'), + label => $i18n->get('pageTemplateId label') }, + + pageHistoryTemplateId => { fieldType => 'template', + namespace => 'WikiPage_pageHistory', + defaultValue => 'WikiPHTmpl000000000001', + tab => 'display', + hoverHelp => $i18n->get('pageHistoryTemplateId hoverHelp'), + label => $i18n->get('pageHistoryTemplateId label') }, + + recentChangesTemplateId => { fieldType => 'template', + namespace => 'WikiMaster_recentChanges', + defaultValue => 'WikiRCTmpl000000000001', + tab => 'display', + hoverHelp => $i18n->get('recentChangesTemplateId hoverHelp'), + label => $i18n->get('recentChangesTemplateId label') }, + + pageListTemplateId => { fieldType => 'template', + namespace => 'WikiMaster_pageList', + defaultValue => 'WikiPLTmpl000000000001', + tab => 'display', + hoverHelp => $i18n->get('pageListTemplateId hoverHelp'), + label => $i18n->get('pageListTemplateId label') }, + ); + + push @$definition, + { + assetName => $i18n->get('assetName'), + icon => 'wikiMaster.gif', + autoGenerateForms => 1, + tableName => 'WikiMaster', + className => 'WebGUI::Asset::Wobject::WikiMaster', + properties => \%properties, + }; + + return $class->SUPER::definition($session, $definition); +} + +#------------------------------------------------------------------- +sub canEditPages { + my $self = shift; + my $userId = shift || $self->session->user->userId; + my $user = WebGUI::User->new($self->session, $userId); + return $self->canView($userId) && $user->isInGroup($self->get('groupToEditPages')); +} + +sub canAdminister { + my $self = shift; + my $userId = shift || $self->session->user->userId; + my $user = WebGUI::User->new($self->session, $userId); + return $self->canView($userId) && $user->isInGroup($self->get('groupToAdminister')); +} + +#------------------------------------------------------------------- +sub getDefaultPage { + my $self = shift; + return $self->{_defaultPage} if $self->{_defaultPage}; + + my $pageId = $self->get('defaultPage'); + if (defined $pageId) { + my $page = WebGUI::Asset->newByDynamicClass($self->session, $pageId); + if (defined $page and $page->isa('WebGUI::Asset::WikiPage') and $page->getParent->getId eq $self->getId) { + $self->{_defaultPage} = $page; + return $page; + } + } + + # No valid default page. Okay, we have to synthesize it. + my $page = WebGUI::Asset->newByPropertyHashRef + ($self->session, { className => 'WebGUI::Asset::WikiPage', + title => $self->get('title'), content => $self->get('description') }); + $page->{_parent} = $self; + $page->{_nonexistent} = 1; + $self->{_defaultPage} = $page; + return $page; +} + +sub prepareMasterTemplate { + my $self = shift; + return $self->{_masterTemplate} if $self->{_masterTemplate}; + $self->{_masterTemplate} = + WebGUI::Asset::Template->new($self->session, $self->get('masterTemplateId')); + $self->{_masterTemplate}->prepare; + return $self->{_masterTemplate}; +} + +sub processMasterTemplate { + my $self = shift; + my $title = shift; + my $content = shift; + my $var = {}; + my $template = $self->prepareMasterTemplate; + + $var->{title} = $title; + $var->{content} = $content; + $var->{displayTitle} = $self->get('displayTitle'); + $var->{canEdit} = $self->canEditPages; + $self->_addFuncTemplateVars($var, qw/addPage listPages recentChanges/); + + return $self->processTemplate($var, undef, $template); +} + +sub prepareView { + my $self = shift; + $self->SUPER::prepareView; + $self->prepareMasterTemplate; + $self->getDefaultPage->prepareView; +} + +sub _addFuncTemplateVars { + my $self = shift; + my $var = shift; + my @funcs = @_; + my $i18n = WebGUI::International->new($self->session, 'Asset_WikiMaster'); + my %specialFuncs = + (addPage => 'func=add;class=WebGUI::Asset::WikiPage'); + + foreach my $func (@funcs) { + $var->{$func.'.url'} = $self->getUrl($specialFuncs{$func} || "func=$func"); + $var->{$func.'.text'} = $i18n->get("func $func link text"); + } +} + +sub view { + my $self = shift; + return $self->getDefaultPage->view; +} + +sub getContentLastModified { + my $self = shift; + return $self->getDefaultPage->getContentLastModified; +} + +#------------------------------------------------------------------- +sub purge { + my $self = shift; + $self->session->db->write('DELETE FROM WikiMaster_titleIndex WHERE assetId = ?', [$self->getId]); + return $self->SUPER::purge; +} + +#------------------------------------------------------------------- +sub processPropertiesFromFormPost { + my $self = shift; + + # BUGGO: Duplication with A::W::Collaboration::processPropertiesFromFormPost + my $groupsChanged = + (($self->session->form->process('groupIdView') ne $self->get('groupIdView')) + or ($self->session->form->process('groupIdEdit') ne $self->get('groupIdEdit'))); + my $ret = $self->SUPER::processPropertiesFromFormPost(@_); + + if ($groupsChanged) { + foreach my $child (@{$self->getLineage(['children'], {returnObjects => 1})}) { + $child->update({ groupIdView => $self->get('groupIdView'), + groupIdEdit => $self->get('groupIdEdit') }); + } + } + + return $ret; +} + +#------------------------------------------------------------------- +sub updateTitleIndex { + my $self = shift; + my @pages = @{+shift}; + my %opts = @_; + return unless @pages; + $self->session->db->write("DELETE FROM WikiMaster_titleIndex WHERE assetId = ? AND pageId IN (".join(', ', ('?') x @pages).")", [$self->getId, map{$_->getId} @pages]); + + foreach my $page (@pages) { + my ($pageId, $title) = ($page->getId, $page->get('title')); + $self->session->db->write("INSERT INTO WikiMaster_titleIndex (assetId, pageId, title) VALUES (?, ?, ?)", [$self->getId, $pageId, $title]); + } +} + +sub autolinkHtml { + my $self = shift; + my $html = shift; + + # TODO: caching? Maybe in WikiPage. + my %mapping = $self->session->db->buildHash("SELECT LOWER(i.title), d.url FROM WikiMaster_titleIndex AS i INNER JOIN assetData AS d ON i.pageId = d.assetId WHERE i.assetId = ?", [$self->getId]); + return $html unless %mapping; + + foreach my $key (keys %mapping) { + $mapping{$key} = WebGUI::HTML::format('/'.$mapping{$key}, 'text'); + } + + my $matchString = join('|', map{quotemeta} keys %mapping); + my $regexp = qr/\b($matchString)\b/i; + + my @acc = (); + my $in_a = 0; + my $p = HTML::Parser->new; + $p->case_sensitive(1); + $p->marked_sections(1); + $p->unbroken_text(1); + $p->handler(start => sub { push @acc, $_[2]; if ($_[0] eq 'a' and exists $_[1]{href}) { $in_a++ } }, + 'tagname, attr, text'); + $p->handler(end => sub { push @acc, $_[2]; if ($_[0] eq 'a' and exists $_[1]{href}) { $in_a-- } }, + 'tagname, attr, text'); + $p->handler(text => sub { + my $text = $_[0]; + unless ($in_a) { + while ($text =~ s#^(.*?)$regexp##i) { + push @acc, sprintf '%s%s', + ($1, $mapping{lc $2}, $2); + } + } + push @acc, $text; + }, 'text'); + $p->handler(default => sub { push @acc, $_[0] }, 'text'); + $p->parse($html); + $p->eof; + undef $p; # Just in case there might be reference loops. + + return join '', @acc; +} + +#------------------------------------------------------------------- +sub www_addPageSave { + my $self = shift; + my $pageClass = $self->session->form->process('class'); + return $self->session->privilege->insufficient unless + $self->canEditPages and UNIVERSAL::isa($pageClass, 'WebGUI::Asset::WikiPage'); + + # Refactor: duplication with A::W::Matrix::www_editListingSave + my $oldTag = WebGUI::VersionTag->getWorking($self->session, 1); + my $newTag = WebGUI::VersionTag->create + ($self->session, { name => (sprintf "%s create of %s - %s", + $self->get('title'), $self->session->form->process('title'), + $self->session->user->username), + workflowId => 'pbworkflow000000000003' }); + $newTag->setWorking; + + # Hrm. Duplication with Asset::www_editSave. How to fix that? + my $page = $self->addChild({ className => $pageClass }); + $page->{_parent} = $self; + + my $error = $page->processPropertiesFromFormPost; + if (ref $error eq 'ARRAY') { + $self->session->stow->set('editFormErrors', $error); + $page->purge; + return $self->www_add; + } + + $page->updateHistory('edited'); + $newTag->requestCommit; + $newTag->clearWorking; + $oldTag->setWorking if defined $oldTag; + return $page->www_view; +} + +#------------------------------------------------------------------- +sub www_listPages { + # TODO: template, i18n + my $self = shift; + my $i18n = WebGUI::International->new($self->session, 'Asset_WikiMaster'); + my $title = $i18n->get('listPages title'); + my @pages = @{$self->getLineage(['children'], {returnObjects => 1})}; + my $var = {}; + my $template = WebGUI::Asset::Template->new($self->session, $self->get('pageListTemplateId')); + $template->prepare; + + $var->{'pl.entries'} = [map { + my $page = $_; + my $subvar = {}; + $subvar->{pageUrl} = WebGUI::HTML::format($page->getUrl, 'text'); + $subvar->{pageTitle} = WebGUI::HTML::format($page->get('title'), 'text'); + $subvar; + } @pages]; + + return $self->processStyle($self->processMasterTemplate($title, $self->processTemplate($var, undef, $template))); +} + +#------------------------------------------------------------------- +sub www_recentChanges { + my $self = shift; + my $ago = WebGUI::International->new($self->session, 'Asset')->get('ago'); + my $dt = $self->session->datetime; + + # Buggo: hardcoded number of recent changes + # TODO: query should have both time limit and number limit, settable by form elements... + my @changes = @{$self->session->db->buildArrayRefOfHashRefs("SELECT h.userId AS userId, u.username AS username, h.dateStamp AS dateStamp, h.actionTaken AS action, d.url AS url, d.title AS title FROM assetHistory AS h LEFT JOIN users AS u ON h.userId = u.userId INNER JOIN asset AS a ON h.assetId = a.assetId INNER JOIN assetData AS d ON a.assetId = d.assetId AND h.dateStamp = d.revisionDate WHERE a.lineage LIKE CONCAT(?, '%') AND a.assetId <> ? AND h.actionTaken IN ('edited', 'trashed') ORDER BY dateStamp DESC LIMIT 0,50", [$self->get('lineage'), $self->getId])}; + + # Buggo, duplication with WikiPage::www_pageHistory? + my $time = $dt->time; + my @days = (); + foreach my $entry (@changes) { + $entry->{date} = $dt->epochToHuman($entry->{dateStamp}, '%z'); + $entry->{time} = $dt->epochToHuman($entry->{dateStamp}, '%Z'); + $entry->{dateInterval} = sprintf '%s %s %s', + ($dt->secondsToInterval($time - $entry->{dateStamp}), $ago); + $entry->{isDelete} = ($entry->{action} eq 'trashed'); + $entry->{isEdit} = ($entry->{action} eq 'edited'); + $entry->{viewUrl} = $entry->{url}; + + # TODO: actionC, and also lowercased version, and change WikiPage to comply + # with that also + + if (!@days || $entry->{date} ne $days[-1][0]{date}) { + push @days, [$entry]; + } else { + push @{$days[-1]}, $entry; + } + } + + my $template = WebGUI::Asset::Template->new($self->session, $self->get('recentChangesTemplateId')); + $template->prepare; + + my $var = {}; + $var->{'rc.entries'} = \@changes; + $var->{'rc.days'} = [map{{'day.date' => $$_[0]{date}, 'day.entries' => $_}} @days]; + + my $title = "Recent changes"; + + return $self->processStyle($self->processMasterTemplate($title, $self->processTemplate($var, undef, $template))); +} + +1; diff --git a/lib/WebGUI/i18n/English/Asset_WikiMaster.pm b/lib/WebGUI/i18n/English/Asset_WikiMaster.pm new file mode 100644 index 000000000..d4623c16b --- /dev/null +++ b/lib/WebGUI/i18n/English/Asset_WikiMaster.pm @@ -0,0 +1,58 @@ +package WebGUI::i18n::English::Asset_WikiMaster; + +our $I18N = +{ + 'assetName' => + { lastUpdated => 1160157064, message => 'Wiki' }, + + 'groupToEditPages hoverHelp' => + { lastUpdated => 1160157064, message => q|Choose a group of users who will be able to edit pages in this wiki instance. They will not, by default, be able to delete pages or revisions, or edit protected pages.| }, + 'groupToEditPages label' => + { lastUpdated => 1160157064, message => q|Who can edit pages?| }, + + 'groupToAdminister hoverHelp' => + { lastUpdated => 1160157064, message => q|Choose a group of users who will be able to perform administrative actions on pages in this wiki instance; such actions include deletion of pages and page revisions, and protecting and unprotecting of pages.| }, + 'groupToAdminister label' => + { lastUpdated => 1160157064, message => q|Who can administer?| }, + + 'richEditor hoverHelp' => + { lastUpdated => 1160157064, message => q|Which rich editor to use for editing pages in this wiki instance.| }, + 'richEditor label' => + { lastUpdated => 1160157064, message => q|Rich Editor| }, + + 'defaultPage hoverHelp' => + { lastUpdated => 1160157064, message => q|Which page to display by default when someone browses to the wiki's main URL.| }, + 'defaultPage label' => + { lastUpdated => 1160157064, message => q|Default Page| }, + + 'pageTemplateId hoverHelp' => + { lastUpdated => 1160157064, message => q|Which template to use to display pages.| }, + 'pageTemplateId label' => + { lastUpdated => 1160157064, message => q|Page Template| }, + + 'recentChangesTemplateId hoverHelp' => + { lastUpdated => 1160157064, message => q|Which template to use for the recent changes display.| }, + 'recentChangesTemplateId label' => + { lastUpdated => 1160157064, message => q|Recent Changes Template| }, + + 'pageHistoryTemplateId hoverHelp' => + { lastUpdated => 1160505291, message => q|Which template to use for the page history display.| }, + 'pageHistoryTemplateId label' => + { lastUpdated => 1160505291, message => q|Page History Template| }, + + 'pageListTemplateId hoverHelp' => + { lastUpdated => 1160417517, message => q|Which template to use for displaying lists of pages.| }, + 'pageListTemplateId label' => + { lastUpdated => 1160417517, message => q|Page List Template| }, + + 'func addPage link text' => + { lastUpdated => 1160157064, message => q|Add a new page| }, + 'func listPages link text' => + { lastUpdated => 1160417517, message => q|List all pages| }, + 'func recentChanges link text' => + { lastUpdated => 1160768887, message => q|Show recent changes| }, + 'listPages title' => + { lastUpdated => 1160417517, message => q|List of pages| }, +}; + +1; diff --git a/lib/WebGUI/i18n/English/Asset_WikiPage.pm b/lib/WebGUI/i18n/English/Asset_WikiPage.pm new file mode 100644 index 000000000..cbdf5cd38 --- /dev/null +++ b/lib/WebGUI/i18n/English/Asset_WikiPage.pm @@ -0,0 +1,31 @@ +package WebGUI::i18n::English::Asset_WikiPage; + +our $I18N = +{ + 'assetName' => + { lastUpdated => 1160157064, message => 'Wiki Page' }, + + 'func edit link text' => + { lastUpdated => 1160157064, message => q|Edit this page| }, + 'func view link text' => + { lastUpdated => 1160157064, message => q|View this page| }, + 'func pageHistory link text' => + { lastUpdated => 1160425002, message => q|View this page's history| }, + 'func protect link text' => + { lastUpdated => 1160425002, message => q|Protect this page| }, + 'func unprotect link text' => + { lastUpdated => 1160425002, message => q|Unprotect this page| }, + + 'actionN edited' => + { lastUpdated => 1160505291, message => q|Edited| }, + 'actionN trashed' => + { lastUpdated => 1160505291, message => q|Deleted| }, + 'actionN protected' => + { lastUpdated => 1160505291, message => q|Protected| }, + 'actionN unprotected' => + { lastUpdated => 1160505291, message => q|Unprotected| }, + 'actionN created' => + { lastUpdated => 1160505291, message => q|Created| }, +}; + +1;