Merge branch 'master' of git://github.com/pdonelan/webgui into filepump

This commit is contained in:
Colin Kuskie 2009-05-16 14:44:27 -07:00
commit 8e3891a7bd
44 changed files with 5516 additions and 80 deletions

View file

@ -17,6 +17,7 @@ package WebGUI::Asset;
use Carp qw( croak confess );
use Scalar::Util qw( blessed );
use Clone qw(clone);
use JSON;
use WebGUI::AssetBranch;
use WebGUI::AssetClipboard;
@ -1927,8 +1928,9 @@ sub outputWidgetMarkup {
my $styleTemplateId = shift;
# construct / retrieve the values we'll use later.
my $assetId = $self->getId;
my $session = $self->session;
my $assetId = $self->getId;
my $hexId = $session->id->toHex($assetId);
my $conf = $session->config;
my $extras = $conf->get('extrasURL');
@ -1952,8 +1954,10 @@ sub outputWidgetMarkup {
$content = $self->session->style->process($content,$styleTemplateId);
}
WebGUI::Macro::process($session, \$content);
$session->log->warn($content);
my ($headTags, $body) = WebGUI::HTML::splitHeadBody($content);
my $jsonContent = to_json( { "asset$assetId" => { content => $body } } );
$body = $content;
my $jsonContent = to_json( { "asset$hexId" => { content => $body } } );
$storage->addFileFromScalar("$assetId.js", "data = $jsonContent");
my $jsonUrl = $storage->getUrl("$assetId.js");
@ -1979,15 +1983,15 @@ sub outputWidgetMarkup {
<script type='text/javascript' src='$wgWidgetJs'></script>
<script type='text/javascript'>
function setupPage() {
WebGUI.widgetBox.doTemplate('widget$assetId'); WebGUI.widgetBox.retargetLinksAndForms();
WebGUI.widgetBox.doTemplate('widget$hexId'); WebGUI.widgetBox.retargetLinksAndForms();
WebGUI.widgetBox.initButton( { 'wgWidgetPath' : '$wgWidgetPath', 'fullUrl' : '$fullUrl', 'assetId' : '$assetId', 'width' : $width, 'height' : $height, 'templateId' : '$templateId' } );
}
YAHOO.util.Event.addListener(window, 'load', setupPage);
</script>
$headTags
</head>
<body id="widget$assetId">
\${asset$assetId.content}
<body id="widget$hexId">
\${asset$hexId.content}
</body>
</html>
OUTPUT

900
lib/WebGUI/Asset/Story.pm Normal file
View file

@ -0,0 +1,900 @@
package WebGUI::Asset::Story;
=head1 LEGAL
-------------------------------------------------------------------
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
-------------------------------------------------------------------
=cut
use strict;
use Class::C3;
use base 'WebGUI::Asset';
use Tie::IxHash;
use WebGUI::Utility;
use WebGUI::International;
use JSON qw/from_json to_json/;
use Storable qw/dclone/;
use Data::Dumper;
=head1 NAME
Package WebGUI::Asset::Story
=head1 DESCRIPTION
The Story Asset is like a Thread for the Collaboration.
=head1 SYNOPSIS
use WebGUI::Asset::Story;
=head1 METHODS
These methods are available from this class:
=cut
#-------------------------------------------------------------------
=head2 addChild ( )
You can't add children to a Story.
=cut
sub addChild {
return undef;
}
#-------------------------------------------------------------------
=head2 addRevision
Copy storage locations so that purging individual revisions works correctly.
Request autocommit.
=cut
sub addRevision {
my $self = shift;
my $session = $self->session;
my $newSelf = $self->next::method(@_);
my $newProperties = {
isHidden => 1,
};
$newSelf->update($newProperties);
my $newPhotoData = $newSelf->duplicatePhotoData;
$newSelf->setPhotoData($newPhotoData);
$newSelf->requestAutoCommit;
return $newSelf;
}
#-------------------------------------------------------------------
=head2 definition ( session, definition )
defines asset properties for New Asset instances. You absolutely need
this method in your new Assets.
=head3 session
=head3 definition
A hash reference passed in from a subclass definition.
=cut
sub definition {
my $class = shift;
my $session = shift;
my $definition = shift;
my %properties;
tie %properties, 'Tie::IxHash';
my $i18n = WebGUI::International->new($session, 'Asset_Story');
%properties = (
headline => {
fieldType => 'text',
#label => $i18n->get('headline'),
#hoverHelp => $i18n->get('headline help'),
defaultValue => '',
},
subtitle => {
fieldType => 'textarea',
#label => $i18n->get('subtitle'),
#hoverHelp => $i18n->get('subtitle help'),
defaultValue => '',
},
byline => {
fieldType => 'text',
#label => $i18n->get('byline'),
#hoverHelp => $i18n->get('byline help'),
defaultValue => '',
},
location => {
fieldType => 'text',
#label => $i18n->get('location'),
#hoverHelp => $i18n->get('location help'),
defaultValue => '',
},
highlights => {
fieldType => 'textarea',
#label => $i18n->get('highlights'),
#hoverHelp => $i18n->get('highlights help'),
defaultValue => '',
},
story => {
fieldType => 'HTMLArea',
#label => $i18n->get('highlights'),
#hoverHelp => $i18n->get('highlights help'),
#richEditId => $self->parent->getStoryRichEdit,
defaultValue => '',
},
photo => {
fieldType => 'text',
defaultValue => '[]',
noFormPost => 1,
},
);
push(@{$definition}, {
assetName => $i18n->get('assetName'),
icon => 'assets.gif',
tableName => 'Story',
className => 'WebGUI::Asset::Story',
properties => \%properties,
autoGenerateForms => 0,
});
return $class->next::method($session, $definition);
}
#-------------------------------------------------------------------
=head2 duplicate ( )
Extent the method from Asset to handle duplicating storage locations.
=cut
sub duplicate {
my $self = shift;
my $newSelf = $self->next::method(@_);
my $newPhotoData = $newSelf->duplicatePhotoData;
$newSelf->setPhotoData($newPhotoData);
return $newSelf;
}
#-------------------------------------------------------------------
=head2 duplicatePhotoData ( )
Duplicate photo data, particularly storage locations. Returns the duplicated
perl structure.
=cut
sub duplicatePhotoData {
my $self = shift;
my $session = $self->session;
my $photoData = $self->getPhotoData;
PHOTO: foreach my $photo (@{ $photoData }) {
next PHOTO unless $photo->{storageId};
my $oldStorage = WebGUI::Storage->get($session, $photo->{storageId});
my $newStorage = $oldStorage->copy;
$photo->{storageId} = $newStorage->getId;
}
return $photoData;
}
#-------------------------------------------------------------------
=head2 exportAssetData ( )
See WebGUI::AssetPackage::exportAssetData() for details.
Adds all storage locations to the package data.
=cut
sub exportAssetData {
my $self = shift;
my $exportData = $self->next::method;
PHOTO: foreach my $photo (@{ $self->getPhotoData }) {
next PHOTO unless $photo->{storageId};
push @{ $exportData->{storage} }, $photo->{storageId};
}
return $exportData;
}
#-------------------------------------------------------------------
=head2 formatDuration ( $lastUpdated )
Format the time since this story was last updated. If it is longer than 1 week, then
return the date.
=head3 $lastUpdated
The date this was last updated. If left blank, it uses the revisionDate.
=cut
sub formatDuration {
my ($self, $lastUpdated) = @_;
$lastUpdated = defined $lastUpdated ? $lastUpdated : $self->get('revisionDate');
my $session = $self->session;
my $datetime = $session->datetime;
my $duration = time() - $lastUpdated;
if ($duration > 86400) { ##1 day
return join ' ', $datetime->secondsToInterval($duration);
}
else {
my $formattedDuration = '';
my $hours = int($duration/3600) * 3600;
my @hours = $datetime->secondsToInterval($hours);
if ($hours[0]) {
$formattedDuration = join ' ', @hours;
}
my $minutes = round(($duration - $hours)/60)*60;
my @minutes = $datetime->secondsToInterval($minutes);
if ($minutes[0]) {
$formattedDuration .= ', ', if $formattedDuration;
$formattedDuration .= join ' ', @minutes;
}
return $formattedDuration;
}
}
#-------------------------------------------------------------------
=head2 getArchive ( )
Returns the parent archive for this Story. Cache the entry for speed.
=cut
sub getArchive {
my $self = shift;
if (!$self->{_archive}) {
$self->{_archive} = $self->getParent->getParent;
}
return $self->{_archive};
}
#-------------------------------------------------------------------
=head2 getAutoCommitWorkflowId ( )
Get the autocommit workflow from the archive containing this Story and
use it.
=cut
sub getAutoCommitWorkflowId {
my $self = shift;
my $archive = $self->getArchive;
if ($archive->hasBeenCommitted) {
return $archive->get('approvalWorkflowId')
|| $self->session->setting->get('defaultVersionTagWorkflow');
}
return undef;
}
#-------------------------------------------------------------------
=head2 getCrumbTrail ( )
Returns the crumb trail for this Story. If rendered from inside
a Topic, it will insert the Topic information into the crumb trail.
The crumb trail will be a loop of variables, in order from this Story's
StoryArchive, the topic, if present, and then this story.
=cut
sub getCrumbTrail {
my $self = shift;
my $crumb_loop = [];
my $archive = $self->getArchive;
push @{ $crumb_loop }, {
title => $archive->getTitle,
url => $archive->getUrl,
};
my $topic = $self->topic;
if ($topic) {
push @{ $crumb_loop }, {
title => $topic->getTitle,
url => $topic->getUrl,
};
}
push @{ $crumb_loop }, {
title => $self->getTitle,
url => $self->getUrl,
};
return $crumb_loop;
}
#-------------------------------------------------------------------
=head2 getEditForm ( )
Returns a templated form for adding or editing Stories.
=cut
sub getEditForm {
my $self = shift;
my $session = $self->session;
my $i18n = WebGUI::International->new($session, 'Asset_Story');
my $form = $session->form;
my $archive = $self->getArchive;
my $isNew = $self->getId eq 'new';
my $url = $isNew ? $archive->getUrl : $self->getUrl;
my $title = $self->getTitle;
my $var = {
formHeader => WebGUI::Form::formHeader($session, {action => $url})
. WebGUI::Form::hidden($session, { name => 'func', value => 'editSave' })
. WebGUI::Form::hidden($session, { name => 'proceed', value => 'showConfirmation' })
,
formFooter => WebGUI::Form::formFooter($session),
formTitle => $isNew
? $i18n->get('add a story','Asset_StoryArchive')
: $i18n->get('editing','Asset_WikiPage').' '.$title,
headlineForm => WebGUI::Form::text($session, {
name => 'headline',
value => $form->get('headline') || $self->get('headline'),
} ),
titleForm => WebGUI::Form::text($session, {
name => 'title',
value => $form->get('title') || $self->get('title'),
} ),
subtitleForm => WebGUI::Form::textarea($session, {
name => 'subtitle',
value => $form->get('subtitle') || $self->get('subtitle')
} ),
bylineForm => WebGUI::Form::text($session, {
name => 'byline',
value => $form->get('byline') || $self->get('byline')
} ),
locationForm => WebGUI::Form::text($session, {
name => 'location',
value => $form->get('location') || $self->get('location')
} ),
keywordsForm => WebGUI::Form::keywords($session, {
name => 'keywords',
value => $form->get('keywords') || WebGUI::Keyword->new($session)->getKeywordsForAsset({ asset => $self })
} ),
highlightsForm => WebGUI::Form::textarea($session, {
name => 'highlights',
value => $form->get('highlights') || $self->get('highlights')
} ),
storyForm => WebGUI::Form::HTMLArea($session, {
name => 'story',
value => $form->get('story') || $self->get('story'),
richEditId => $archive->get('richEditorId')
}),
saveButton => WebGUI::Form::submit($session, {
name => 'saveStory',
value => $i18n->get('save story'),
}),
cancelButton => WebGUI::Form::button($session, {
name => 'cancel',
value => $i18n->get('cancel','WebGUI'),
extras => q|onclick="history.go(-1);" class="backwardButton"|,
}),
saveAndAddButton => WebGUI::Form::submit($session, {
name => 'saveAndReturn',
value => $i18n->get('save and add another photo'),
}),
};
$var->{ photo_form_loop } = [];
##Provide forms for the existing photos, if any
##Existing photos get a delete Yes/No.
##And a form for new ones
my $photoData = $self->getPhotoData;
my $numberOfPhotos = scalar @{ $photoData };
foreach my $photoIndex (1..$numberOfPhotos) {
my $photo = $photoData->[$photoIndex-1];
push @{ $var->{ photo_form_loop } }, {
imgUploadForm => WebGUI::Form::image($session, {
name => 'photo'.$photoIndex,
maxAttachments => 1,
value => $photo->{storageId},
}),
imgCaptionForm => WebGUI::Form::text($session, {
name => 'imgCaption'.$photoIndex,
value => $photo->{caption},
}),
imgByLineForm => WebGUI::Form::text($session, {
name => 'imgByline'.$photoIndex,
value => $photo->{byLine},
}),
imgAltForm => WebGUI::Form::text($session, {
name => 'imgAlt'.$photoIndex,
value => $photo->{alt},
}),
imgTitleForm => WebGUI::Form::text($session, {
name => 'imgTitle'.$photoIndex,
value => $photo->{title},
}),
imgUrlForm => WebGUI::Form::url($session, {
name => 'imgUrl'.$photoIndex,
value => $photo->{url},
}),
imgDeleteForm => WebGUI::Form::yesNo($session, {
name => 'deletePhoto'.$photoIndex,
value => 0,
}),
};
}
push @{ $var->{ photo_form_loop } }, {
imgUploadForm => WebGUI::Form::image($session, {
name => 'newPhoto',
maxAttachments => 1,
}),
imgCaptionForm => WebGUI::Form::text($session, {
name => 'newImgCaption',
}),
imgByLineForm => WebGUI::Form::text($session, {
name => 'newImgByline',
}),
imgAltForm => WebGUI::Form::text($session, {
name => 'newImgAlt',
}),
imgTitleForm => WebGUI::Form::text($session, {
name => 'newImgTitle',
}),
imgUrlForm => WebGUI::Form::url($session, {
name => 'newImgUrl',
}),
};
if ($isNew) {
$var->{formHeader} .= WebGUI::Form::hidden($session, { name => 'assetId', value => 'new' })
. WebGUI::Form::hidden($session, { name => 'class', value => $form->process('class', 'className') });
}
else {
$var->{formHeader} .= WebGUI::Form::hidden($session, { name => 'url', value => $url});
}
return $self->processTemplate($var, $archive->get('editStoryTemplateId'));
}
#-------------------------------------------------------------------
=head2 getPhotoData ( )
Returns the photo hash formatted as perl data. See also L<setPhotoData>.
=cut
sub getPhotoData {
my $self = shift;
if (!exists $self->{_photoData}) {
my $json = $self->get('photo');
$json ||= '[]';
$self->{_photoData} = from_json($json);
}
return dclone($self->{_photoData});
}
#-------------------------------------------------------------------
=head2 getRssData ( )
Returns RSS data for this Story. The date of the RSS item is the lastModified
property of the Asset.
=cut
sub getRssData {
my $self = shift;
my $data = {
title => $self->get('headline') || $self->getTitle,
description => $self->get('subtitle'),
'link' => $self->getUrl,
author => $self->get('byline'),
date => $self->get('lastModified'),
};
return $data;
}
#-------------------------------------------------------------------
=head2 prepareView ( )
Extent the default method to handle the case when a Story Topic is rendering
this Story.
=cut
sub prepareView {
my $self = shift;
$self->next::method();
my $templateId;
my $topic = $self->topic;
if ($topic) {
$templateId = $topic->get('storyTemplateId');
}
else {
$templateId = $self->getArchive->get('storyTemplateId');
}
my $template = WebGUI::Asset::Template->new($self->session, $templateId);
$template->prepare;
$self->{_viewTemplate} = $template;
}
#-------------------------------------------------------------------
=head2 processPropertiesFromFormPost ( )
Handle photos and photo metadata, like captions, etc.
=cut
sub processPropertiesFromFormPost {
my $self = shift;
my $session = $self->session;
$self->next::method;
my $form = $session->form;
##Handle old data first, to avoid iterating across a newly added photo.
my $photoData = $self->getPhotoData;
my $numberOfPhotos = scalar @{ $photoData };
##Post process photo data here.
PHOTO: foreach my $photoIndex (1..$numberOfPhotos) {
##TODO: Deletion check and storage cleanup
my $storageId = $photoData->[$photoIndex-1]->{storageId};
if ($form->process('deletePhoto'.$photoIndex, 'yesNo')) {
my $storage = WebGUI::Storage->get($session, $storageId);
$storage->delete if $storage;
splice @{ $photoData }, $photoIndex-1, 1;
next PHOTO;
}
my $newPhoto = {
storageId => $form->process('photo' .$photoIndex, 'image', $storageId),
caption => $form->process('imgCaption'.$photoIndex, 'text'),
alt => $form->process('imgAlt' .$photoIndex, 'text'),
title => $form->process('imgTitle' .$photoIndex, 'text'),
byLine => $form->process('imgByline' .$photoIndex, 'text'),
url => $form->process('imgUrl' .$photoIndex, 'url' ),
};
splice @{ $photoData }, $photoIndex-1, 1, $newPhoto;
}
my $newStorage = $form->process('newPhoto', 'image');
if ($newStorage) {
push @{ $photoData }, {
caption => $form->process('newImgCaption', 'text'),
alt => $form->process('newImgAlt', 'text'),
title => $form->process('newImgTitle', 'text'),
byLine => $form->process('newImgByline', 'text'),
url => $form->process('newImgUrl', 'url'),
storageId => $newStorage,
};
}
$self->setPhotoData($photoData);
}
#-------------------------------------------------------------------
=head2 purge ( )
Cleaning up all storage objects in all revisions.
=cut
sub purge {
my $self = shift;
##Delete all storage locations from all revisions of the Asset
my $sth = $self->session->db->read("select photo from Story where assetId=?",[$self->getId]);
STORAGE: while (my ($json) = $sth->array) {
my $photos = from_json($json);
PHOTO: foreach my $photo (@{ $photos }) {
next PHOTO unless $photo->{storageId};
my $storage = WebGUI::Storage->get($self->session,$photo->{storageId});
$storage->delete if $storage;
}
}
$sth->finish;
return $self->next::method;
}
#-------------------------------------------------------------------
=head2 purgeRevision
Remove the storage locations for this revision of the Asset.
=cut
sub purgeRevision {
my $self = shift;
my $session = $self->session;
foreach my $photo ( @{ $self->getPhotoData} ) {
my $storage = WebGUI::Storage->get($session, $self-$photo->{storageId});
$storage->delete if $storage;
}
return $self->next::method;
}
#-------------------------------------------------------------------
=head2 setPhotoData ( $perlStructure )
Update the JSON stored in the object from its perl equivalent, and update the database
as well via update. This deletes the cached copy of the equivalent perl structure.
=head3 $perlStructure
This should be an array of hashes. Photos will be in the order uploaded.
The values in the hash will be metadata about the Photo, and the storageId
that holds the image. Each storageId will hold only 1 file.
=over 4
=item *
caption
=item *
byLine
=item *
alt
=item *
title
=item *
url
=item *
storageId
=back
subhash keys can be empty, or missing altogether. Shoot, you can really put anything you
want in there as there's no valid content checking.
=cut
sub setPhotoData {
my $self = shift;
my $photoData = shift || [];
##Convert to JSON
my $photo = to_json($photoData);
##Update the db.
$self->update({photo => $photo});
delete $self->{_photoData};
return;
}
#-------------------------------------------------------------------
=head2 setSize ( fileSize )
Set the size of this asset by including all the files in its storage
location. C<fileSize> is an integer of additional bytes to include in
the asset size.
=cut
sub setSize {
my $self = shift;
my $fileSize = shift || 0;
my $session = $self->session;
PHOTO: foreach my $photo (@{ $self->getPhotoData }) {
my $storage = WebGUI::Storage->get($session, $photo->{storageId});
next PHOTO unless defined $storage;
foreach my $file (@{$storage->getFiles}) {
$fileSize += $storage->getFileSize($file);
}
}
return $self->next::method($fileSize);
}
#-------------------------------------------------------------------
=head2 topic ( $topicAsset )
Tells the Story that it is being viewed from a Topic, and to behave
accordingly. Returns a StoryTopic asset if set.
=head3 $topicAsset
The topic that is displaying this Story.
=cut
sub topic {
my $self = shift;
my $topic = shift;
if (defined $topic) {
$self->{_topic} = $topic;
}
return $self->{_topic};
}
#-------------------------------------------------------------------
=head2 update
Extend the superclass to make sure that the asset always stays hidden from navigation.
=cut
sub update {
my $self = shift;
my $properties = shift;
return $self->next::method({%$properties, isHidden => 1});
}
#-------------------------------------------------------------------
=head2 validParent
Make sure that the current session asset is a StoryArchive for pasting and adding checks.
This is a class method.
=cut
sub validParent {
my $class = shift;
my $session = shift;
return $session->asset && $session->asset->isa('WebGUI::Asset::Wobject::StoryArchive');
}
#-------------------------------------------------------------------
=head2 view ( )
method called by the container www_view method.
=cut
##Keyword cloud generated by WebGUI::Keyword
sub view {
my $self = shift;
my $session = $self->session;
my $var = $self->viewTemplateVariables();
return $self->processTemplate($var,undef, $self->{_viewTemplate});
}
#-------------------------------------------------------------------
=head2 viewTemplateVars ( $var )
Add template variables to the existing template variables. This includes asset level variables.
=head3 $var
Template variables will be added onto this hash ref.
=cut
sub viewTemplateVariables {
my ($self) = @_;
my $session = $self->session;
my $archive = $self->getArchive;
my $var = $self->get;
if ($var->{highlights}) {
my @highlights = split "\n+", $var->{highlights};
foreach my $highlight (@highlights) {
push @{ $var->{highlights_loop} }, { highlight => $highlight };
}
}
my $key = WebGUI::Keyword->new($session);
my $keywords = $key->getKeywordsForAsset( { asArrayRef => 1, asset => $self });
$var->{keyword_loop} = [];
foreach my $keyword (@{ $keywords }) {
push @{ $var->{keyword_loop} }, {
keyword => $keyword,
url => $archive->getUrl("func=view;keywords=".$session->url->escape($keyword)),
};
}
$var->{updatedTime} = $self->formatDuration();
$var->{updatedTimeEpoch} = $self->get('revisionDate');
$var->{crumb_loop} = $self->getCrumbTrail();
my $photoData = $self->getPhotoData;
$var->{photo_loop} = [];
my $photoCounter = 0;
PHOTO: foreach my $photo (@{ $photoData }) {
next PHOTO unless $photo->{storageId};
my $storage = WebGUI::Storage->get($session, $photo->{storageId});
my $file = $storage->getFiles->[0];
next PHOTO unless $file;
my $imageUrl = $storage->getUrl($file);
push @{ $var->{photo_loop} }, {
imageUrl => $imageUrl,
imageCaption => $photo->{caption},
imageByline => $photo->{byLine},
imageAlt => $photo->{alt},
imageTitle => $photo->{title},
imageLink => $photo->{url},
};
++$photoCounter;
}
$var->{hasPhotos} = $photoCounter;
$var->{singlePhoto} = $photoCounter == 1;
return $var;
}
#-------------------------------------------------------------------
=head2 www_edit ( )
Web facing method which is the default edit page. Unless the method needs
special handling or formatting, it does not need to be included in
the module.
Overridden because the standard, autogenerated form is not used.
=cut
sub www_edit {
my $self = shift;
my $session = $self->session;
return $session->privilege->insufficient() unless $self->canEdit;
return $session->privilege->locked() unless $self->canEditIfLocked;
return $self->getArchive->processStyle($self->getEditForm);
}
#-------------------------------------------------------------------
=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_Story');
return $self->getArchive->processStyle('<p>'.$i18n->get('story received').'</p><p><a href="'.$self->getArchive->getUrl.'">'.$i18n->get('493','WebGUI').'</a></p>');
}
#-------------------------------------------------------------------
=head2 www_view
Override www_view from asset because Stories inherit a style template from
the Story Archive that contains them.
=cut
sub www_view {
my $self = shift;
return $self->session->privilege->noAccess unless $self->canView;
$self->session->http->sendHeader;
$self->prepareView;
return $self->getArchive->processStyle($self->view);
}
1;
#vim:ft=perl

View file

@ -1210,7 +1210,8 @@ sub viewList {
### Build the event vars
my $dtLast = $dtStart; # The DateTime of the last event
for my $event (@events) {
EVENT: for my $event (@events) {
next EVENT unless $event && $event->canView();
my ( %eventVar, %eventDate )
= $self->getEventVars( $event );

View file

@ -437,7 +437,7 @@ sub appendTemplateVarsSearchForm {
$var->{ searchForm_creationDate_after }
= WebGUI::Form::dateTime( $session, {
name => "creationDate_after",
value => $form->get("creationDate_after","dateTime") || $oneYearAgo,
value => $form->get("creationDate_after") || $oneYearAgo,
});
$var->{ searchForm_creationDate_before }
= WebGUI::Form::dateTime( $session, {
@ -1369,8 +1369,9 @@ search and display the results if necessary.
sub www_search {
my $self = shift;
my $form = $self->session->form;
my $db = $self->session->db;
my $session = $self->session;
my $form = $session->form;
my $db = $session->db;
my $var = $self->getTemplateVars;
# NOTE: Search form is added as part of getTemplateVars()
@ -1413,7 +1414,8 @@ sub www_search {
;
}
my $dateAfter = $form->get("creationDate_after", "dateTime");
my $oneYearAgo = WebGUI::DateTime->new( $session, time )->add( years => -1 )->epoch;
my $dateAfter = $form->get("creationDate_after") || $oneYearAgo;
my $dateBefore = $form->get("creationDate_before", "dateTime");
my $creationDate = {};
if ($dateAfter) {
@ -1463,7 +1465,7 @@ sub www_search {
$p->appendTemplateVars( $var );
for my $result ( @{ $p->getPageData } ) {
my $asset = WebGUI::Asset->newByDynamicClass( $self->session, $result->{assetId} );
my $asset = WebGUI::Asset->newByDynamicClass( $session, $result->{assetId} );
push @{ $var->{search_results} }, {
%{ $asset->getTemplateVars },
isAlbum => $asset->isa( 'WebGUI::Asset::Wobject::GalleryAlbum' ),

View file

@ -0,0 +1,605 @@
package WebGUI::Asset::Wobject::StoryArchive;
our $VERSION = "1.0.0";
#-------------------------------------------------------------------
# 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 Tie::IxHash;
use WebGUI::International;
use WebGUI::Utility;
use WebGUI::Asset::Story;
use WebGUI::Asset::Wobject::Folder;
use WebGUI::Paginator;
use WebGUI::Keyword;
use WebGUI::Search;
use Class::C3;
use base qw/WebGUI::AssetAspect::RssFeed WebGUI::Asset::Wobject/;
use File::Path;
use constant DATE_FORMAT => '%c_%D_%y';
#-------------------------------------------------------------------
=head2 addChild ( )
Story Archive really only has Folders for children. When addChild is
called, check the date to see which folder to use. If the correct folder
does not exist, then make it.
=cut
sub addChild {
my $self = shift;
my ($properties) = @_;
##Allow subclassing
return undef unless $properties->{className} =~ /^WebGUI::Asset::Story/;
my $todayFolder = $self->getFolder;
return undef unless $todayFolder;
my $story = $todayFolder->addChild(@_);
return $story;
}
#-------------------------------------------------------------------
=head2 canPostStories ( )
Determines whether or not a user can post stories to this Archive.
=head3 userId
An explicit userId to check against. If no userId is sent, then it
will use the current session user instead.
=cut
sub canPostStories {
my ($self, $userId) = @_;
$userId ||= $self->session->user->userId;
my $user = WebGUI::User->new($self->session, $userId);
return $user->isInGroup($self->get("groupToPost")) || $self->canEdit($userId);
}
#-------------------------------------------------------------------
=head2 definition ( )
defines wobject properties for New Wobject instances. You absolutely need
this method in your new Wobjects. If you choose to "autoGenerateForms", the
getEditForm method is unnecessary/redundant/useless.
=cut
sub definition {
my $class = shift;
my $session = shift;
my $definition = shift;
my $i18n = WebGUI::International->new($session, 'Asset_StoryArchive');
my %properties;
tie %properties, 'Tie::IxHash';
%properties = (
storiesPerPage => {
tab => 'display',
fieldType => 'integer',
label => $i18n->get('stories per page'),
hoverHelp => $i18n->get('stories per page help'),
defaultValue => 25,
},
groupToPost => {
tab => 'security',
fieldType => 'group',
label => $i18n->get('group to post'),
hoverHelp => $i18n->get('group to post help'),
defaultValue => '12',
},
templateId => {
tab => 'display',
fieldType => 'template',
label => $i18n->get('template'),
hoverHelp => $i18n->get('template help'),
namespace => 'StoryArchive',
defaultValue => 'yxD5ka7XHebPLD-LXBwJqw',
},
storyTemplateId => {
tab => 'display',
fieldType => 'template',
label => $i18n->get('story template'),
hoverHelp => $i18n->get('story template help'),
namespace => 'Story',
defaultValue => '3QpYtHrq_jmAk1FNutQM5A',
},
editStoryTemplateId => {
tab => 'display',
fieldType => 'template',
label => $i18n->get('edit story template'),
hoverHelp => $i18n->get('edit story template help'),
namespace => 'Story/Edit',
defaultValue => 'E3tzZjzhmYoNlAyP2VW33Q',
},
keywordListTemplateId => {
tab => 'display',
fieldType => 'template',
label => $i18n->get('keyword list template'),
hoverHelp => $i18n->get('keyword list template help'),
namespace => 'StoryArchive/KeywordList',
defaultValue => '0EAJ9EYb9ap2XwfrcXfdLQ',
},
archiveAfter => {
tab => 'display',
fieldType => 'interval',
label => $i18n->get('archive after'),
hoverHelp => $i18n->get('archive after help'),
defaultValue => 31536000,
},
richEditorId => {
tab => 'display',
fieldType => 'selectRichEditor',
label => $i18n->get('rich editor'),
hoverHelp => $i18n->get('rich editor help'),
defaultValue => 'PBrichedit000000000002',
},
approvalWorkflowId =>{
tab => 'security',
fieldType => 'workflow',
defaultValue => 'pbworkflow000000000003',
type => 'WebGUI::VersionTag',
label => $i18n->get('approval workflow'),
hoverHelp => $i18n->get('approval workflow help'),
},
);
push(@{$definition}, {
assetName=>$i18n->get('assetName'),
icon=>'assets.gif',
autoGenerateForms=>1,
tableName=>'StoryArchive',
className=>'WebGUI::Asset::Wobject::StoryArchive',
properties=>\%properties,
});
return $class->SUPER::definition($session, $definition);
}
#-------------------------------------------------------------------
=head2 exportAssetCollateral (basePath, params, session)
Extended the master method in order to produce keyword files.
=cut
sub exportAssetCollateral {
# Lots of copy/paste here from AssetExportHtml.pm, since none of the methods there were
# directly useful without ginormous refactoring.
my $self = shift;
my $basepath = shift;
my $args = shift;
my $reportSession = shift;
my $session = $self->session;
my $reporti18n = WebGUI::International->new($session, 'Asset');
my $basename = $basepath->basename;
my $filedir;
# We want our keyword files to "appear" as children of the asset to avoid
# clashing with multiple story archives.
if ($basename eq 'index.html') {
# Get the parent of the file index.html, which is the asset's directory.
$filedir = $basepath->parent->absolute->stringify;
}
else {
##Create a directory that has the same base
my $dirname = $basename;
$dirname =~ s/\.\w+$//;
$filedir = $basepath->parent->subdir($dirname)->absolute->stringify;
eval { File::Path::mkpath($filedir) };
if($@) {
WebGUI::Error->throw(error => "could not make directory " . $filedir);
}
}
if ( $reportSession && !$args->{quiet} ) {
$reportSession->output->print('<br />');
}
# open another session to handle printing...
my $printSession = WebGUI::Session->open(
$self->session->config->getWebguiRoot,
$self->session->config->getFilename,
undef,
undef,
$self->session->getId,
);
my $keywordObj = WebGUI::Keyword->new($printSession);
my $keywords = $keywordObj->findKeywords({
asset => $self,
limit => 50, ##This is based on the tagcloud setting
});
my $listTemplate = WebGUI::Asset->new($session, $self->get('keywordListTemplateId'), 'WebGUI::Asset::Template');
foreach my $keyword (@{ $keywords }) {
##Keywords may not be URL safe, so urlize them
my $keyword_url = $self->getKeywordFilename($keyword);
my $dest = Path::Class::File->new($filedir, $keyword_url);
# tell the user which asset we're exporting.
if ( $reportSession && !$args->{quiet} ) {
my $message = sprintf $reporti18n->get('exporting page'), $dest->absolute->stringify;
$reportSession->output->print(
'&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' . $message . '<br />');
}
# next, get the contents, open the file, and write the contents to the file.
my $fh = eval { $dest->open('>:utf8') };
if($@) {
$printSession->close;
WebGUI::Error->throw(error => "can't open " . $dest->absolute->stringify . " for writing: $!");
}
$printSession->output->setHandle($fh);
my $storyIds = $keywordObj->getMatchingAssets({
startAsset => $self,
keyword => $keyword,
isa => 'WebGUI::Asset::Story',
rowsPerPage => 50,
});
my $listOfStories = [];
STORYID: foreach my $storyId (@{ $storyIds }) {
my $story = WebGUI::Asset->newByDynamicClass($session, $storyId);
next STORYID unless $story;
push @{ $listOfStories }, {
title => $story->getTitle,
url => $story->getUrl,
};
}
my $var = {
asset_loop => $listOfStories,
keyword => $keyword,
};
my $output = $listTemplate->process($var);
my $contents = $self->processStyle($output);
$printSession->output->print($contents);
# tell the user we did this asset collateral correctly
if ( $reportSession && !$args->{quiet} ) {
$reportSession->output->print($reporti18n->get('done'));
}
$fh->flush;
$fh->close;
}
$printSession->close;
return $self->next::method($basepath, $args, $reportSession);
}
#-------------------------------------------------------------------
=head2 getFolder ( date )
Stories are stored in Folders under the Story Archive to prevent lineage issues.
Gets the correct folder for stories. If the Folder does not exist, then it will
be created and autocommitted. The autocommit is COMPLETELY automatic. This is
because it's possible to gum up the Story submitting proces with a Folder under
a different version tag.
=head3 date
There is one folder for each day that Stories are submitted. The requested date
should be an epoch. If no date is passed, it will use the current time instead.
=cut
sub getFolder {
my ($self, $date) = @_;
my $session = $self->session;
my $folderName = $session->datetime->epochToHuman($date, DATE_FORMAT);
my $folderUrl = join '/', $self->getUrl, $folderName;
my $folder = WebGUI::Asset->newByUrl($session, $folderUrl);
return $folder if $folder;
##The requested folder doesn't exist. Make it and autocommit it.
##For a fully automatic commit, save the current tag, create a new one
##with the commit without approval workflow, commit it, then restore
##the original if it exists
my $oldVersionTag = WebGUI::VersionTag->getWorking($session, 'noCreate');
my $newVersionTag = WebGUI::VersionTag->create($session, { workflowId => 'pbworkflow00000000003', });
$newVersionTag->setWorking;
##Call SUPER because my addChild calls getFolder
$folder = $self->SUPER::addChild({
className => 'WebGUI::Asset::Wobject::Folder',
title => $folderName,
menuTitle => $folderName,
url => $folderUrl,
isHidden => 1,
});
$newVersionTag->commit();
##Restore the old one, if it exists
$oldVersionTag->setWorking() if $oldVersionTag;
##Get a new version of the asset from the db with the correct state
$folder = WebGUI::Asset->newByUrl($session, $folderUrl);
return $folder;
}
#-------------------------------------------------------------------
=head2 getKeywordFilename ( $keyword )
Returns the name for the file containing stories that match this keyword. Used
in exportAssetCollateral, and in viewTemplateVariables.
=head3 $keyword
The keyword to generate a URL for.
=cut
sub getKeywordFilename {
my ($self,$keyword) = @_;
return $self->session->url->urlize('keyword_'.$keyword.'.html');
}
#-------------------------------------------------------------------
=head2 getKeywordStaticURL ( $keyword )
Returns the whole URL for the file containing stories that match this keyword. Used
in exportAssetCollateral.
The goal of this method is to create a "safe" URL where all the keyword files can
reside with no clashes. The best place is based on the URL for the StoryArchive.
=head3 $keyword
Generates a specific URL for $keyword.
=cut
sub getKeywordStaticURL {
my ($self,$keyword) = @_;
my $url = $self->getUrl;
my @parts = split /\//, $url;
my $lastPart = pop @parts;
if (index $lastPart, '.' == -1) {
return join '/', $self->getUrl, $self->getKeywordFilename($keyword);
}
else {
$lastPart =~ s/\.[^.]*$//;
return join '/', @parts, $lastPart, $self->getKeywordFilename($keyword);
}
}
#-------------------------------------------------------------------
=head2 getRssFeedItems ( )
Returns an arrayref of hashrefs, containing information on stories
for generating an RSS and Atom feeds.
=cut
sub getRssFeedItems {
my $self = shift;
my $stories = $self->getLineageIterator(['descendants'],{
excludeClasses => ['WebGUI::Asset::Wobject::Folder'],
orderByClause => 'creationDate desc, lineage',
returnObjects => 1,
limit => $self->get('itemsPerFeed'),
});
my $storyData = [];
while (my $story = $stories->()) {
push @{ $storyData }, $story->getRssData;
}
return $storyData;
}
#-------------------------------------------------------------------
=head2 prepareView ( )
See WebGUI::Asset::prepareView() for details.
=cut
sub prepareView {
my $self = shift;
$self->SUPER::prepareView();
my $template = WebGUI::Asset::Template->new($self->session, $self->get("templateId"));
$template->prepare;
$self->{_viewTemplate} = $template;
}
#-------------------------------------------------------------------
=head2 view ( )
method called by the www_view method. Returns a processed template
to be displayed within the page style.
=cut
sub view {
my $self = shift;
my $session = $self->session;
#This automatically creates template variables for all of your wobject's properties.
my $mode = $session->form->hasParam('keyword')
? 'keyword'
: $session->form->hasParam('search')
? 'search'
: 'view';
my $var = $self->viewTemplateVariables($mode);
return $self->processTemplate($var, undef, $self->{_viewTemplate});
}
#-------------------------------------------------------------------
=head2 viewTemplateVars ( $mode )
Make template variables for the view template.
=head3 $mode
Whether to get assets in view mode, by time, or search mode, by keywords.
If the asset is being exported for HTML, the following changes are mode:
=over 4
=item *
The search form template variables are not generated.
=item *
The pagination variables are not generated.
=item *
The pagination size is set to 10 standard pages.
=back
=cut
sub viewTemplateVariables {
my ($self, $mode) = @_;
my $session = $self->session;
my $keywords = $session->form->get('keyword');
my $query = $session->form->get('query');
my $exporting = $session->scratch->get('isExporting');
my $p;
my $var = $self->get();
if ($mode eq 'keyword') {
$var->{mode} = 'keyword';
my $wordList = WebGUI::Keyword::string2list($keywords);
my $key = WebGUI::Keyword->new($session);
$p = $key->getMatchingAssets({
startAsset => $self,
keywords => $wordList,
isa => 'WebGUI::Asset::Story',
usePaginator => 1,
rowsPerPage => $self->get('storiesPerPage'),
});
}
elsif ($mode eq 'search') {
$var->{mode} = 'search';
my $search = WebGUI::Search->new($session);
$search->search({
keywords => $query,
lineage => [ $self->get('lineage'), ],
classes => [ qw/WebGUI::Asset::Story/, ],
});
$p = $search->getPaginatorResultSet($self->getUrl, $self->get('storiesPerPage'));
}
else {
$var->{mode} = 'view';
##Only return assetIds, we'll build data for the things that are actually displayed.
my $storySql = $self->getLineageSql(['descendants'],{
excludeClasses => ['WebGUI::Asset::Wobject::Folder'],
orderByClause => 'creationDate desc, lineage',
});
my $storiesPerPage = $self->get('storiesPerPage');
if ($exporting) {
##10 pages worth of data on 1 page in export mode
$storiesPerPage *= 10;
}
$p = WebGUI::Paginator->new($session, $self->getUrl, $storiesPerPage);
$p->setDataByQuery($storySql);
}
my $storyIds = $p->getPageData();
if (! $exporting ) {
##Pagination variables aren't useful in export mode
$p->appendTemplateVars($var);
}
$var->{date_loop} = [];
my $lastStoryDate = '';
my $datePointer = undef;
##Only build objects for the assets that we need
STORY: foreach my $storyId (@{ $storyIds }) {
my $story = WebGUI::Asset->new($session, $storyId->{assetId}, $storyId->{className}, $storyId->{revisionDate});
next STORY unless $story;
my $creationDate = $story->get('creationDate');
my ($creationDay,undef) = $session->datetime->dayStartEnd($creationDate);
my $storyDate = $session->datetime->epochToHuman($creationDay, DATE_FORMAT);
if ($storyDate ne $lastStoryDate) {
push @{ $var->{date_loop} }, {};
$datePointer = $var->{date_loop}->[-1];
$datePointer->{epochDate} = $creationDay;
$datePointer->{story_loop} = [];
$lastStoryDate = $storyDate;
}
push @{$datePointer->{story_loop}}, {
url => $story->getUrl,
title => $story->getTitle,
creationDate => $creationDate,
}
}
$var->{canPostStories} = $self->canPostStories;
$var->{addStoryUrl} = $var->{canPostStories}
? $self->getUrl('func=add;class=WebGUI::Asset::Story')
: '';
$var->{rssUrl} = $exporting ? $self->getStaticRssFeedUrl : $self->getRssFeedUrl;
$var->{atomUrl} = $exporting ? $self->getStaticAtomFeedUrl : $self->getAtomFeedUrl;
my $cloudOptions = {
startAsset => $self,
displayFunc => 'view',
};
##In export mode, tags should link to the pages generated during the collateral export
if($exporting) {
$cloudOptions->{urlCallback} = 'getKeywordStaticURL';
$cloudOptions->{displayFunc} = '';
}
$var->{keywordCloud} = WebGUI::Keyword->new($session)->generateCloud($cloudOptions);
if (! $exporting) {
my $i18n = WebGUI::International->new($session, 'Asset');
$var->{searchHeader} = WebGUI::Form::formHeader($session, { action => $self->getUrl })
. WebGUI::Form::hidden($session, { name => 'func', value => 'view' });
$var->{searchFooter} = WebGUI::Form::formFooter($session);
$var->{searchButton} = WebGUI::Form::submit($session, { name => 'search', value => $i18n->get('search')});
$var->{searchForm} = WebGUI::Form::text($session, { name => 'query', value => $query});
}
return $var;
}
#-------------------------------------------------------------------
=head2 www_add ( )
The only real children of StoryArchive are Folders, which then hold Stories. So we intercept
www_add, find the right folder to use, then allow that folder to continue on.
=cut
sub www_add {
my $self = shift;
my $session = $self->session;
my $form = $session->form;
if ($form->get('class') ne 'WebGUI::Asset::Story') {
$session->log->warn('Refusing to add '. $form->get('class'). ' to StoryArchive');
return undef;
}
my $todayFolder = $self->getFolder;
if (!$todayFolder) {
$session->log->warn('Unable to get folder for today. Not adding Story');
return undef;
}
$todayFolder->www_add;
}
1;
#vim:ft=perl

View file

@ -0,0 +1,267 @@
package WebGUI::Asset::Wobject::StoryTopic;
$VERSION = "1.0.0";
#-------------------------------------------------------------------
# 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 Tie::IxHash;
use WebGUI::International;
use WebGUI::Utility;
use WebGUI::Asset::Story;
use Class::C3;
use base qw/WebGUI::AssetAspect::RssFeed WebGUI::Asset::Wobject/;
use constant DATE_FORMAT => '%c_%D_%y';
#-------------------------------------------------------------------
=head2 definition ( )
defines wobject properties for New Wobject instances. You absolutely need
this method in your new Wobjects. If you choose to "autoGenerateForms", the
getEditForm method is unnecessary/redundant/useless.
=cut
sub definition {
my $class = shift;
my $session = shift;
my $definition = shift;
my $i18n = WebGUI::International->new($session, 'Asset_StoryTopic');
my %properties;
tie %properties, 'Tie::IxHash';
%properties = (
storiesPer => {
tab => 'display',
fieldType => 'integer',
label => $i18n->get('stories per topic'),
hoverHelp => $i18n->get('stories per topic help'),
defaultValue => 15,
},
storiesShort => {
tab => 'display',
fieldType => 'integer',
label => $i18n->get('stories short'),
hoverHelp => $i18n->get('stories short help'),
defaultValue => 5,
},
templateId => {
tab => 'display',
fieldType => 'template',
label => $i18n->get('template'),
hoverHelp => $i18n->get('template help'),
filter => 'fixId',
namespace => 'StoryTopic',
defaultValue => 'A16v-YjWAShXWvSACsraeg',
},
storyTemplateId => {
tab => 'display',
fieldType => 'template',
label => $i18n->get('story template'),
hoverHelp => $i18n->get('story template help'),
filter => 'fixId',
namespace => 'Story',
defaultValue => 'TbDcVLbbznPi0I0rxQf2CQ',
},
);
push(@{$definition}, {
assetName=>$i18n->get('assetName'),
icon=>'assets.gif',
autoGenerateForms=>1,
tableName=>'StoryTopic',
className=>'WebGUI::Asset::Wobject::StoryTopic',
properties=>\%properties,
});
return $class->SUPER::definition($session, $definition);
}
#-------------------------------------------------------------------
=head2 getRssFeedItems ( )
Returns an arrayref of hashrefs, containing information on stories
for generating an RSS and Atom feeds.
=cut
sub getRssFeedItems {
my ($self) = @_;
my $session = $self->session;
my $wordList = WebGUI::Keyword::string2list($self->get('keywords'));
my $key = WebGUI::Keyword->new($session);
my $storyIds = $key->getMatchingAssets({
keywords => $wordList,
isa => 'WebGUI::Asset::Story',
rowsPerPage => $self->get('storiesPer'),
});
my $storyData = [];
STORY: foreach my $storyId (@{ $storyIds }) {
my $story = WebGUI::Asset->newByDynamicClass($session, $storyId);
next STORY unless $story;
push @{ $storyData }, $story->getRssData;
}
return $storyData;
}
#-------------------------------------------------------------------
=head2 prepareView ( )
See WebGUI::Asset::prepareView() for details.
=cut
sub prepareView {
my $self = shift;
$self->SUPER::prepareView();
my $template = WebGUI::Asset::Template->new($self->session, $self->get("templateId"));
$template->prepare;
$self->{_viewTemplate} = $template;
}
#-------------------------------------------------------------------
=head2 view ( )
Method called by the www_view method. Returns a processed template
to be displayed within the page style.
=cut
sub view {
my $self = shift;
my $session = $self->session;
#This automatically creates template variables for all of your wobject's properties.
my $var = $self->viewTemplateVariables;
return $self->processTemplate($var, undef, $self->{_viewTemplate});
}
#-------------------------------------------------------------------
=head2 viewTemplateVars ( )
Make template variables for the view template.
=cut
sub viewTemplateVariables {
my ($self) = @_;
my $session = $self->session;
my $exporting = $session->scratch->get('isExporting');
my $numberOfStories = $self->{_standAlone}
? $self->get('storiesPer')
: $self->get('storiesShort');
my $var = $self->get();
my $wordList = WebGUI::Keyword::string2list($self->get('keywords'));
my $key = WebGUI::Keyword->new($session);
my $p = $key->getMatchingAssets({
keywords => $wordList,
isa => 'WebGUI::Asset::Story',
usePaginator => 1,
rowsPerPage => $numberOfStories,
});
my $storyIds = $p->getPageData();
$var->{story_loop} = [];
##Only build objects for the assets that we need
STORY: foreach my $storyId (@{ $storyIds }) {
my $story = WebGUI::Asset->new($session, $storyId->{assetId}, $storyId->{className}, $storyId->{revisionDate});
next STORY unless $story;
push @{$var->{story_loop}}, {
url => ( $exporting
? $story->getUrl
: $session->url->append($self->getUrl, 'func=viewStory;assetId='.$storyId->{assetId}) ),
title => $story->getTitle,
creationDate => $story->get('creationDate'),
}
}
if ($self->{_standAlone}) {
my $topStoryData = $storyIds->[0];
shift @{ $var->{story_loop} };
##Note, this could have saved from the loop above, but this looks more clean and encapsulated to me.
my $topStory = WebGUI::Asset->new($session, $topStoryData->{assetId}, $topStoryData->{className}, $topStoryData->{revisionDate});
$var->{topStoryTitle} = $topStory->getTitle;
$var->{topStorySubtitle} = $topStory->get('subtitle');
$var->{topStoryUrl} = $session->url->append($self->getUrl, 'func=viewStory;assetId='.$topStoryData->{assetId}),
$var->{topStoryCreationDate} = $topStory->get('creationDate');
##TODO: Photo variables
my $photoData = $topStory->getPhotoData;
PHOTO: foreach my $photo (@{ $photoData }) {
next PHOTO unless $photo->{storageId};
my $storage = WebGUI::Storage->get($session, $photo->{storageId});
my $file = $storage->getFiles->[0];
next PHOTO unless $file;
my $imageUrl = $storage->getUrl($file);
$var->{topStoryImageUrl} = $imageUrl;
$var->{topStoryImageCaption} = $photo->{caption};
$var->{topStoryImageByline} = $photo->{byLine};
$var->{topStoryImageAlt} = $photo->{alt};
$var->{topStoryImageTitle} = $photo->{title};
$var->{topStoryImageLink} = $photo->{url};
last PHOTO;
}
}
$var->{standAlone} = $self->{_standAlone};
$var->{rssUrl} = $exporting ? $self->getStaticRssFeedUrl : $self->getRssFeedUrl;
$var->{atomUrl} = $exporting ? $self->getStaticAtomFeedUrl : $self->getAtomFeedUrl;
return $var;
}
#-------------------------------------------------------------------
=head2 www_view ( )
Overside the method inherited from Wobject to set the mode so template
variables are set correctly in viewTemplateVars.
=cut
sub www_view {
my $self = shift;
$self->{_standAlone} = 1;
return $self->SUPER::www_view;
}
#-------------------------------------------------------------------
=head2 www_viewStory ( )
Display a story, set in the form variable assetId
=cut
sub www_viewStory {
my $self = shift;
my $session = $self->session;
my $storyId = $session->form->get('assetId');
my $story;
if ($storyId) {
$story = WebGUI::Asset->new($session, $storyId);
}
if (! $story) {
my $notFound = WebGUI::Asset->getNotFound($session);
$session->asset($notFound);
return $notFound->www_view;
}
$story->topic($self);
return $story->www_view;
}
1;
#vim:ft=perl

View file

@ -1240,6 +1240,7 @@ sub www_loadQuestions {
if(! $self->session->form->param('shownsummary')){
my ($summary,$html) = $self->getSummary();
my $json = to_json( { type => 'summary', summary => $summary, html => $html });
$self->session->http->setMimeType('application/json');
return $json;
}
}
@ -1328,6 +1329,7 @@ sub surveyEnd {
#$self->session->http->setRedirect($url);
#$self->session->http->setMimeType('application/json');
my $json = to_json( { type => 'forward', url => $url } );
$self->session->http->setMimeType('application/json');
return $json;
}

View file

@ -207,7 +207,8 @@ sub exportAssetCollateral {
# Get the parent dir's *path* (essentially the name of the dir) relative to
# its own parent dir.
$filenameBase = $basepath->parent->relative( $basepath->parent->parent )->stringify;
} else {
}
else {
# Get the 1st ancestor, since the asset is a file recognized by apache, so
# we want our files in the same dir.
$filedir = $basepath->parent->absolute->stringify;
@ -405,11 +406,19 @@ sub getStaticRssFeedUrl {
#-------------------------------------------------------------------
=head2 getFeed ()
=head2 getFeed ( $feed )
Adds the syndicated items to the feed; returns the stringified edition.
Returns this feed so that XML::FeedPP methods can be chained on it.
TODO: convert dates?
=head3 $feed
An XML::FeedPP sub-object, XML::FeedPP::{Atom,Rss,Rdf} that will be filled
with data from the Asset via the getRssFeedItems method.
=cut
sub getFeed {
@ -421,7 +430,8 @@ sub getFeed {
if (!$new_item->guid) {
if ($new_item->link) {
$new_item->guid( $new_item->link );
} else {
}
else {
$new_item->guid( $self->session->id->generate );
$set_permalink_false = 1;
}

View file

@ -128,7 +128,24 @@ sub exportCheckPath {
Main logic hub for export functionality. This method calls most of the rest of
the methods that handle exporting. Any exceptions thrown by the called methods
are returned as strings to the caller. Returns a status description upon
completion. Takes a hashref of arguments, containing the following keys:
completion.
Internally, it sets two scratch variables in private sessions that it creates
for exporting.
=over 4
=item exportMode
If this scratch variable exists, and is true, then the Asset is being exported.
=item exportUrl
This scratch variable is used by the Widget Macro.
=back
Takes a hashref of arguments, containing the following keys:
=head3 quiet
@ -173,6 +190,7 @@ false will do nothing.
sub exportAsHtml {
my $self = shift;
my $session = $self->session;
# set a scratch variable for Assets to know we're exporting
my ($returnCode, $message);
# get the i18n object
@ -287,8 +305,9 @@ sub exportAsHtml {
my $exportSession = WebGUI::Session->open($self->session->config->getWebguiRoot, $self->session->config->getFilename);
$exportSession->user( { userId => $userId } );
# set a scratch variable for widgets to know we're exporting
$exportSession->scratch->set('exportUrl', $exportUrl);
# set a scratch variable for Assets and widgets to know we're exporting
$exportSession->scratch->set('isExporting', 1);
$exportSession->scratch->set('exportUrl', $exportUrl);
my $asset = WebGUI::Asset->newByDynamicClass($exportSession, $assetId);
my $fullPath = $asset->exportGetUrlAsPath;
@ -519,6 +538,8 @@ Translates a URL into an appropriate path and filename for exporting. For
example, given C<'/foo/bar/baz'>, will return C<'/foo/bar/baz/index.html'>
provided the value of indexFile as given to exportAsHtml was C<'index.html'>.
Returns a Path::Class::File object.
=head3 url
URL of the asset we need an export path for

View file

@ -227,7 +227,7 @@ my $text = "";
my $inside = {};
sub html2text {
my $html = shift;
my $html = shift() . " ";
$text = "";
$inside = {};
my $tagHandler = sub {

View file

@ -30,6 +30,8 @@ our $HELP = {
{ name => 'isPrototype', },
{ name => 'status', },
{ name => 'assetSize', },
{ name => 'keywords',
description => 'keywords template var' },
],
fields => [],
related => []

View file

@ -0,0 +1,166 @@
package WebGUI::Help::Asset_Story;
use strict;
our $HELP = {
'edit template' => {
title => 'edit template',
body => '',
isa => [
{ namespace => 'Asset_Template',
tag => 'template variables'
},
],
fields => [],
variables => [
{ name => 'formHeader',
required => 1 },
{ name => 'formTitle', },
{ name => 'formFooter',
required => 1 },
{ name => 'titleForm', },
{ name => 'headlineForm', },
{ name => 'subtitleForm', },
{ name => 'bylineForm', },
{ name => 'locationForm', },
{ name => 'keywordsForm', },
{ name => 'summaryForm', },
{ name => 'highlightsForm', },
{ name => 'storyForm', },
{ name => 'saveButton', },
{ name => 'saveAndAddButton', },
{ name => 'cancelButton', },
{ name => 'photo_form_loop',
variables => [
{ name => 'imgUploadForm', },
{ name => 'imgCaptionForm', },
{ name => 'imgBylineForm', },
{ name => 'imgAltForm', },
{ name => 'imgTitleForm', },
{ name => 'imgUrlForm', },
{ name => 'imgDeleteForm', },
],
},
],
related => []
},
'view template' => {
title => 'view template',
body => '',
isa => [
{ namespace => 'Asset_Template',
tag => 'template variables'
},
],
fields => [],
variables => [
{ name => 'highlights_loop',
'variables' => [
{ name => 'highlight', },
],
},
{ name => 'keywords_loop',
'variables' => [
{ name => 'keyword', },
{ name => 'url',
description => 'keyword_url'
},
],
},
{ name => 'updatedTime', },
{ name => 'updatedTimeEpoch', },
{ name => 'crumb_loop',
'variables' => [
{ name => 'title',
description => 'crumb_title'
},
{ name => 'url',
description => 'crumb_url'
},
],
},
{ name => 'hasPhotos', },
{ name => 'singlePhoto', },
{ name => 'photo_loop',
'variables' => [
{ name => 'imageUrl', },
{ name => 'imageCaption', },
{ name => 'imageByline', },
{ name => 'imageAlt', },
{ name => 'imageTitle', },
{ name => 'imageLink', },
],
},
],
related => []
},
'story asset template variables' => {
private => 1,
title => 'story asset template variables title',
body => '',
isa => [
{ namespace => 'Asset',
tag => 'asset template asset variables'
},
],
fields => [],
variables => [
{ name => 'headline',
description => 'headline tmplvar',
},
{ name => 'subtitle',
description => 'subtitle tmplvar',
},
{ name => 'byline',
description => 'byline tmplvar',
},
{ name => 'updatedTime', },
{ name => 'updatedTimeEpoch', },
],
related => []
},
'story asset template variables' => {
private => 1,
title => 'story asset template variables title',
body => '',
isa => [
{ namespace => 'Asset',
tag => 'asset template asset variables'
},
],
fields => [],
variables => [
{ name => 'headline',
description => 'headline tmplvar',
},
{ name => 'subtitle',
description => 'subtitle tmplvar',
},
{ name => 'byline',
description => 'byline tmplvar',
},
{ name => 'location',
description => 'location tmplvar',
},
{ name => 'highlights',
description => 'highlights tmplvar',
},
{ name => 'story',
description => 'story tmplvar',
},
{ name => 'photo',
description => 'photo tmplvar',
},
{ name => 'storageId',
description => 'storageId tmplvar',
},
],
related => []
},
};
1;

View file

@ -0,0 +1,102 @@
package WebGUI::Help::Asset_StoryArchive;
use strict;
our $HELP = {
'view template' => {
title => 'view template',
body => '',
isa => [
{ namespace => "Asset_StoryArchive",
tag => "storyarchive asset template variables"
},
{ namespace => "Asset_Template",
tag => "template variables"
},
{ namespace => "Asset",
tag => "asset template"
},
{ tag => 'pagination template variables',
namespace => 'WebGUI'
},
],
fields => [],
variables => [
{ 'name' => 'date_loop',
'variables' => [
{ 'name' => 'epochDate' },
{ 'name' => 'story_loop',
'variables' => [
{ 'name' => 'url' },
{ 'name' => 'title' },
{ 'name' => 'creationDate' },
],
},
]
},
{ 'name' => 'searchHeader' },
{ 'name' => 'searchForm' },
{ 'name' => 'searchButton' },
{ 'name' => 'searchFooter' },
{ 'name' => 'canPostStories' },
{ 'name' => 'addStoryUrl' },
{ 'name' => 'rssUrl' },
{ 'name' => 'atomUrl' },
{ 'name' => 'keywordCloud' },
],
related => []
},
'storyarchive asset template variables' => {
private => 1,
title => 'storyarchive asset template variables title',
body => '',
isa => [
{ namespace => "Asset_Wobject",
tag => "wobject template variables"
},
],
fields => [],
variables => [
{ 'name' => 'storesPerPage',
'description' => 'stories per page help',
},
{ 'name' => 'groupToPost', },
{ 'name' => 'templateId', },
{ 'name' => 'storyTemplateId', },
{ 'name' => 'editStoryTemplateId', },
{ 'name' => 'keywordListTemplateId', },
{ 'name' => 'archiveAfter', },
{ 'name' => 'richEditorId', },
{ 'name' => 'approvalWorkflowId', },
],
related => []
},
'keyword list template' => {
title => 'view template',
body => '',
isa => [
{ namespace => "Asset_Template",
tag => "template variables"
},
],
fields => [],
variables => [
{ 'name' => 'asset_loop',
'variables' => [
{ 'name' => 'title',
description => 'asset title' },
{ 'name' => 'url',
description => 'asset url' },
]
},
{ 'name' => 'keyword' },
],
related => []
},
};
1;

View file

@ -0,0 +1,64 @@
package WebGUI::Help::Asset_StoryTopic;
use strict;
our $HELP = {
'view template' => {
title => 'view template',
body => '',
isa => [
{ namespace => "Asset_StoryTopic",
tag => "storytopic asset template variables"
},
{ namespace => "Asset_Template",
tag => "template variables"
},
{ namespace => "Asset",
tag => "asset template"
},
],
fields => [],
variables => [
{ name => 'standAlone' },
{ name => 'story_loop',
variables => [
{ name => 'url' },
{ name => 'title' },
{ name => 'creationDate' },
],
},
{ name => 'topStoryTitle' },
{ name => 'topStorySubtitle' },
{ name => 'topStoryUrl' },
{ name => 'topStoryCreationDate' },
{ name => 'topStoryImageUrl' },
{ name => 'topStoryImageCaption' },
{ name => 'topStoryImageByline' },
{ name => 'topStoryImageAlt' },
{ name => 'topStoryImageTitle' },
{ name => 'topStoryImageLink' },
{ name => 'rssUrl' },
{ name => 'atomUrl' },
],
related => []
},
'storytopic asset template variables' => {
private => 1,
title => 'storytopic asset template variables title',
body => '',
isa => [
{ namespace => "Asset_Wobject",
tag => "wobject template variables"
},
],
fields => [],
variables => [
],
related => []
},
};
1;

View file

@ -256,7 +256,7 @@ sub getKeywordsForAsset {
=head2 getMatchingAssets ( { startAsset => $asset, keyword => $keyword } )
Returns an array reference of asset ids matching the params.
Returns an array reference of asset ids matching the params. Assets are returned in order of creationDate.
=head3 startAsset
@ -283,6 +283,11 @@ A classname pattern to match. For example, if you provide 'WebGUI::Asset::Sku' t
Instead of returning an array reference of assetId's, return a paginator object.
=head3 rowsPerPage
If usePaginator is passed, then this variable will set the number of rows per page that the paginator uses.
If usePaginator is not passed, then this variable will limit the number of assetIds that are returned.
=cut
sub getMatchingAssets {
@ -331,14 +336,17 @@ sub getMatchingAssets {
# write the query
my $query = 'select distinct assetKeyword.assetId from assetKeyword left join asset using (assetId)
where '.join(' and ', @clauses).' order by creationDate desc';
where '.join(' and ', @clauses).' order by creationDate desc, lineage';
# perform the search
if ($options->{usePaginator}) {
my $p = WebGUI::Paginator->new($self->session);
my $p = WebGUI::Paginator->new($self->session, undef, $options->{rowsPerPage});
$p->setDataByQuery($query, undef, undef, \@params);
return $p;
}
elsif ($options->{rowsPerPage}) {
$query .= ' limit '. $options->{rowsPerPage};
}
return $self->session->db->buildArrayRef($query, \@params);
}

View file

@ -294,11 +294,13 @@ sub create {
my $id = $headers->{messageId} || "WebGUI-" . $session->id->generate;
if ($id !~ m/\@/) {
my $domain = $from;
$domain =~ s/.*\@//msx;
$domain =~ s/^.*\@//msx;
$domain =~ s/>$//msx;
$id .= '@' . $domain;
}
if ($id !~ m/[<>]/msx) {
$id = "<$id>";
if ($id !~ m/^<.+?>$/msx) {
$id =~ s/(^<)|(>$)//msxg;
$id = "<".$id.">";
}
my $message = MIME::Entity->build(
Type=>$type,

View file

@ -713,7 +713,7 @@ sub setDataByQuery {
my $sth;
if ($unconditional) {
$sth = $dbh->unconditionalRead($sql,$placeholders);
return $sth->errorMessage if ($sth->errorCode > 0);
return $sth->errorMessage if (defined $sth->errorCode);
} else {
$sth = $dbh->read($sql,$placeholders);
}

View file

@ -828,8 +828,19 @@ sub set {
}
$properties->{fieldName} = $self->getId;
##Save the fieldType now. It can't be chacked against getFormControlClass now
##because it will return the OLD formControlClass, not the new one that we need
##to check against.
my $originalFieldType = $self->get('fieldType');
# Update the record
$db->setRow("userProfileField","fieldName",$properties);
foreach my $key (keys %{$properties}) {
$self->{_properties}{$key} = $properties->{$key};
}
# If the fieldType has changed, modify the userProfileData column
if ($properties->{fieldType} ne $self->get("fieldType")) {
if ($properties->{fieldType} ne $originalFieldType) {
# Create a copy of the new properties so we don't mess them up
my $fieldClass = $self->getFormControlClass;
eval "use $fieldClass;";
@ -845,11 +856,6 @@ sub set {
$db->write($sql);
}
# Update the record
$db->setRow("userProfileField","fieldName",$properties);
foreach my $key (keys %{$properties}) {
$self->{_properties}{$key} = $properties->{$key};
}
}
#-------------------------------------------------------------------

View file

@ -288,7 +288,7 @@ sub addFileFromFormPost {
my $filename;
my $attachmentCount = 1;
foreach my $upload ($session->request->upload($formVariableName)) {
$session->errorHandler->info("Trying to get " . $upload->filename);
$session->errorHandler->info("Trying to get " . $upload->filename." from ".$formVariableName);
return $filename
if $attachmentCount > $attachmentLimit;
my $clientFilename = $upload->filename;

View file

@ -0,0 +1,104 @@
package WebGUI::Workflow::Activity::ArchiveOldStories;
=head1 LEGAL
-------------------------------------------------------------------
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
-------------------------------------------------------------------
=cut
use strict;
use base 'WebGUI::Workflow::Activity';
use WebGUI::Asset;
use WebGUI::Asset::Wobject::StoryArchive;
=head1 NAME
Package WebGUI::Workflow::Activity::ArchiveOldStories
=head1 DESCRIPTION
Uses the settings in the Story Archive to determine whether the Stories (and Folders) in those Story Archives should be archived.
=head1 SYNOPSIS
See WebGUI::Workflow::Activity for details on how to use any activity.
=head1 METHODS
These methods are available from this class:
=cut
#-------------------------------------------------------------------
=head2 definition ( session, definition )
See WebGUI::Workflow::Activity::defintion() for details.
=cut
sub definition {
my $class = shift;
my $session = shift;
my $definition = shift;
my $i18n = WebGUI::International->new($session, "Workflow_Activity_ArchiveOldStories");
push(@{$definition}, {
name=>$i18n->get("activityName"),
properties=> {}
});
return $class->SUPER::definition($session,$definition);
}
#-------------------------------------------------------------------
=head2 execute ( )
See WebGUI::Workflow::Activity::execute() for details.
=cut
sub execute {
my $self = shift;
my $session = $self->session;
my $epoch = $self->session->datetime->time();
my $getAnArchive = WebGUI::Asset::Wobject::StoryArchive->getIsa($session);
ARCHIVE: while (my $archive = $getAnArchive->()) {
next ARCHIVE unless $archive && $archive->get("archiveAfter");
my $archiveDate = $epoch - $archive->get("archiveAfter");
my $folders = $archive->getLineage(
['children'],
{
statusToInclude => ['approved'],
whereClause => 'creationDate < '.$session->db->quote($archiveDate),
returnObjects => 1,
},
);
FOLDER: foreach my $folder (@{ $folders }) {
next FOLDER unless $folder;
my $stories = $folder->getLineage(
['children'], { returnObjects => 1, },
);
STORY: foreach my $story (@{ $stories }) {
next STORY unless $story;
$story->update({ status => 'archived' });
}
$folder->update({ status => 'archived' });
}
}
return $self->COMPLETE;
}
1;

View file

@ -26,6 +26,12 @@ our $I18N = {
context => q|help for the keywords property|
},
'keywords template var' => {
message => q|This will be a string with the keywords for this asset. Individual keywords will be joined with spaces, unless the keyword contains spaces, in which case it will be quoted.|,
lastUpdated => 0,
context => q|help for the keywords template variable|
},
'add the missing page' => {
message => q|Add the missing page.|,
lastUpdated => 0,

View file

@ -0,0 +1,451 @@
package WebGUI::i18n::English::Asset_Story;
use strict;
our $I18N = {
'assetName' => {
message => q|Story|,
context => q|Story, as in news story.|,
lastUpdated => 0
},
'headline' => {
message => q|Headline|,
context => q|Usually the title of a story. Label in the edit screen and template.|,
lastUpdated => 0
},
'headline help' => {
message => q|Often the same as title. If left blank, it will take the headline from the title.|,
context => q|Hoverhelp in the edit screen and template.|,
lastUpdated => 0
},
'headline tmplvar' => {
message => q|The headline for the Story.|,
context => q|Template variable help.|,
lastUpdated => 0
},
'subtitle' => {
message => q|Subtitle|,
context => q|Similar to headline, but usually contains more information. Label in the edit screen and template.|,
lastUpdated => 0
},
'subtitle help' => {
message => q|Similar to headline, but usually contains more information.|,
context => q|Hoverhelp in the edit screen and template.|,
lastUpdated => 0
},
'subtitle tmplvar' => {
message => q|The subtitle from the Story.|,
context => q|Template variable help.|,
lastUpdated => 0
},
'byline' => {
message => q|By line|,
context => q|Who wrote the story. Label in the edit screen and template.|,
lastUpdated => 0
},
'byline help' => {
message => q|Who wrote the story.|,
context => q|Hoverhelp in the edit screen and template.|,
lastUpdated => 0
},
'byline tmplvar' => {
message => q|The byline from the Story.|,
context => q|Template variable help.|,
lastUpdated => 0
},
'location' => {
message => q|Location|,
context => q|Where the story takes place. Label in the edit screen and template.|,
lastUpdated => 0
},
'location help' => {
message => q|Where the story takes place.|,
context => q|Hoverhelp in the edit screen and template.|,
lastUpdated => 0
},
'location tmplvar' => {
message => q|The location from the Story.|,
context => q|Template variable help.|,
lastUpdated => 0
},
'highlights' => {
message => q|Story Highlights|,
context => q|Bullet point level summaries from the story. Label in the edit screen and template.|,
lastUpdated => 0
},
'highlights help' => {
message => q|Bullet point level items from the story. Enter 1 per line.|,
context => q|Hoverhelp in the edit screen and template.|,
lastUpdated => 0
},
'highlights tmplvar' => {
message => q|All of the highlights from the Story. Each highlight will be separated by a newline character.|,
context => q|Template variable help.|,
lastUpdated => 0
},
'story' => {
message => q|Story|,
context => q|Label in the edit screen and template.|,
lastUpdated => 0
},
'story help' => {
message => q|The story.|,
context => q|Hoverhelp in the edit screen and template.|,
lastUpdated => 0
},
'story tmplvar' => {
message => q|The story. Note that it may contain embedded content from the Rich Text Editor.|,
context => q|Template variable help.|,
lastUpdated => 0
},
'save story' => {
message => q|Save Story|,
context => q|Button label in the Edit Story form.|,
lastUpdated => 0
},
'save and add another photo' => {
message => q|Save and Add Another Photo|,
context => q|Button label in the Edit Story form.|,
lastUpdated => 0
},
'story received' => {
message => q|Your story has been received and is being processed so it can be added to the site. It will be available for further editing after being processed. Please be patient.|,
lastUpdated => 0,
},
'edit template' => {
message => q|Edit Story Template.|,
lastUpdated => 0,
},
'formHeader' => {
message => q|HTML code to begin the form for adding or editing a Story.|,
lastUpdated => 0,
},
'formTitle' => {
message => q|Internationalized title for this form.|,
lastUpdated => 0,
},
'titleForm' => {
message => q|Form for the user to enter a title for this story.|,
lastUpdated => 0,
},
'headlineForm' => {
message => q|Form for the user to enter a headline for this story.|,
lastUpdated => 0,
},
'subtitleForm' => {
message => q|Form for the user to enter a subtitle for this story.|,
lastUpdated => 0,
},
'bylineForm' => {
message => q|Form for the user to enter a byline for this story.|,
lastUpdated => 0,
},
'locationForm' => {
message => q|Form for the user to enter a location for this story.|,
lastUpdated => 0,
},
'keywordsForm' => {
message => q|Form for the user to enter keywords for this story.|,
lastUpdated => 0,
},
'summaryForm' => {
message => q|Form for the user to enter a summary of this story.|,
lastUpdated => 0,
},
'highlightsForm' => {
message => q|Form for the user to enter highlights for this story.|,
lastUpdated => 0,
},
'storyForm' => {
message => q|Form for the user to enter the actual story.|,
lastUpdated => 0,
},
'saveButton' => {
message => q|Button for the user to save the form.|,
lastUpdated => 0,
},
'saveAndAddButton' => {
message => q|Button for the user to save the form, and then reopen the edit form to add another photo.|,
lastUpdated => 0,
},
'cancelButton' => {
message => q|Button for the user to cancel this form without saving anything.|,
lastUpdated => 0,
},
'formFooter' => {
message => q|HTML code to end the form for adding or editing a Story.|,
lastUpdated => 0,
},
'view template' => {
message => q|View Story Template.|,
lastUpdated => 0,
},
'highlights_loop' => {
message => q|A loop containing all the highlights from the story.|,
lastUpdated => 0,
},
'highlight' => {
message => q|One highlight, without formatting or extra HTML.|,
lastUpdated => 0,
},
'keywords_loop' => {
message => q|A loop containing all the keywords from the story.|,
lastUpdated => 0,
},
'keyword' => {
message => q|One keyword, with no formatting.|,
lastUpdated => 0,
},
'keyword_url' => {
message => q|A URL to view all stories in this archive related to this keyword.|,
lastUpdated => 0,
},
'crumb_loop' => {
message => q|A loop containing the crumbtrail. The first element will be a link to the archive that contains the story. The last element will be the story, with title and url. If there are 3 elements, the middle element will be the topic.|,
lastUpdated => 0,
},
'crumb_title' => {
message => q|The title of a page in the crumb trail.|,
lastUpdated => 0,
},
'crumb_url' => {
message => q|The title of a page in the crumb trail.|,
lastUpdated => 0,
},
'updatedTime' => {
message => q|The time this Story was last updated, as a formatted duration, like 1 Hour(s) ago.|,
lastUpdated => 0,
},
'updatedTimeEpoch' => {
message => q|The time this Story was last updated, as an epoch.|,
lastUpdated => 0,
},
'photo tmplvar' => {
message => q|The photo JSON blob from the Story asset.|,
lastUpdated => 0,
},
'storageId tmplvar' => {
message => q|The photo JSON blob from the Story asset.|,
lastUpdated => 0,
},
'ago' => {
message => q|ago|,
context => q|As in the phrase, Last updated 3 hours ago.|,
lastUpdated => 0,
},
'storageId tmplvar' => {
message => q|The photo JSON blob from the Story asset.|,
lastUpdated => 0,
},
'story asset template variables title' => {
message => q|Story Asset Template Variables.|,
context => q|Title of a help page for asset level template variables.|,
lastUpdated => 0,
},
'photo_form_loop' => {
message => q|A loop containing subforms for all photos that have been loaded, and a blank form for uploading new photos.|,
context => q|Template variable for edit form.|,
lastUpdated => 0,
},
'imgUploadForm' => {
message => q|A form field to upload an image.|,
context => q|Template variable for edit form.|,
lastUpdated => 0,
},
'imgCaptionForm' => {
message => q|A form field for the caption for this image.|,
context => q|Template variable for edit form.|,
lastUpdated => 0,
},
'imgBylineForm' => {
message => q|A form field for a by-line for this image.|,
context => q|Template variable for edit form.|,
lastUpdated => 0,
},
'imgAltForm' => {
message => q|A form field for alternate text for the image, for the IMG tag ALT field.|,
context => q|Template variable for edit form.|,
lastUpdated => 0,
},
'imgTitleForm' => {
message => q|A form field for the title for the image, for the IMG tag TITLE field.|,
context => q|Template variable for edit form.|,
lastUpdated => 0,
},
'imgUrlForm' => {
message => q|A field for the URL for this image. If present, then the image will be rendered as a link to this URL.|,
context => q|Template variable for edit form.|,
lastUpdated => 0,
},
'imgDeleteForm' => {
message => q|A field to delete the image, along with all data attached to it. This form will not be present in the set of variables in the loop for adding a new image.|,
context => q|Template variable for edit form.|,
lastUpdated => 0,
},
'photo caption' => {
message => q|Photo Caption|,
context => q|Label in the edit story form. Short for Photograph Caption.|,
lastUpdated => 0,
},
'photo byline' => {
message => q|Photo By Line|,
context => q|Label in the edit story form. The person who took, or owns this photo.|,
lastUpdated => 0,
},
'photo alt' => {
message => q|Photo Alternate Text|,
context => q|Label in the edit story form. Text for the ALT attribute of an IMG tag.|,
lastUpdated => 0,
},
'photo title' => {
message => q|Photo Alternate Title|,
context => q|Label in the edit story form. Text for the TITLE attribute of an IMG tag.|,
lastUpdated => 0,
},
'photo url' => {
message => q|Photo URL|,
context => q|Label in the edit story form. A link from the photo to more information about it, or referring to it.|,
lastUpdated => 0,
},
'photo delete' => {
message => q|Delete Photo|,
context => q|Label in the edit story form. Request that the photo be deleted, and all information with it.|,
lastUpdated => 0,
},
'photo_loop' => {
message => q|A loop containing photos and information about the photos.|,
context => q|Template variable|,
lastUpdated => 0,
},
'imageUrl' => {
message => q|The URL to the image.|,
context => q|Template variable|,
lastUpdated => 0,
},
'imageCaption' => {
message => q|A caption for the image.|,
context => q|Template variable|,
lastUpdated => 0,
},
'imageByline' => {
message => q|A byline for the image.|,
context => q|Template variable|,
lastUpdated => 0,
},
'imageAlt' => {
message => q|Alternate text for the image, suitable for use as the ALT parameter for an IMG tag.|,
context => q|Template variable|,
lastUpdated => 0,
},
'imageTitle' => {
message => q|Alternate text for the image, suitable for use as the TITLE parameter for an IMG tag.|,
context => q|Template variable|,
lastUpdated => 0,
},
'imageLink' => {
message => q|A URL for the image to link to.|,
context => q|Template variable|,
lastUpdated => 0,
},
'hasPhotos' => {
message => q|This template variable will be true if the Story has photos uploaded to it.|,
context => q|Template variable|,
lastUpdated => 0,
},
'singlePhoto' => {
message => q|This template variable will be true if the Story has just 1 photo uploaded to it.|,
context => q|Template variable|,
lastUpdated => 0,
},
'imageLink' => {
message => q|A URL for the image to link to.|,
context => q|Template variable|,
lastUpdated => 0,
},
'Source' => {
message => q|Source|,
context => q|Label for story template. Referring to who took, or who owns, a picture.|,
lastUpdated => 0,
},
};
1;

View file

@ -0,0 +1,302 @@
package WebGUI::i18n::English::Asset_StoryArchive;
use strict;
our $I18N = {
'assetName' => {
message => q|Story Archive|,
context => q|An Asset that holds stories.|,
lastUpdated => 0
},
'stories per page' => {
message => q|Stories Per Page|,
context => q|Label in the edit screen and template.|,
lastUpdated => 0
},
'stories per page help' => {
message => q|The number of stories displayed on a page. If the asset is exported as HTML, then the generated page will have 10 standard pages of stories.|,
context => q|Hoverhelp in the edit screen and template.|,
lastUpdated => 0
},
'group to post' => {
message => q|Group to Post|,
context => q|Label in the edit screen and template.|,
lastUpdated => 0
},
'group to post help' => {
message => q|The group allowed to add stories to this Story Archive.|,
context => q|Hoverhelp in the edit screen and template.|,
lastUpdated => 0
},
'groupToPost' => {
message => q|The GUID of the group allowed to add stories to this Story Archive.|,
context => q|Template variable.|,
lastUpdated => 0
},
'template' => {
message => q|Story Archive Template|,
context => q|Label in the edit screen and template.|,
lastUpdated => 0
},
'template help' => {
message => q|The Template used to display the Story Archive.|,
context => q|Hoverhelp in the edit screen and template.|,
lastUpdated => 0
},
'templateId' => {
message => q|The GUID of the template used to display the Story Archive.|,
context => q|Template variable|,
lastUpdated => 0
},
'story template' => {
message => q|Story Template|,
context => q|Label in the edit screen and template.|,
lastUpdated => 0
},
'story template help' => {
message => q|The Template used to display Story assets from this Story Archive.|,
context => q|Hoverhelp in the edit screen and template.|,
lastUpdated => 0
},
'storyTemplateId' => {
message => q|The GUID of the template used to display the Story assets.|,
context => q|Template variable|,
lastUpdated => 0
},
'edit story template' => {
message => q|Edit Story Template|,
context => q|Label in the edit screen and template.|,
lastUpdated => 0
},
'edit story template help' => {
message => q|The Template used to add or edit Story assets.|,
context => q|Hoverhelp in the edit screen and template.|,
lastUpdated => 0
},
'keyword list template' => {
message => q|Keyword List Template|,
context => q|Label in the edit screen and template.|,
lastUpdated => 0
},
'keyword list template help' => {
message => q|The Template used to render the list of assets matching a keyword when this StoryArchive is exported.|,
context => q|Hoverhelp in the edit screen and template.|,
lastUpdated => 0
},
'editStoryTemplateId' => {
message => q|The GUID of the template used to add or edit Story assets.|,
context => q|Template variable|,
lastUpdated => 0
},
'keywordListTemplateId' => {
message => q|The GUID of the template used to render list of assets matching a keyword when this StoryArchive is exported.|,
context => q|Template variable|,
lastUpdated => 0
},
'archive after' => {
message => q|Archive Stories After|,
context => q|Label in the edit screen and template.|,
lastUpdated => 0
},
'archive after help' => {
message => q|After this time, Story assets will be archived and no longer show up in the list of Stories or feeds. Set to 0 to disable archiving.|,
context => q|Hoverhelp in the edit screen and template.|,
lastUpdated => 0
},
'archiveAfter' => {
message => q|Amount of time in seconds. After this time, Stories will be archived.|,
context => q|Template variable|,
lastUpdated => 0
},
'rich editor' => {
message => q|Rich Editor|,
context => q|Label in the edit screen and template.|,
lastUpdated => 0
},
'rich editor help' => {
message => q|The WYSIWIG editor used to edit the content of Story assets.|,
context => q|Hoverhelp in the edit screen and template.|,
lastUpdated => 0
},
'richEditorId' => {
message => q|The GUID of the WYSIWIG editor used to edit the content of Story assets.|,
context => q|Template variable|,
lastUpdated => 0
},
'approval workflow' => {
message => q|Story Approval Workflow|,
context => q|Label in the edit screen and template.|,
lastUpdated => 0,
},
'approval workflow help' => {
message => q|Choose a workflow to be executed on each Story as it gets submitted.|,
context => q|Hoverhelp in the edit screen and template.|,
lastUpdated => 0,
},
'approvalWorkflowId' => {
message => q|The GUID of the workflow to be executed on each Story as it gets submitted.|,
context => q|Template variable|,
lastUpdated => 0,
},
'storyarchive asset template variables title' => {
message => q|Story Archive Asset Template Variables.|,
context => q|Title of a help page for asset level template variables.|,
lastUpdated => 0,
},
'view template' => {
message => q|Story Archive, View Template|,
context => q|Title of a help page.|,
lastUpdated => 0,
},
'date_loop' => {
message => q|A loop containing stories in the date they were submitted, with subloops for each day. The loop is paginated.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'epochDate' => {
message => q|The epoch that is the beginning of the day for a day where stories were submitted to the Story Archive.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'story_loop' => {
message => q|A loop containing all stories there were submitted on the day given by epochDate.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'url' => {
message => q|The URL to view a story.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'title' => {
message => q|The title of a story.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'creationDate' => {
message => q|The epoch date when this story was created, or submitted, to the Story Archive.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'add a story' => {
message => q|Add a Story.|,
context => q|label for the URL to add a story to the archive.|,
lastUpdated => 0,
},
'searchHeader' => {
message => q|HTML code for beginning the search form. This variable is empty when the Story Archive is being exported as HTML.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'searchForm' => {
message => q|The text field where users can enter in keywords for the search. This variable is empty when the Story Archive is being exported as HTML.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'searchButton' => {
message => q|Button with internationalized label for submitting the search form. This variable is empty when the Story Archive is being exported as HTML.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'searchFooter' => {
message => q|HTML code for ending the search form. This variable is empty when the Story Archive is being exported as HTML.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'canPostStories' => {
message => q|A boolean which is true if the user can post stories.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'addStoryUrl' => {
message => q|The URL for the user to add a Story.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'rssUrl' => {
message => q|The URL for the RSS feed for this Story Archive.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'atomUrl' => {
message => q|The URL for the Atom feed for this Story Archive.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'keywordCloud' => {
message => q|The tag cloud for the keywords for stories in this Story Archive.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'asset_loop' => {
message => q|A loop containing up to the first 50 assets that match the keyword.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'asset title' => {
message => q|The title of this asset.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'asset url' => {
message => q|The title of this url.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'keyword' => {
message => q|The keyword for this list of assets.|,
context => q|Template variable.|,
lastUpdated => 0,
},
};
1;

View file

@ -0,0 +1,176 @@
package WebGUI::i18n::English::Asset_StoryTopic;
use strict;
our $I18N = {
'assetName' => {
message => q|Story Topic|,
context => q|An Asset that displays stories based on keywords.|,
lastUpdated => 0
},
'stories per topic' => {
message => q|Stories Per Topic|,
context => q|Label in the edit screen and template.|,
lastUpdated => 0
},
'stories per topic help' => {
message => q|The number of stories displayed in RSS and Atom feeds from this Story Topic, and when viewing the Topic directly.|,
context => q|Hoverhelp in the edit screen and template.|,
lastUpdated => 0
},
'stories short' => {
message => q|Stories Per Page|,
context => q|Label in the edit screen and template.|,
lastUpdated => 0
},
'stories short help' => {
message => q|The number of stories displayed on a page.|,
context => q|Hoverhelp in the edit screen and template.|,
lastUpdated => 0
},
'story template' => {
message => q|Story Template|,
context => q|Label in the edit screen and template.|,
lastUpdated => 0
},
'story template help' => {
message => q|The Template used to display Story assets from this Story Topic.|,
context => q|Hoverhelp in the edit screen and template.|,
lastUpdated => 0
},
'template' => {
message => q|Main Template|,
context => q|Label in the edit screen and template.|,
lastUpdated => 0
},
'template help' => {
message => q|The Template used to display the Story Topic.|,
context => q|Hoverhelp in the edit screen and template.|,
lastUpdated => 0
},
'story_loop' => {
message => q|A loop containing the most recent stories for this topic.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'url' => {
message => q|The URL to view a story.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'title' => {
message => q|The title of a story.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'creationDate' => {
message => q|The epoch date when this story was created, or submitted, to its Story Archive.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'topStoryUrl' => {
message => q|The URL to view the top story.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'topStoryTitle' => {
message => q|The title of the top story.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'topStorySubtitle' => {
message => q|The subtitle of the top story.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'topStoryCreationDate' => {
message => q|The epoch date when the top story was created, or submitted, to its Story Archive.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'topStoryImageCaption' => {
message => q|The Caption of the first photo for the top story, if it exists.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'topStoryImageByline' => {
message => q|The Byline of the first photo for the top story, if it exists.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'topStoryImageAlt' => {
message => q|The alternate image text for the first photo for the top story, if it exists.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'topStoryImageTitle' => {
message => q|The image title for the first photo for the top story, if it exists.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'topStoryImageLink' => {
message => q|The link for the first photo for the top story, if it exists.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'topStoryImageUrl' => {
message => q|The URL to the first photo for the top story, if it exists.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'standAlone' => {
message => q|This variable will be true if the Story Topic is being viewed directly, by its URL. Otherwise, if it is viewed as part of a Page Layout or other Container, it will be false.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'view template' => {
message => q|View Story Topic Template|,
context => q|Template variable.|,
lastUpdated => 0,
},
'storytopic asset template variables title' => {
message => q|Story Topic Asset Template Variables|,
context => q|Template variable.|,
lastUpdated => 0,
},
'rssUrl' => {
message => q|The URL for the RSS feed for this Story Archive.|,
context => q|Template variable.|,
lastUpdated => 0,
},
'atomUrl' => {
message => q|The URL for the Atom feed for this Story Archive.|,
context => q|Template variable.|,
lastUpdated => 0,
},
};
1;

View file

@ -0,0 +1,13 @@
package WebGUI::i18n::English::Workflow_Activity_ArchiveOldStories;
use strict;
our $I18N = {
'activityName' => {
message => q|Archive Old Stories|,
context => q|The name of this workflow activity.|,
lastUpdated => 0,
},
};
1;