webgui/lib/WebGUI/Asset/WikiPage.pm

677 lines
20 KiB
Perl

package WebGUI::Asset::WikiPage;
# -------------------------------------------------------------------
# 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
# -------------------------------------------------------------------
use strict;
use Class::C3;
use base qw(
WebGUI::AssetAspect::Subscribable
WebGUI::AssetAspect::Comments
WebGUI::AssetAspect::AutoSynopsis
WebGUI::Asset
);
use Tie::IxHash;
use WebGUI::International;
use WebGUI::Utility;
use WebGUI::VersionTag;
#-------------------------------------------------------------------
=head2 addChild ( )
You can't add children to a wiki page.
=cut
sub addChild {
return undef;
}
#-------------------------------------------------------------------
=head2 addRevision ( )
Override the default method in order to deal with attachments.
=cut
sub addRevision {
my $self = shift;
my $newSelf = $self->next::method(@_);
my $now = time();
$newSelf->update({
isHidden => 1,
});
return $newSelf;
}
#-------------------------------------------------------------------
=head2 canAdd ($session)
This functions as a class or an object method. It sets the subclassGroupId to 7
instead of the default of 12.
=cut
sub canAdd {
my $class = shift;
my $session = shift;
return $class->next::method($session, undef, '7');
}
#-------------------------------------------------------------------
=head2 canEdit
Returns true if the current user can administer the wiki containing this WikiPage, or
if the current user can edit wiki pages and is trying to add or edit pages, or the page
is not protected.
=cut
sub canEdit {
my $self = shift;
my $wiki = $self->getWiki;
return undef unless defined $wiki;
my $form = $self->session->form;
my $addNew = $form->process("func" ) eq "add";
my $editSave = $form->process("assetId" ) eq "new"
&& $form->process("func" ) eq "editSave"
&& $form->process("class","className" ) eq "WebGUI::Asset::WikiPage";
return $wiki->canAdminister
|| ( $wiki->canEditPages && ( $addNew || $editSave || !$self->isProtected) );
}
#-------------------------------------------------------------------
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 =
(
content => { fieldType => "HTMLArea",
defaultValue => undef },
views => {
fieldType => "integer",
defaultValue => 0,
noFormPost => 1
},
isProtected => {
fieldType => "yesNo",
defaultValue => 0,
noFormPost => 1
},
actionTaken => {
fieldType => "text",
defaultValue => '',
noFormPost => 1,
},
actionTakenBy => {
fieldType => "user",
defaultValue => '',
noFormPost => 1,
},
isFeatured => {
fieldType => "yesNo",
defaultValue => 0,
noFormPost => 1,
},
);
push @$definition,
{
assetName => $i18n->get('assetName'),
icon => 'wikiPage.gif',
autoGenerateForms => 1,
tableName => 'WikiPage',
className => 'WebGUI::Asset::WikiPage',
properties => \%properties,
};
return $class->next::method($session, $definition);
}
#-------------------------------------------------------------------
=head2 getAutoCommitWorkflowId
Overrides the master class to handle spam prevention. If the content matches any of
the spamStopWords, then the commit is canceled and the content is rolled back to the
previous version. Otherwise, it returns the autoCommitWorkflowId for the regular asset
commit flow to handle.
=cut
sub getAutoCommitWorkflowId {
my $self = shift;
my $wiki = $self->getWiki;
if ($wiki->hasBeenCommitted) {
# delete spam
my $spamStopWords = $self->session->config->get('spamStopWords');
if (ref $spamStopWords eq 'ARRAY' && @{ $spamStopWords }) {
my $spamRegex = join('|',@{$spamStopWords});
$spamRegex =~ s/\s/\\ /g;
if ($self->get('content') =~ m{$spamRegex}xmsi) {
my $tag = WebGUI::VersionTag->new($self->session, $self->get('tagId'));
$self->purgeRevision;
if ($tag->getAssetCount == 0) {
$tag->rollback;
}
return undef;
}
}
return $wiki->get('approvalWorkflow')
|| $self->session->setting->get('defaultVersionTagWorkflow');
}
return undef;
}
#-------------------------------------------------------------------
=head2 getEditForm
Renders a templated edit form for adding or editing a wiki page.
=cut
sub getEditForm {
my $self = shift;
my $session = $self->session;
my $form = $session->form;
my $i18n = WebGUI::International->new($session, "Asset_WikiPage");
my $newPage = 0;
my $wiki = $self->getWiki;
my $url = ($self->getId eq "new") ? $wiki->getUrl : $self->getUrl;
my $var = {
title=> $i18n->get("editing")." ".(defined($self->get('title'))? $self->get('title') : $i18n->get("assetName")),
formHeader => WebGUI::Form::formHeader($session, { action => $url})
.WebGUI::Form::hidden($session, { name => 'func', value => 'editSave' })
.WebGUI::Form::hidden($session, { name=>"proceed", value=>"showConfirmation" }),
formTitle => WebGUI::Form::text($session, { name => 'title', maxlength => 255, size => 40,
value => $self->get('title'), defaultValue=>$form->get("title","text") }),
formContent => WebGUI::Form::HTMLArea($session, { name => 'content', richEditId => $wiki->get('richEditor'), value => $self->get('content') }) ,
formSubmit => WebGUI::Form::submit($session, { value => 'Save' }),
formProtect => WebGUI::Form::yesNo($session, { name => "isProtected", value=>$self->getValue("isProtected")}),
formFeatured => WebGUI::Form::yesNo( $session, { name => 'isFeatured', value=>$self->getValue('isFeatured')}),
formKeywords => WebGUI::Form::keywords($session, {
name => "keywords",
value => WebGUI::Keyword->new($session)->getKeywordsForAsset({asset=>$self}),
}),
allowsAttachments => $wiki->get("allowAttachments"),
formFooter => WebGUI::Form::formFooter($session),
isNew => ($self->getId eq "new"),
canAdminister => $wiki->canAdminister,
deleteConfirm => $i18n->get("delete page confirmation"),
deleteLabel => $i18n->get("deleteLabel"),
deleteUrl => $self->getUrl("func=delete"),
titleLabel => $i18n->get("titleLabel"),
contentLabel => $i18n->get("contentLabel"),
attachmentLabel => $i18n->get("attachmentLabel"),
protectQuestionLabel => $i18n->get("protectQuestionLabel"),
isProtected => $self->isProtected
};
my $children = [];
if ($self->getId eq "new") {
$var->{formHeader} .= WebGUI::Form::hidden($session, { name=>"assetId", value=>"new" })
.WebGUI::Form::hidden($session, { name=>"class", value=>$form->process("class","className") });
} else {
$children = $self->getLineage(["children"]);
}
$var->{formAttachment} = WebGUI::Form::Attachments($session, {
value => $children,
maxAttachments => $wiki->get("allowAttachments"),
maxImageSize => $wiki->get("maxImageSize"),
thumbnailSize => $wiki->get("thumbnailSize"),
});
return $self->processTemplate($var, $wiki->getValue('pageEditTemplateId'));
}
#-------------------------------------------------------------------
=head2 getSubscriptionTemplate ( )
=cut
sub getSubscriptionTemplate {
my ( $self ) = @_;
return $self->getParent->getSubscriptionTemplate;
}
#-------------------------------------------------------------------
=head2 getTemplateVars ( )
Get the common template vars for this asset
=cut
sub getTemplateVars {
my ( $self ) = @_;
my $session = $self->session;
my $i18n = WebGUI::International->new($session, "Asset_WikiPage");
my $wiki = $self->getWiki;
my $owner = WebGUI::User->new( $session, $self->get('ownerUserId') );
my $keyObj = WebGUI::Keyword->new($session);
my $keywords = $keyObj->getKeywordsForAsset({
asset => $self,
asArrayRef => 1,
});
my @keywordsLoop = ();
foreach my $word (@{$keywords}) {
push @keywordsLoop, {
keyword => $word,
url => $wiki->getUrl("func=byKeyword;keyword=".$word),
};
}
my $var = {
%{ $self->get },
url => $self->getUrl,
keywordsLoop => \@keywordsLoop,
viewLabel => $i18n->get("viewLabel"),
editLabel => $i18n->get("editLabel"),
historyLabel => $i18n->get("historyLabel"),
wikiHomeLabel => $i18n->get("wikiHomeLabel", "Asset_WikiMaster"),
searchLabel => $i18n->get("searchLabel", "Asset_WikiMaster"),
searchUrl => $wiki->getUrl("func=search"),
recentChangesUrl => $wiki->getUrl("func=recentChanges"),
recentChangesLabel => $i18n->get("recentChangesLabel", "Asset_WikiMaster"),
mostPopularUrl => $wiki->getUrl("func=mostPopular"),
mostPopularLabel => $i18n->get("mostPopularLabel", "Asset_WikiMaster"),
wikiHomeUrl => $wiki->getUrl,
historyUrl => $self->getUrl("func=getHistory"),
editContent => $self->getEditForm,
allowsAttachments => $wiki->get("allowAttachments"),
comments => $self->getFormattedComments(),
canEdit => $self->canEdit,
canAdminister => $wiki->canAdminister,
isProtected => $self->isProtected,
content => $wiki->autolinkHtml(
$self->scrubContent,
{skipTitles => [$self->get('title')]},
),
isSubscribed => $self->isSubscribed,
subscribeUrl => $self->getSubscribeUrl,
unsubscribeUrl => $self->getUnsubscribeUrl,
owner => $owner->get('alias'),
};
return $var;
}
#-------------------------------------------------------------------
=head2 getWiki
Returns an object referring to the wiki that contains this page. If it is not a WikiMaster,
or the parent is undefined, it returns undef.
=cut
sub getWiki {
my $self = shift;
my $parent = $self->getParent;
return undef unless defined $parent and $parent->isa('WebGUI::Asset::Wobject::WikiMaster');
return $parent;
}
#-------------------------------------------------------------------
=head2 indexContent
Extends the master class to handle indexing the wiki content.
=cut
sub indexContent {
my $self = shift;
my $indexer = $self->next::method;
$indexer->addKeywords($self->get('content'));
return $indexer;
}
#-------------------------------------------------------------------
=head2 isProtected
Returns a boolean indicating whether or not this WikiPage is protected.
=cut
sub isProtected {
my $self = shift;
return $self->get("isProtected");
}
#-------------------------------------------------------------------
=head2 preparePageTemplate
This is essentially prepareView, but is smart and will only do the template
preparation once. Returns the preparted page template.
=cut
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};
}
#-------------------------------------------------------------------
=head2 prepareView
Extends the master class to handle preparing the main view template for the page.
=cut
sub prepareView {
my $self = shift;
$self->next::method;
$self->preparePageTemplate;
}
#-------------------------------------------------------------------
=head2 processPropertiesFromFormPost
Extends the master method to handle properties and attachments.
=cut
sub processPropertiesFromFormPost {
my $self = shift;
my $session = $self->session;
$self->next::method(@_);
my $actionTaken = ($session->form->process("assetId") eq "new") ? "Created" : "Edited";
my $wiki = $self->getWiki;
my $properties = {
groupIdView => $wiki->get('groupIdView'),
groupIdEdit => $wiki->get('groupToAdminister'),
actionTakenBy => $self->session->user->userId,
actionTaken => $actionTaken,
};
if ($wiki->canAdminister) {
$properties->{isProtected} = $session->form->get("isProtected");
$properties->{isFeatured} = $session->form->get("isFeatured");
}
($properties->{synopsis}) = $self->getSynopsisAndContent(undef, $self->get('content'));
$self->update($properties);
# deal with attachments from the attachments form control
my $options = {
maxImageSize => $wiki->get('maxImageSize'),
thumbnailSize => $wiki->get('thumbnailSize'),
};
my @attachments = $session->form->param("attachments");
my @tags = ();
foreach my $assetId (@attachments) {
my $asset = WebGUI::Asset->newByDynamicClass($session, $assetId);
if (defined $asset) {
unless ($asset->get("parentId") eq $self->getId) {
$asset->setParent($self);
$asset->update({
ownerUserId => $self->get( "ownerUserId" ),
groupIdEdit => $wiki->get( "groupToEditPages" ),
groupIdView => $self->get( "groupIdView" ),
});
}
$asset->applyConstraints($options);
push(@tags, $asset->get("tagId"));
$asset->setVersionTag($self->get("tagId"));
}
}
# clean up empty tags
foreach my $tag (@tags) {
my $version = WebGUI::VersionTag->new($self->session, $tag);
if (defined $version) {
if ($version->getAssetCount == 0) {
$version->rollback;
}
}
}
}
#-------------------------------------------------------------------
=head2 scrubContent ( [ content ] )
Uses WikiMaster settings to remove unwanted markup and apply site wide replacements.
=head3 content
Optionally pass the ontent that we want to run the filters on. Otherwise we get it from self.
=cut
sub scrubContent {
my $self = shift;
my $content = shift || $self->get("content");
$content =~ s/\^-\;//g;
my $scrubbedContent = WebGUI::HTML::filter($content, $self->getWiki->get("filterCode"));
if ($self->getWiki->get("useContentFilter")) {
$scrubbedContent = WebGUI::HTML::processReplacements($self->session, $scrubbedContent);
}
return $scrubbedContent;
}
#-------------------------------------------------------------------
=head2 update
Wrap update to force isHidden to be on, all the time.
=cut
sub update {
my $self = shift;
my $properties = shift;
$properties->{isHidden} = 1;
return $self->next::method($properties);
}
#-------------------------------------------------------------------
=head2 validParent
Make sure that the current session asset is a WikiMaster for pasting and adding checks.
This is a class method.
=cut
sub validParent {
my $class = shift;
my $session = shift;
return $session->asset->isa('WebGUI::Asset::Wobject::WikiMaster');
}
#-------------------------------------------------------------------
=head2 view
Renders this asset.
=cut
sub view {
my $self = shift;
return $self->processTemplate($self->getTemplateVars, $self->getWiki->get("pageTemplateId"));
}
#-------------------------------------------------------------------
=head2 www_delete
Overrides the master method so that privileges are checked on the parent wiki instead
of the page. Returns the user to viewing the wiki.
=cut
sub www_delete {
my $self = shift;
return $self->session->privilege->insufficient unless $self->getWiki->canAdminister;
$self->trash;
$self->session->asset($self->getParent);
return $self->getParent->www_view;
}
#-------------------------------------------------------------------
=head2 www_edit
Overrides the master class to render the edit form in the parent wiki's style.
=cut
sub www_edit {
my $self = shift;
return $self->session->privilege->insufficient unless $self->canEdit;
return $self->session->privilege->locked unless $self->canEditIfLocked;
return $self->getWiki->processStyle($self->getEditForm);
}
#-------------------------------------------------------------------
=head2 www_getHistory
Returns the version history of this wiki page. The output is templated.
=cut
sub www_getHistory {
my $self = shift;
return $self->session->privilege->insufficient unless $self->canEdit;
my $var = {};
my ($icon, $date) = $self->session->quick(qw(icon datetime));
my $i18n = WebGUI::International->new($self->session, 'Asset_WikiPage');
foreach my $revision (@{$self->getRevisions}) {
my $user = WebGUI::User->new($self->session, $revision->get("actionTakenBy"));
push(@{$var->{pageHistoryEntries}}, {
toolbar => $icon->delete("func=purgeRevision;revisionDate=".$revision->get("revisionDate"), $revision->get("url"), $i18n->get("delete confirmation"))
.$icon->edit('func=edit;revision='.$revision->get("revisionDate"), $revision->get("url"))
.$icon->view('func=view;revision='.$revision->get("revisionDate"), $revision->get("url")),
date => $date->epochToHuman($revision->get("revisionDate")),
username => $user->profileField('alias') || $user->username,
actionTaken => $revision->get("actionTaken"),
interval => join(" ", $date->secondsToInterval(time() - $revision->get("revisionDate")))
});
}
return $self->processTemplate($var, $self->getWiki->get('pageHistoryTemplateId'));
}
#-------------------------------------------------------------------
=head2 www_purgeRevision
Override the main method to change which group is allowed to purge revisions for WikiPages. Only
members who can administer the parent wiki (canAdminister) can purge revisions.
=cut
sub www_purgeRevision {
my $self = shift;
my $session = $self->session;
return $session->privilege->insufficient() unless $self->getWiki->canAdminister;
my $revisionDate = $session->form->process("revisionDate");
return undef unless $revisionDate;
my $asset = WebGUI::Asset->new($session, $self->getId, $self->get("className"), $revisionDate);
return undef if ($asset->get('revisionDate') != $revisionDate);
my $parent = $asset->getParent;
$asset->purgeRevision;
if ($session->form->process("proceed") eq "manageRevisionsInTag") {
my $working = (defined $self) ? $self : $parent;
$session->http->setRedirect($working->getUrl("op=manageRevisionsInTag"));
return undef;
}
unless (defined $self) {
return $parent->www_view;
}
return $self->www_manageRevisions;
}
#-------------------------------------------------------------------
=head2 www_restoreWikiPage
Publishes a wiki page that has been put into the trash or the clipboard.
=cut
sub www_restoreWikiPage {
my $self = shift;
return $self->session->privilege->insufficient unless $self->getWiki->canAdminister;
$self->publish;
return $self->www_view;
}
#-------------------------------------------------------------------
=head2 www_showConfirmation ( )
Shows a confirmation message letting the user know their page has been submitted.
=cut
sub www_showConfirmation {
my $self = shift;
my $i18n = WebGUI::International->new($self->session, "Asset_WikiPage");
return $self->getWiki->processStyle('<p>'.$i18n->get("page received").'</p><p><a href="'.$self->getWiki->getUrl.'">'.$i18n->get("493","WebGUI").'</a></p>');
}
#-------------------------------------------------------------------
=head2 www_view
Override the master method to count the number of times this page has been viewed,
and to render it with the parent's style.
=cut
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->isVisitor);
$self->session->http->sendHeader;
$self->prepareView;
return $self->getWiki->processStyle($self->view);
}
1;