Merge branch 'master' of git://github.com/pdonelan/webgui into filepump
This commit is contained in:
commit
8e3891a7bd
44 changed files with 5516 additions and 80 deletions
|
|
@ -1,3 +1,12 @@
|
|||
7.7.5
|
||||
- Adding StoryManager.
|
||||
- fixed #10223: Calendar List View Ignores Event Permissions (dhelsten)
|
||||
- fixed #10226: html2text dropping text
|
||||
- fixed #10210: Generated Message-Id invalid (patch basis from tektek)
|
||||
- fixed #10209: Changing existing user profile field type doesn't change underlying database column type
|
||||
- fixed #10047: SQLReport Debug doesn't catch when bind variables are incorrect
|
||||
- fixed #10260: WebGUI::Asset::Wobject::Gallery.pm default search date misfunction
|
||||
|
||||
7.7.4
|
||||
- rfe: Extend DateTime for Week-Nrs (#9151)
|
||||
- fixed: 7.6 upgrade left some default content marked as packages
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
BIN
docs/upgrades/packages-7.7.5/root_import_storymanager.wgpkg
Normal file
BIN
docs/upgrades/packages-7.7.5/root_import_storymanager.wgpkg
Normal file
Binary file not shown.
218
docs/upgrades/upgrade_7.7.4-7.7.5.pl
Normal file
218
docs/upgrades/upgrade_7.7.4-7.7.5.pl
Normal file
|
|
@ -0,0 +1,218 @@
|
|||
#!/usr/bin/env perl
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
# WebGUI is Copyright 2001-2009 Plain Black Corporation.
|
||||
#-------------------------------------------------------------------
|
||||
# Please read the legal notices (docs/legal.txt) and the license
|
||||
# (docs/license.txt) that came with this distribution before using
|
||||
# this software.
|
||||
#-------------------------------------------------------------------
|
||||
# http://www.plainblack.com info@plainblack.com
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
our ($webguiRoot);
|
||||
|
||||
BEGIN {
|
||||
$webguiRoot = "../..";
|
||||
unshift (@INC, $webguiRoot."/lib");
|
||||
}
|
||||
|
||||
use strict;
|
||||
use Getopt::Long;
|
||||
use WebGUI::Session;
|
||||
use WebGUI::Storage;
|
||||
use WebGUI::Asset;
|
||||
use WebGUI::Utility qw/isIn/;
|
||||
|
||||
|
||||
my $toVersion = '7.7.5';
|
||||
my $quiet; # this line required
|
||||
|
||||
|
||||
my $session = start(); # this line required
|
||||
|
||||
# upgrade functions go here
|
||||
|
||||
# Story Manager
|
||||
installStoryManagerTables($session);
|
||||
sm_upgradeConfigFiles($session);
|
||||
sm_updateDailyWorkflow($session);
|
||||
|
||||
finish($session); # this line required
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Describe what our function does
|
||||
#sub exampleFunction {
|
||||
# my $session = shift;
|
||||
# print "\tWe're doing some stuff here that you should know about... " unless $quiet;
|
||||
# # and here's our code
|
||||
# print "DONE!\n" unless $quiet;
|
||||
#}
|
||||
|
||||
sub installStoryManagerTables {
|
||||
my ($session) = @_;
|
||||
print "\tAdding Story Manager tables... " unless $quiet;
|
||||
my $db = $session->db;
|
||||
$db->write(<<EOSTORY);
|
||||
CREATE TABLE Story (
|
||||
assetId CHAR(22) BINARY NOT NULL,
|
||||
revisionDate BIGINT NOT NULL,
|
||||
headline CHAR(255),
|
||||
subtitle CHAR(255),
|
||||
byline CHAR(255),
|
||||
location CHAR(255),
|
||||
highlights TEXT,
|
||||
story MEDIUMTEXT,
|
||||
photo LONGTEXT,
|
||||
PRIMARY KEY ( assetId, revisionDate )
|
||||
)
|
||||
EOSTORY
|
||||
|
||||
$db->write(<<EOARCHIVE);
|
||||
CREATE TABLE StoryArchive (
|
||||
assetId CHAR(22) BINARY NOT NULL,
|
||||
revisionDate BIGINT NOT NULL,
|
||||
storiesPerPage INTEGER,
|
||||
groupToPost CHAR(22) BINARY,
|
||||
templateId CHAR(22) BINARY,
|
||||
storyTemplateId CHAR(22) BINARY,
|
||||
editStoryTemplateId CHAR(22) BINARY,
|
||||
keywordListTemplateId CHAR(22) BINARY,
|
||||
archiveAfter INT(11),
|
||||
richEditorId CHAR(22) BINARY,
|
||||
approvalWorkflowId CHAR(22) BINARY DEFAULT 'pbworkflow000000000003',
|
||||
PRIMARY KEY ( assetId, revisionDate )
|
||||
)
|
||||
EOARCHIVE
|
||||
|
||||
$db->write(<<EOTOPIC);
|
||||
CREATE TABLE StoryTopic (
|
||||
assetId CHAR(22) BINARY NOT NULL,
|
||||
revisionDate BIGINT NOT NULL,
|
||||
storiesPer INTEGER,
|
||||
storiesShort INTEGER,
|
||||
templateId CHAR(22) BINARY,
|
||||
storyTemplateId CHAR(22) BINARY,
|
||||
PRIMARY KEY ( assetId, revisionDate )
|
||||
)
|
||||
EOTOPIC
|
||||
|
||||
print "DONE!\n" unless $quiet;
|
||||
}
|
||||
|
||||
sub sm_upgradeConfigFiles {
|
||||
my ($session) = @_;
|
||||
print "\tAdding Story Manager to config file... " unless $quiet;
|
||||
my $config = $session->config;
|
||||
$config->addToHash(
|
||||
'assets',
|
||||
'WebGUI::Asset::Wobject::StoryTopic' => {
|
||||
'category' => 'community'
|
||||
},
|
||||
);
|
||||
$config->addToHash(
|
||||
'assets',
|
||||
"WebGUI::Asset::Wobject::StoryArchive" => {
|
||||
"isContainer" => 1,
|
||||
"category" => "community"
|
||||
},
|
||||
);
|
||||
my $activities = $config->get('workflowActivities');
|
||||
my $none = $activities->{None};
|
||||
if (!isIn('WebGUI::Workflow::Activity::ArchiveOldStories', @{ $none })) {
|
||||
unshift @{ $none }, 'WebGUI::Workflow::Activity::ArchiveOldStories';
|
||||
}
|
||||
$config->set('workflowActivities', $activities);
|
||||
print "DONE!\n" unless $quiet;
|
||||
}
|
||||
|
||||
sub sm_updateDailyWorkflow {
|
||||
my ($session) = @_;
|
||||
print "\tAdding Archive Old Stories to Daily Workflow... " unless $quiet;
|
||||
my $workflow = WebGUI::Workflow->new($session, 'pbworkflow000000000001');
|
||||
foreach my $activity (@{ $workflow->getActivities }) {
|
||||
return if $activity->getName() eq 'WebGUI::Workflow::Activity::ArchiveOldStories';
|
||||
}
|
||||
my $activity = $workflow->addActivity('WebGUI::Workflow::Activity::ArchiveOldStories');
|
||||
$activity->set('title', 'Archive Old Stories');
|
||||
$activity->set('description', 'Archive old stories, based on the settings of the Story Archives that own them');
|
||||
print "DONE!\n" unless $quiet;
|
||||
}
|
||||
|
||||
# -------------- DO NOT EDIT BELOW THIS LINE --------------------------------
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Add a package to the import node
|
||||
sub addPackage {
|
||||
my $session = shift;
|
||||
my $file = shift;
|
||||
|
||||
# Make a storage location for the package
|
||||
my $storage = WebGUI::Storage->createTemp( $session );
|
||||
$storage->addFileFromFilesystem( $file );
|
||||
|
||||
# Import the package into the import node
|
||||
my $package = WebGUI::Asset->getImportNode($session)->importPackage( $storage );
|
||||
|
||||
# Turn off the package flag, and set the default flag for templates added
|
||||
my $assetIds = $package->getLineage( ['self','descendants'] );
|
||||
for my $assetId ( @{ $assetIds } ) {
|
||||
my $asset = WebGUI::Asset->newByDynamicClass( $session, $assetId );
|
||||
if ( !$asset ) {
|
||||
print "Couldn't instantiate asset with ID '$assetId'. Please check package '$file' for corruption.\n";
|
||||
next;
|
||||
}
|
||||
my $properties = { isPackage => 0 };
|
||||
if ($asset->isa('WebGUI::Asset::Template')) {
|
||||
$properties->{isDefault} = 1;
|
||||
}
|
||||
$asset->update( $properties );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
#-------------------------------------------------
|
||||
sub start {
|
||||
my $configFile;
|
||||
$|=1; #disable output buffering
|
||||
GetOptions(
|
||||
'configFile=s'=>\$configFile,
|
||||
'quiet'=>\$quiet
|
||||
);
|
||||
my $session = WebGUI::Session->open($webguiRoot,$configFile);
|
||||
$session->user({userId=>3});
|
||||
my $versionTag = WebGUI::VersionTag->getWorking($session);
|
||||
$versionTag->set({name=>"Upgrade to ".$toVersion});
|
||||
return $session;
|
||||
}
|
||||
|
||||
#-------------------------------------------------
|
||||
sub finish {
|
||||
my $session = shift;
|
||||
updateTemplates($session);
|
||||
my $versionTag = WebGUI::VersionTag->getWorking($session);
|
||||
$versionTag->commit;
|
||||
$session->db->write("insert into webguiVersion values (".$session->db->quote($toVersion).",'upgrade',".$session->datetime->time().")");
|
||||
$session->close();
|
||||
}
|
||||
|
||||
#-------------------------------------------------
|
||||
sub updateTemplates {
|
||||
my $session = shift;
|
||||
return undef unless (-d "packages-".$toVersion);
|
||||
print "\tUpdating packages.\n" unless ($quiet);
|
||||
opendir(DIR,"packages-".$toVersion);
|
||||
my @files = readdir(DIR);
|
||||
closedir(DIR);
|
||||
my $newFolder = undef;
|
||||
foreach my $file (@files) {
|
||||
next unless ($file =~ /\.wgpkg$/);
|
||||
# Fix the filename to include a path
|
||||
$file = "packages-" . $toVersion . "/" . $file;
|
||||
addPackage( $session, $file );
|
||||
}
|
||||
}
|
||||
|
||||
#vim:ft=perl
|
||||
|
|
@ -526,6 +526,13 @@
|
|||
"isContainer" : 1,
|
||||
"category" : "community"
|
||||
},
|
||||
"WebGUI::Asset::Wobject::StoryArchive" : {
|
||||
"isContainer" : 1,
|
||||
"category" : "community"
|
||||
},
|
||||
"WebGUI::Asset::Wobject::StoryTopic" : {
|
||||
"category" : "community"
|
||||
},
|
||||
"WebGUI::Asset::Wobject::StockData" : {
|
||||
"category" : "intranet"
|
||||
},
|
||||
|
|
@ -812,6 +819,7 @@
|
|||
|
||||
"workflowActivities" : {
|
||||
"None" : [
|
||||
"WebGUI::Workflow::Activity::ArchiveOldStories",
|
||||
"WebGUI::Workflow::Activity::ArchiveOldThreads",
|
||||
"WebGUI::Workflow::Activity::CalendarUpdateFeeds",
|
||||
"WebGUI::Workflow::Activity::CleanDatabaseCache",
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package WebGUI;
|
||||
|
||||
|
||||
our $VERSION = '7.7.4';
|
||||
our $VERSION = '7.7.5';
|
||||
our $STATUS = 'beta';
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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
900
lib/WebGUI/Asset/Story.pm
Normal 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
|
||||
|
|
@ -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 );
|
||||
|
||||
|
|
|
|||
|
|
@ -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' ),
|
||||
|
|
|
|||
605
lib/WebGUI/Asset/Wobject/StoryArchive.pm
Normal file
605
lib/WebGUI/Asset/Wobject/StoryArchive.pm
Normal 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(
|
||||
' ' . $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
|
||||
267
lib/WebGUI/Asset/Wobject/StoryTopic.pm
Normal file
267
lib/WebGUI/Asset/Wobject/StoryTopic.pm
Normal 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
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -227,7 +227,7 @@ my $text = "";
|
|||
my $inside = {};
|
||||
|
||||
sub html2text {
|
||||
my $html = shift;
|
||||
my $html = shift() . " ";
|
||||
$text = "";
|
||||
$inside = {};
|
||||
my $tagHandler = sub {
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ our $HELP = {
|
|||
{ name => 'isPrototype', },
|
||||
{ name => 'status', },
|
||||
{ name => 'assetSize', },
|
||||
{ name => 'keywords',
|
||||
description => 'keywords template var' },
|
||||
],
|
||||
fields => [],
|
||||
related => []
|
||||
|
|
|
|||
166
lib/WebGUI/Help/Asset_Story.pm
Normal file
166
lib/WebGUI/Help/Asset_Story.pm
Normal 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;
|
||||
102
lib/WebGUI/Help/Asset_StoryArchive.pm
Normal file
102
lib/WebGUI/Help/Asset_StoryArchive.pm
Normal 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;
|
||||
64
lib/WebGUI/Help/Asset_StoryTopic.pm
Normal file
64
lib/WebGUI/Help/Asset_StoryTopic.pm
Normal 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;
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
104
lib/WebGUI/Workflow/Activity/ArchiveOldStories.pm
Normal file
104
lib/WebGUI/Workflow/Activity/ArchiveOldStories.pm
Normal 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;
|
||||
|
||||
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
451
lib/WebGUI/i18n/English/Asset_Story.pm
Normal file
451
lib/WebGUI/i18n/English/Asset_Story.pm
Normal 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;
|
||||
302
lib/WebGUI/i18n/English/Asset_StoryArchive.pm
Normal file
302
lib/WebGUI/i18n/English/Asset_StoryArchive.pm
Normal 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;
|
||||
176
lib/WebGUI/i18n/English/Asset_StoryTopic.pm
Normal file
176
lib/WebGUI/i18n/English/Asset_StoryTopic.pm
Normal 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;
|
||||
|
|
@ -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;
|
||||
|
|
@ -150,7 +150,7 @@ $canViewMaker->prepare(
|
|||
},
|
||||
);
|
||||
|
||||
plan tests => 103
|
||||
plan tests => 104
|
||||
+ scalar(@fixIdTests)
|
||||
+ scalar(@fixTitleTests)
|
||||
+ 2*scalar(@getTitleTests) #same tests used for getTitle and getMenuTitle
|
||||
|
|
@ -391,6 +391,7 @@ is($importNode->fixUrl('/extras2'), '_extras2', 'underscore prepended to URLs th
|
|||
is($importNode->fixUrl('one.html/two.html'), 'one/two.html', 'extensions are not allowed higher up in the path');
|
||||
is($importNode->fixUrl('one.html/two.html/three.html'), 'one/two/three.html', 'extensions are not allowed anywhere in the path');
|
||||
is($importNode->fixUrl('one.one.html/two.html/three.html'), 'one/two/three.html', 'multiple dot extensions are removed in any path element');
|
||||
is($importNode->fixUrl('.startsWithDot'), '.startswithdot', 'leading dots are okay');
|
||||
|
||||
##Now, check duplicate URLs
|
||||
|
||||
|
|
|
|||
391
t/Asset/Story.t
Normal file
391
t/Asset/Story.t
Normal file
|
|
@ -0,0 +1,391 @@
|
|||
#-------------------------------------------------------------------
|
||||
# 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 FindBin;
|
||||
use strict;
|
||||
use lib "$FindBin::Bin/../lib";
|
||||
|
||||
use WebGUI::Test;
|
||||
use WebGUI::Session;
|
||||
use WebGUI::Storage;
|
||||
|
||||
use Test::More; # increment this value for each test you create
|
||||
use Test::Deep;
|
||||
use Data::Dumper;
|
||||
|
||||
my $tests = 42;
|
||||
plan tests => 1
|
||||
+ $tests
|
||||
;
|
||||
|
||||
#TODO: This script tests certain aspects of WebGUI::Storage and it should not
|
||||
|
||||
my $session = WebGUI::Test->session;
|
||||
|
||||
my $class = 'WebGUI::Asset::Story';
|
||||
my $loaded = use_ok($class);
|
||||
my $story;
|
||||
my $wgBday = WebGUI::Test->webguiBirthday;
|
||||
|
||||
my $defaultNode = WebGUI::Asset->getDefault($session);
|
||||
my $archive = $defaultNode->addChild({
|
||||
className => 'WebGUI::Asset::Wobject::StoryArchive',
|
||||
title => 'Test Archive',
|
||||
#1234567890123456789012
|
||||
assetId => 'TestStoryArchiveAsset1',
|
||||
});
|
||||
my $topic = $defaultNode->addChild({
|
||||
className => 'WebGUI::Asset::Wobject::StoryTopic',
|
||||
title => 'Test Topic',
|
||||
#1234567890123456789012
|
||||
assetId => 'TestStoryTopicAsset123',
|
||||
keywords => 'tango,yankee',
|
||||
});
|
||||
my $archiveTag = WebGUI::VersionTag->getWorking($session);
|
||||
$archiveTag->commit;
|
||||
|
||||
my $storage1 = WebGUI::Storage->create($session);
|
||||
my $storage2 = WebGUI::Storage->create($session);
|
||||
WebGUI::Test->storagesToDelete($storage1, $storage2);
|
||||
|
||||
|
||||
SKIP: {
|
||||
|
||||
skip "Unable to load module $class", $tests unless $loaded;
|
||||
|
||||
############################################################
|
||||
#
|
||||
# validParent
|
||||
#
|
||||
############################################################
|
||||
|
||||
ok(! WebGUI::Asset::Story->validParent($session), 'validParent: no session asset');
|
||||
$session->asset($defaultNode);
|
||||
ok(! WebGUI::Asset::Story->validParent($session), 'validParent: wrong type of asset');
|
||||
$session->asset($archive);
|
||||
ok( WebGUI::Asset::Story->validParent($session), 'validParent: StoryArchive is valid');
|
||||
|
||||
############################################################
|
||||
#
|
||||
# Make a new one. Test defaults
|
||||
#
|
||||
############################################################
|
||||
|
||||
$story = $archive->addChild({
|
||||
className => 'WebGUI::Asset::Story',
|
||||
title => 'Story 1',
|
||||
subtitle => 'The story of a CMS',
|
||||
byline => 'JT Smith',
|
||||
});
|
||||
|
||||
isa_ok($story, 'WebGUI::Asset::Story', 'Created a Story asset');
|
||||
is($story->get('photo'), '[]', 'by default, photos is an empty JSON array');
|
||||
is($story->get('isHidden'), 1, 'by default, stories are hidden');
|
||||
$story->update({isHidden => 0});
|
||||
is($story->get('isHidden'), 1, 'stories cannot be set to not be hidden');
|
||||
is($story->get('state'), 'published', 'Story is published');
|
||||
|
||||
{
|
||||
##Version control does not alter the current object's status, fetch an updated copy from the
|
||||
##db.
|
||||
my $storyDB = WebGUI::Asset->newByUrl($session, $story->getUrl);
|
||||
is($storyDB->get('status'), 'approved', 'Story is approved');
|
||||
}
|
||||
|
||||
|
||||
############################################################
|
||||
#
|
||||
# getArchive
|
||||
#
|
||||
############################################################
|
||||
|
||||
is($story->getArchive->getId, $archive->getId, 'getArchive gets the parent archive for the Story');
|
||||
|
||||
############################################################
|
||||
#
|
||||
# Photo JSON
|
||||
#
|
||||
############################################################
|
||||
|
||||
my $photoData = $story->getPhotoData();
|
||||
cmp_deeply(
|
||||
$photoData, [],
|
||||
'getPhotoData: returns an empty array ref with no JSON data'
|
||||
);
|
||||
|
||||
$story->setPhotoData([
|
||||
{
|
||||
byLine => 'Andrew Dufresne',
|
||||
caption => 'Shawshank Prison',
|
||||
},
|
||||
]);
|
||||
|
||||
is($story->get('photo'), q|[{"caption":"Shawshank Prison","byLine":"Andrew Dufresne"}]|, 'setPhotoData: set JSON in the photo property');
|
||||
|
||||
$photoData = $story->getPhotoData();
|
||||
$photoData->[0]->{caption}="My cell";
|
||||
|
||||
cmp_deeply(
|
||||
$story->getPhotoData,
|
||||
[
|
||||
{
|
||||
byLine => 'Andrew Dufresne',
|
||||
caption => 'Shawshank Prison',
|
||||
},
|
||||
],
|
||||
'getPhotoData does not return an unsafe reference'
|
||||
);
|
||||
|
||||
$story->setPhotoData();
|
||||
cmp_deeply(
|
||||
$story->getPhotoData, [],
|
||||
'setPhotoData: wipes the stored data if nothing is passed'
|
||||
);
|
||||
|
||||
############################################################
|
||||
#
|
||||
# formatDuration
|
||||
#
|
||||
############################################################
|
||||
|
||||
is($story->formatDuration(time() - (24*3600+15)), '1 Day(s)', 'formatDuration, 1 day');
|
||||
is($story->formatDuration(time() - (48*3600+15)), '2 Day(s)', 'formatDuration, 2 day');
|
||||
like($story->formatDuration($wgBday), qr{Year.s.}, 'formatDuration: a long time ago');
|
||||
is($story->formatDuration(time() - (3600+5)), '1 Hour(s)', 'formatDuration: 1 hour');
|
||||
is($story->formatDuration(time() - (60+5)), '1 Minute(s)', 'formatDuration: 1 minute');
|
||||
is($story->formatDuration(time() - (7200+120)), '2 Hour(s), 2 Minute(s)', 'formatDuration: 2 hours, 2 minutes');
|
||||
|
||||
############################################################
|
||||
#
|
||||
# getCrumbTrail
|
||||
#
|
||||
############################################################
|
||||
|
||||
cmp_deeply(
|
||||
$story->getCrumbTrail,
|
||||
[
|
||||
{
|
||||
title => $archive->getTitle,
|
||||
url => $archive->getUrl,
|
||||
},
|
||||
{
|
||||
title => $story->getTitle,
|
||||
url => $story->getUrl,
|
||||
},
|
||||
],
|
||||
'getCrumbTrail: with no topic set'
|
||||
);
|
||||
|
||||
$story->topic($topic);
|
||||
|
||||
cmp_deeply(
|
||||
$story->getCrumbTrail,
|
||||
[
|
||||
{
|
||||
title => $archive->getTitle,
|
||||
url => $archive->getUrl,
|
||||
},
|
||||
{
|
||||
title => $topic->getTitle,
|
||||
url => $topic->getUrl,
|
||||
},
|
||||
{
|
||||
title => $story->getTitle,
|
||||
url => $story->getUrl,
|
||||
},
|
||||
],
|
||||
'getCrumbTrail: with topic set'
|
||||
);
|
||||
|
||||
$story->topic('');
|
||||
|
||||
############################################################
|
||||
#
|
||||
# getRssData
|
||||
#
|
||||
############################################################
|
||||
|
||||
can_ok($story, 'getRssData');
|
||||
|
||||
cmp_deeply(
|
||||
$story->getRssData,
|
||||
{
|
||||
title => 'Story 1',
|
||||
description => 'The story of a CMS',
|
||||
'link' => re('story-1$'),
|
||||
author => 'JT Smith',
|
||||
date => $story->get('lastModified'),
|
||||
},
|
||||
'getRssData: returns correct data'
|
||||
);
|
||||
|
||||
$story->update({headline => 'WebGUI, Web Done Right'});
|
||||
|
||||
is($story->getRssData->{title}, 'WebGUI, Web Done Right', '... headline preferred over title if present');
|
||||
|
||||
############################################################
|
||||
#
|
||||
# viewTemplateVariables
|
||||
#
|
||||
############################################################
|
||||
|
||||
$story->update({
|
||||
highlights => "one\ntwo\nthree",
|
||||
keywords => "foxtrot,tango,whiskey",
|
||||
});
|
||||
is($story->get('highlights'), "one\ntwo\nthree", 'highlights set correctly for template var check');
|
||||
|
||||
$storage1->addFileFromFilesystem(WebGUI::Test->getTestCollateralPath('gooey.jpg'));
|
||||
$storage2->addFileFromFilesystem(WebGUI::Test->getTestCollateralPath('lamp.jpg'));
|
||||
|
||||
$story->setPhotoData([
|
||||
{
|
||||
storageId => $storage1->getId,
|
||||
caption => 'Mascot for a popular CMS',
|
||||
byLine => 'Darcy Gibson',
|
||||
alt => 'Gooey',
|
||||
title => 'Mascot',
|
||||
url => 'http://www.webgui.org',
|
||||
},
|
||||
{
|
||||
storageId => $storage2->getId,
|
||||
caption => 'The Lamp',
|
||||
byLine => 'Aladdin',
|
||||
alt => 'Lamp',
|
||||
title => '',
|
||||
url => 'http://www.lamp.com',
|
||||
},
|
||||
]);
|
||||
|
||||
|
||||
my $viewVariables = $story->viewTemplateVariables;
|
||||
#diag Dumper $viewVariables;
|
||||
cmp_deeply(
|
||||
$viewVariables->{highlights_loop},
|
||||
[
|
||||
{ highlight => "one", },
|
||||
{ highlight => "two", },
|
||||
{ highlight => "three", },
|
||||
],
|
||||
'viewTemplateVariables: highlights_loop is okay'
|
||||
);
|
||||
|
||||
is($viewVariables->{title}, 'Story 1', '... title is okay');
|
||||
is($viewVariables->{headline}, 'WebGUI, Web Done Right', '... headline is okay');
|
||||
|
||||
cmp_bag(
|
||||
$viewVariables->{keyword_loop},
|
||||
[
|
||||
{ keyword => "foxtrot", url => '/home/test-archive?func=view;keywords=foxtrot', },
|
||||
{ keyword => "tango", url => '/home/test-archive?func=view;keywords=tango', },
|
||||
{ keyword => "whiskey", url => '/home/test-archive?func=view;keywords=whiskey', },
|
||||
],
|
||||
'viewTemplateVariables: keywords_loop is okay'
|
||||
);
|
||||
|
||||
is ($viewVariables->{updatedTimeEpoch}, $story->get('revisionDate'), 'viewTemplateVariables: updatedTimeEpoch');
|
||||
|
||||
cmp_deeply(
|
||||
$viewVariables->{photo_loop},
|
||||
[
|
||||
{
|
||||
imageUrl => re('gooey.jpg'),
|
||||
imageCaption => 'Mascot for a popular CMS',
|
||||
imageByline => 'Darcy Gibson',
|
||||
imageAlt => 'Gooey',
|
||||
imageTitle => 'Mascot',
|
||||
imageLink => 'http://www.webgui.org',
|
||||
},
|
||||
{
|
||||
imageUrl => re('lamp.jpg'),
|
||||
imageCaption => 'The Lamp',
|
||||
imageByline => 'Aladdin',
|
||||
imageAlt => 'Lamp',
|
||||
imageTitle => '',
|
||||
imageLink => 'http://www.lamp.com',
|
||||
},
|
||||
],
|
||||
'viewTemplateVariables: photo_loop is okay'
|
||||
);
|
||||
|
||||
ok(! $viewVariables->{singlePhoto}, 'viewVariables: singlePhoto: there is more than 1');
|
||||
ok( $viewVariables->{hasPhotos}, 'viewVariables: hasPhotos: it has photos');
|
||||
|
||||
##Simulate someone deleting the file stored in the storage object.
|
||||
$storage2->deleteFile('lamp.jpg');
|
||||
$viewVariables = $story->viewTemplateVariables;
|
||||
|
||||
cmp_deeply(
|
||||
$viewVariables->{photo_loop},
|
||||
[
|
||||
{
|
||||
imageUrl => re('gooey.jpg'),
|
||||
imageCaption => 'Mascot for a popular CMS',
|
||||
imageByline => 'Darcy Gibson',
|
||||
imageAlt => 'Gooey',
|
||||
imageTitle => 'Mascot',
|
||||
imageLink => 'http://www.webgui.org',
|
||||
},
|
||||
],
|
||||
'viewTemplateVariables: photo_loop: if the storage has no files, it is not shown'
|
||||
);
|
||||
|
||||
ok($viewVariables->{singlePhoto}, 'viewVariables: singlePhoto: there is just 1');
|
||||
ok($viewVariables->{hasPhotos}, 'viewVariables: hasPhotos: it has photos');
|
||||
|
||||
############################################################
|
||||
#
|
||||
# duplicatePhotoData
|
||||
#
|
||||
############################################################
|
||||
|
||||
$photoData = $story->getPhotoData;
|
||||
$photoData->[0]->{storageId} = re('^[A-Za-z0-9_-]{22}$');
|
||||
$photoData->[1]->{storageId} = re('^[A-Za-z0-9_-]{22}$');
|
||||
my $newPhotoData = $story->duplicatePhotoData;
|
||||
|
||||
cmp_deeply(
|
||||
$newPhotoData,
|
||||
$photoData,
|
||||
'duplicatePhotoData: checking JSON data minus storage locations'
|
||||
);
|
||||
|
||||
isnt($newPhotoData->[0]->{storageId}, $photoData->[0]->{storageId}, '... and storage 0 is duplicated');
|
||||
isnt($newPhotoData->[1]->{storageId}, $photoData->[1]->{storageId}, '... and storage 1 is duplicated');
|
||||
|
||||
WebGUI::Test->storagesToDelete( map { $_->{storageId} } @{ $newPhotoData } );
|
||||
|
||||
############################################################
|
||||
#
|
||||
# exportAssetData
|
||||
#
|
||||
############################################################
|
||||
|
||||
my $exportData = $story->exportAssetData;
|
||||
isa_ok($exportData, 'HASH', 'exportAssetData');
|
||||
|
||||
cmp_bag(
|
||||
$exportData->{storage},
|
||||
[
|
||||
$storage1->getId,
|
||||
$storage2->getId,
|
||||
],
|
||||
'...asset package data has the storage locations in it'
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
END {
|
||||
$story->purge if $story;
|
||||
$archive->purge if $archive;
|
||||
$topic->purge if $topic;
|
||||
$archiveTag->rollback;
|
||||
WebGUI::VersionTag->getWorking($session)->rollback;
|
||||
}
|
||||
653
t/Asset/Wobject/StoryArchive.t
Normal file
653
t/Asset/Wobject/StoryArchive.t
Normal file
|
|
@ -0,0 +1,653 @@
|
|||
# vim:syntax=perl
|
||||
#-------------------------------------------------------------------
|
||||
# WebGUI is Copyright 2001-2009 Plain Black Corporation.
|
||||
#-------------------------------------------------------------------
|
||||
# Please read the legal notices (docs/legal.txt) and the license
|
||||
# (docs/license.txt) that came with this distribution before using
|
||||
# this software.
|
||||
#------------------------------------------------------------------
|
||||
# http://www.plainblack.com info@plainblack.com
|
||||
#------------------------------------------------------------------
|
||||
|
||||
# Write a little about what this script tests.
|
||||
#
|
||||
#
|
||||
|
||||
use FindBin;
|
||||
use strict;
|
||||
use lib "$FindBin::Bin/../../lib";
|
||||
use Test::More;
|
||||
use Test::Deep;
|
||||
use File::Copy qw/mv/;
|
||||
use File::Path;
|
||||
use Data::Dumper;
|
||||
use Path::Class;
|
||||
|
||||
use WebGUI::Test; # Must use this before any other WebGUI modules
|
||||
use WebGUI::Test::Maker::Permission;
|
||||
use WebGUI::Session;
|
||||
use WebGUI::Text;
|
||||
use WebGUI::Utility;
|
||||
use WebGUI::DateTime;
|
||||
use DateTime;
|
||||
|
||||
################################################################
|
||||
#
|
||||
# setup session, users and groups for this test
|
||||
#
|
||||
################################################################
|
||||
|
||||
my $session = WebGUI::Test->session;
|
||||
|
||||
my $staff = WebGUI::Group->new($session, 'new');
|
||||
WebGUI::Test->groupsToDelete($staff);
|
||||
$staff->name('Reporting Staff');
|
||||
|
||||
my $reporter = WebGUI::User->new($session, 'new');
|
||||
$reporter->username('reporter');
|
||||
my $editor = WebGUI::User->new($session, 'new');
|
||||
$editor->username('editor');
|
||||
my $reader = WebGUI::User->new($session, 'new');
|
||||
$reader->username('reader');
|
||||
$staff->addUsers([$reporter->userId]);
|
||||
|
||||
my $archive = 'placeholder for Test::Maker::Permission';
|
||||
|
||||
my $canPostMaker = WebGUI::Test::Maker::Permission->new();
|
||||
$canPostMaker->prepare({
|
||||
object => $archive,
|
||||
session => $session,
|
||||
method => 'canPostStories',
|
||||
pass => [3, $editor, $reporter ],
|
||||
fail => [1, $reader ],
|
||||
});
|
||||
|
||||
my $tests = 45
|
||||
+ $canPostMaker->plan
|
||||
;
|
||||
plan tests => 1
|
||||
+ $tests;
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# put your tests here
|
||||
|
||||
my $class = 'WebGUI::Asset::Wobject::StoryArchive';
|
||||
my $loaded = use_ok($class);
|
||||
|
||||
my $storage;
|
||||
my $versionTag;
|
||||
|
||||
my $creationDateSth = $session->db->prepare('update asset set creationDate=? where assetId=?');
|
||||
|
||||
SKIP: {
|
||||
|
||||
skip "Unable to load module $class", $tests unless $loaded;
|
||||
|
||||
$archive = WebGUI::Asset->getDefault($session)->addChild({className => $class, title => 'My Stories', url => '/home/mystories'});
|
||||
$versionTag = WebGUI::VersionTag->getWorking($session);
|
||||
$versionTag->commit;
|
||||
|
||||
isa_ok($archive, 'WebGUI::Asset::Wobject::StoryArchive', 'created StoryArchive');
|
||||
|
||||
################################################################
|
||||
#
|
||||
# canPostStories
|
||||
#
|
||||
################################################################
|
||||
|
||||
$archive->update({
|
||||
ownerUserId => $editor->userId,
|
||||
groupToPost => $staff->getId,
|
||||
});
|
||||
|
||||
is($archive->get('groupToPost'), $staff->getId, 'set Staff group to post to Story Archive');
|
||||
|
||||
$canPostMaker->{_tests}->[0]->{object} = $archive;
|
||||
|
||||
$canPostMaker->run();
|
||||
|
||||
################################################################
|
||||
#
|
||||
# getFolder
|
||||
#
|
||||
################################################################
|
||||
|
||||
##Note, this is just to prevent date rollover from happening.
|
||||
##We'll test implicit getFolder later on.
|
||||
my $now = time();
|
||||
my $todayFolder = $archive->getFolder($now);
|
||||
isa_ok($todayFolder, 'WebGUI::Asset::Wobject::Folder', 'getFolder created a Folder');
|
||||
is($archive->getChildCount, 1, 'getFolder created a child');
|
||||
my $dt = DateTime->from_epoch(epoch => $now, time_zone => $session->datetime->getTimeZone);
|
||||
my $folderName = $dt->strftime('%B_%d_%Y');
|
||||
$folderName =~ s/^(\w+_)0/$1/;
|
||||
is($todayFolder->getTitle, $folderName, 'getFolder: folder has the right name');
|
||||
my $folderUrl = join '/', $archive->getUrl, lc $folderName;
|
||||
is($todayFolder->getUrl, $folderUrl, 'getFolder: folder has the right URL');
|
||||
is($todayFolder->getParent->getId, $archive->getId, 'getFolder: created folder has the right parent');
|
||||
is($todayFolder->get('state'), 'published', 'getFolder: created folder is published');
|
||||
is($todayFolder->get('status'), 'approved', 'getFolder: created folder is approved');
|
||||
|
||||
my $sameFolder = $archive->getFolder($now);
|
||||
is($sameFolder->getId, $todayFolder->getId, 'call with same time returns the same folder');
|
||||
undef $sameFolder;
|
||||
|
||||
my ($startOfDay, $endOfDay) = $session->datetime->dayStartEnd($now);
|
||||
$sameFolder = $archive->getFolder($startOfDay);
|
||||
is($sameFolder->getId, $todayFolder->getId, 'call within same day(start) returns the same folder');
|
||||
undef $sameFolder;
|
||||
$sameFolder = $archive->getFolder($endOfDay);
|
||||
is($sameFolder->getId, $todayFolder->getId, 'call within same day(end) returns the same folder');
|
||||
undef $sameFolder;
|
||||
$todayFolder->purge;
|
||||
is($archive->getChildCount, 0, 'leaving with an empty archive');
|
||||
|
||||
################################################################
|
||||
#
|
||||
# addChild
|
||||
#
|
||||
################################################################
|
||||
|
||||
my $child = $archive->addChild({className => 'WebGUI::Asset::Wobject::StoryTopic'});
|
||||
is($child, undef, 'addChild: Will only add Stories');
|
||||
|
||||
$child = $archive->addChild({className => 'WebGUI::Asset::Story', title => 'First Story'});
|
||||
isa_ok($child, 'WebGUI::Asset::Story', 'addChild added and returned a Story');
|
||||
is($archive->getChildCount, 1, 'addChild: added it to the archive');
|
||||
my $folder = $archive->getFirstChild();
|
||||
isa_ok($folder, 'WebGUI::Asset::Wobject::Folder', 'Folder was added to Archive');
|
||||
is($folder->getChildCount, 1, 'The folder has 1 child...');
|
||||
is($folder->getFirstChild->getTitle, 'First Story', '... and it is the correct child');
|
||||
|
||||
################################################################
|
||||
#
|
||||
# getKeywordFilename
|
||||
#
|
||||
################################################################
|
||||
|
||||
##Note, this method depends heavily on the default installed language pack.
|
||||
##Because of that, we'll only test for whether or not url->urlize is called.
|
||||
|
||||
is ($archive->getKeywordFilename('camelCase'), 'keyword_camelcase.html', 'getKeywordFilename returns a lower case keyword with _keyword.html appended');
|
||||
|
||||
################################################################
|
||||
#
|
||||
# viewTemplateVariables
|
||||
#
|
||||
################################################################
|
||||
|
||||
my $wgBday = WebGUI::Test->webguiBirthday;
|
||||
my $oldFolder = $archive->getFolder($wgBday);
|
||||
|
||||
my $yesterday = $now-24*3600;
|
||||
my $newFolder = $archive->getFolder($yesterday);
|
||||
|
||||
my ($wgBdayMorn,undef) = $session->datetime->dayStartEnd($wgBday);
|
||||
my ($yesterdayMorn,undef) = $session->datetime->dayStartEnd($yesterday);
|
||||
|
||||
my $story = $oldFolder->addChild({ className => 'WebGUI::Asset::Story', title => 'WebGUI is released', keywords => 'roger,foxtrot,echo'});
|
||||
$creationDateSth->execute([$wgBday, $story->getId]);
|
||||
|
||||
{
|
||||
my $storyDB = WebGUI::Asset->newByUrl($session, $story->getUrl);
|
||||
is ($storyDB->get('status'), 'approved', 'addRevision always calls for an autocommit');
|
||||
}
|
||||
|
||||
my $pastStory = $newFolder->addChild({ className => 'WebGUI::Asset::Story', title => "Yesterday is history" });
|
||||
$creationDateSth->execute([$yesterday, $pastStory->getId]);
|
||||
|
||||
my $templateVars;
|
||||
$templateVars = $archive->viewTemplateVariables();
|
||||
|
||||
cmp_deeply(
|
||||
$templateVars,
|
||||
superhashof({
|
||||
searchHeader => ignore(),
|
||||
searchForm => ignore(),
|
||||
searchButton => ignore(),
|
||||
searchFooter => ignore(),
|
||||
}),
|
||||
'viewTemplateVars: search variables present'
|
||||
);
|
||||
|
||||
cmp_deeply(
|
||||
$templateVars,
|
||||
superhashof({
|
||||
rssUrl => $archive->getRssFeedUrl,
|
||||
atomUrl => $archive->getAtomFeedUrl,
|
||||
}),
|
||||
'viewTemplateVars: RSS and Atom feed template variables'
|
||||
);
|
||||
|
||||
KEY: foreach my $key (keys %{ $templateVars }) {
|
||||
next KEY if isIn($key, qw/canPostStories addStoryUrl date_loop mode/);
|
||||
delete $templateVars->{$key};
|
||||
}
|
||||
|
||||
cmp_deeply(
|
||||
$templateVars,
|
||||
{
|
||||
canPostStories => 0,
|
||||
mode => 'view',
|
||||
addStoryUrl => '',
|
||||
date_loop => [
|
||||
{
|
||||
epochDate => ignore(),
|
||||
story_loop => [ {
|
||||
creationDate => ignore(),
|
||||
url => re('first-story'),
|
||||
title => 'First Story',
|
||||
}, ],
|
||||
},
|
||||
{
|
||||
epochDate => $yesterdayMorn,
|
||||
story_loop => [{
|
||||
creationDate => $yesterday,
|
||||
url => re('yesterday-is-history'),
|
||||
title => "Yesterday is history",
|
||||
}, ],
|
||||
},
|
||||
{
|
||||
epochDate => $wgBdayMorn,
|
||||
story_loop => [ {
|
||||
creationDate => $wgBday,
|
||||
url => '/home/mystories/august_16_2001/webgui-is-released',
|
||||
title => 'WebGUI is released',
|
||||
}, ],
|
||||
},
|
||||
]
|
||||
},
|
||||
'viewTemplateVariables: returns expected template variables with 3 stories in different folders'
|
||||
);
|
||||
|
||||
my $story2 = $folder->addChild({ className => 'WebGUI::Asset::Story', title => 'Story 2', keywords => "roger,foxtrot"});
|
||||
my $story3 = $folder->addChild({ className => 'WebGUI::Asset::Story', title => 'Story 3', keywords => "foxtrot,echo"});
|
||||
my $story4 = $folder->addChild({ className => 'WebGUI::Asset::Story', title => 'Story 4', keywords => "roger,echo"});
|
||||
foreach my $storilet ($story2, $story3, $story4) {
|
||||
$session->db->write("update asset set creationDate=$now where assetId=?",[$storilet->getId]);
|
||||
}
|
||||
$archive->update({storiesPerPage => 3});
|
||||
|
||||
##Don't assume that Admin and Visitor have the same timezone.
|
||||
$session->user({userId => 3});
|
||||
($wgBdayMorn,undef) = $session->datetime->dayStartEnd($wgBday);
|
||||
|
||||
$templateVars = $archive->viewTemplateVariables();
|
||||
KEY: foreach my $key (keys %{ $templateVars }) {
|
||||
next KEY if isIn($key, qw/canPostStories addStoryUrl date_loop/);
|
||||
delete $templateVars->{$key};
|
||||
}
|
||||
|
||||
cmp_deeply(
|
||||
$templateVars,
|
||||
{
|
||||
canPostStories => 1,
|
||||
addStoryUrl => '/home/mystories?func=add;class=WebGUI::Asset::Story',
|
||||
date_loop => [
|
||||
{
|
||||
epochDate => ignore(),
|
||||
story_loop => [
|
||||
{
|
||||
creationDate => ignore(),
|
||||
url => re('first-story'),
|
||||
title => 'First Story',
|
||||
},
|
||||
{
|
||||
creationDate => ignore(),
|
||||
url => ignore(),
|
||||
title => 'Story 2',
|
||||
},
|
||||
{
|
||||
creationDate => ignore(),
|
||||
url => ignore(),
|
||||
title => 'Story 3',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
'viewTemplateVariables: returns expected template variables with several stories in 3 different folders'
|
||||
);
|
||||
|
||||
TODO: {
|
||||
local $TODO = "viewTemplateVariables code to write";
|
||||
ok(0, 'Check that Stories from the future are not displayed unless the user canEdit this StoryArchive');
|
||||
}
|
||||
|
||||
################################################################
|
||||
#
|
||||
# viewTemplateVariables, keywords search mode
|
||||
#
|
||||
################################################################
|
||||
|
||||
$session->request->setup_body({ keyword => 'foxtrot' } );
|
||||
$archive->update({storiesPerPage => 25});
|
||||
|
||||
$templateVars = $archive->viewTemplateVariables('keyword');
|
||||
is($templateVars->{mode}, 'keyword', 'viewTemplateVariables mode == keyword');
|
||||
cmp_deeply(
|
||||
$templateVars->{date_loop},
|
||||
[
|
||||
{
|
||||
epochDate => ignore(),
|
||||
story_loop => [
|
||||
{
|
||||
creationDate => ignore(),
|
||||
url => ignore(),
|
||||
title => 'Story 2',
|
||||
},
|
||||
{
|
||||
creationDate => ignore(),
|
||||
url => ignore(),
|
||||
title => 'Story 3',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
epochDate => $wgBdayMorn,
|
||||
story_loop => [
|
||||
{
|
||||
creationDate => ignore(),
|
||||
url => ignore(),
|
||||
title => 'WebGUI is released',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
'viewTemplateVariables: keyword mode returns the correct assets in the same form as view mode'
|
||||
);
|
||||
|
||||
$archive->update({storiesPerPage => 3});
|
||||
|
||||
$session->request->setup_body({ } );
|
||||
|
||||
################################################################
|
||||
#
|
||||
# viewTemplateVariables, search mode
|
||||
#
|
||||
################################################################
|
||||
|
||||
$session->request->setup_body({ query => 'echo' } );
|
||||
$archive->update({storiesPerPage => 25});
|
||||
$templateVars = $archive->viewTemplateVariables('search');
|
||||
is($templateVars->{mode}, 'search', 'viewTemplateVariables mode == search');
|
||||
|
||||
cmp_bag(
|
||||
$templateVars->{date_loop},
|
||||
[
|
||||
{
|
||||
epochDate => ignore(),
|
||||
story_loop => [
|
||||
{
|
||||
creationDate => ignore(),
|
||||
url => ignore(),
|
||||
title => 'Story 3',
|
||||
},
|
||||
{
|
||||
creationDate => ignore(),
|
||||
url => ignore(),
|
||||
title => 'Story 4',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
epochDate => $wgBdayMorn,
|
||||
story_loop => [
|
||||
{
|
||||
creationDate => ignore(),
|
||||
url => ignore(),
|
||||
title => 'WebGUI is released',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
'viewTemplateVariables: search mode returns the correct assets in the same form as view mode'
|
||||
);
|
||||
|
||||
################################################################
|
||||
#
|
||||
# viewTemplateVariables, export mode
|
||||
#
|
||||
################################################################
|
||||
|
||||
|
||||
$session->scratch->set('isExporting', 1);
|
||||
$archive->update({ storiesPerPage => 3, });
|
||||
$templateVars = $archive->viewTemplateVariables();
|
||||
ok( ( !exists $templateVars->{searchHeader}
|
||||
&& !exists $templateVars->{searchForm}
|
||||
&& !exists $templateVars->{searchButton}
|
||||
&& !exists $templateVars->{searchForm}
|
||||
),
|
||||
'... export mode, no search variables present'
|
||||
);
|
||||
|
||||
cmp_deeply(
|
||||
$templateVars,
|
||||
superhashof({
|
||||
rssUrl => $archive->getStaticRssFeedUrl,
|
||||
atomUrl => $archive->getStaticAtomFeedUrl,
|
||||
}),
|
||||
'... export mode, RSS and Atom feed template variables show the static url'
|
||||
);
|
||||
|
||||
my $storyCount = 0;
|
||||
foreach my $date_loop (@{ $templateVars->{date_loop} }) {
|
||||
$storyCount += scalar @{ $date_loop->{story_loop} };
|
||||
}
|
||||
|
||||
cmp_ok($storyCount, '>', 3, '... export mode, pagination increased beyond storiesPerPage');
|
||||
|
||||
$session->scratch->delete('isExporting');
|
||||
|
||||
################################################################
|
||||
#
|
||||
# tagCloud template variable in view
|
||||
#
|
||||
################################################################
|
||||
|
||||
$templateVars = $archive->viewTemplateVariables();
|
||||
my @anchors = simpleHrefParser($templateVars->{keywordCloud});
|
||||
my @expectedAnchors = ();
|
||||
foreach my $keyword(qw/echo foxtrot roger/) {
|
||||
push @expectedAnchors, [ $keyword, '/home/mystories?func=view;keyword='.$keyword ];
|
||||
}
|
||||
cmp_bag(
|
||||
\@anchors,
|
||||
\@expectedAnchors,
|
||||
'keywordCloud template variable has keywords and correct links',
|
||||
);
|
||||
|
||||
################################################################
|
||||
#
|
||||
# tagCloud template variable in view, exportMode
|
||||
#
|
||||
################################################################
|
||||
|
||||
$session->scratch->set('isExporting', 1);
|
||||
|
||||
$templateVars = $archive->viewTemplateVariables();
|
||||
@anchors = simpleHrefParser($templateVars->{keywordCloud});
|
||||
@expectedAnchors = ();
|
||||
foreach my $keyword(qw/echo foxtrot roger/) {
|
||||
push @expectedAnchors, [ $keyword, '/home/mystories/keyword_'.$keyword.'.html' ];
|
||||
}
|
||||
cmp_bag(
|
||||
\@anchors,
|
||||
\@expectedAnchors,
|
||||
'... keywordCloud template variable has keywords and correct links in export mode',
|
||||
);
|
||||
|
||||
$session->scratch->delete('isExporting');
|
||||
|
||||
################################################################
|
||||
#
|
||||
# RSS and Atom checks
|
||||
#
|
||||
################################################################
|
||||
|
||||
is($archive->getRssFeedUrl, '/home/mystories?func=viewRss', 'RSS Feed Url');
|
||||
is($archive->getAtomFeedUrl, '/home/mystories?func=viewAtom', 'Atom Feed Url');
|
||||
|
||||
$archive->update({itemsPerFeed => 3});
|
||||
|
||||
cmp_deeply(
|
||||
$archive->getRssFeedItems(),
|
||||
[
|
||||
{
|
||||
title => 'First Story',
|
||||
description => ignore(),
|
||||
'link' => ignore(),
|
||||
date => ignore(),
|
||||
author => ignore(),
|
||||
},
|
||||
{
|
||||
title => 'Story 2',
|
||||
description => ignore(),
|
||||
'link' => ignore(),
|
||||
date => ignore(),
|
||||
author => ignore(),
|
||||
},
|
||||
{
|
||||
title => 'Story 3',
|
||||
description => ignore(),
|
||||
'link' => ignore(),
|
||||
date => ignore(),
|
||||
author => ignore(),
|
||||
},
|
||||
],
|
||||
'rssFeedItems'
|
||||
);
|
||||
|
||||
################################################################
|
||||
#
|
||||
# export Collateral tests
|
||||
#
|
||||
################################################################
|
||||
|
||||
my $exportStorage = WebGUI::Storage->create($session);
|
||||
WebGUI::Test->storagesToDelete($exportStorage);
|
||||
my $basedir = Path::Class::Dir->new($exportStorage->getPath);
|
||||
$exportStorage->addFileFromScalar('index', 'export story archive content');
|
||||
my $assetDir = $basedir->subdir('mystories');
|
||||
my $assetFile = $assetDir->file('index.html');
|
||||
mkpath($assetDir->stringify);
|
||||
mv($exportStorage->getPath('index'), $assetFile->stringify);
|
||||
$archive->exportAssetCollateral($assetFile, {}, $session);
|
||||
|
||||
my $exportedFiles = $exportStorage->getFiles();
|
||||
cmp_bag(
|
||||
$exportedFiles,
|
||||
[qw/
|
||||
mystories.rss mystories
|
||||
mystories.atom
|
||||
/],
|
||||
'exportAssetCollateral: feed files exported'
|
||||
);
|
||||
|
||||
cmp_bag(
|
||||
[ map { $_->relative($assetDir)->stringify } $assetDir->children ],
|
||||
[qw/
|
||||
keyword_echo.html
|
||||
keyword_roger.html
|
||||
keyword_foxtrot.html
|
||||
index.html
|
||||
/],
|
||||
'exportAssetCollateral: keyword files exported into correct dir (below the asset)'
|
||||
);
|
||||
|
||||
my $roger = $exportStorage->getFileContentsAsScalar('mystories/keyword_roger.html');
|
||||
my @rogerStories = map { $_->[0] } fetchKeywordAssetList($roger);
|
||||
cmp_bag(
|
||||
\@rogerStories,
|
||||
[
|
||||
'Story 2',
|
||||
'Story 4',
|
||||
'WebGUI is released',
|
||||
],
|
||||
'... contents of roger keyword file'
|
||||
);
|
||||
|
||||
my $foxtrot = $exportStorage->getFileContentsAsScalar('mystories/keyword_foxtrot.html');
|
||||
my @foxtrotStories = map { $_->[0] } fetchKeywordAssetList($foxtrot);
|
||||
cmp_bag(
|
||||
\@foxtrotStories,
|
||||
[
|
||||
'Story 2',
|
||||
'Story 3',
|
||||
'WebGUI is released',
|
||||
],
|
||||
'... contents of foxtrot keyword file'
|
||||
);
|
||||
|
||||
my $echo = $exportStorage->getFileContentsAsScalar('mystories/keyword_echo.html');
|
||||
my @echoStories = map { $_->[0] } fetchKeywordAssetList($echo);
|
||||
cmp_bag(
|
||||
\@echoStories,
|
||||
[
|
||||
'Story 3',
|
||||
'Story 4',
|
||||
'WebGUI is released',
|
||||
],
|
||||
'... contents of echo keyword file'
|
||||
);
|
||||
|
||||
################################################################
|
||||
#
|
||||
# getKeywordStaticURL
|
||||
#
|
||||
################################################################
|
||||
|
||||
is($archive->getKeywordStaticURL('foo'), '/home/mystories/keyword_foo.html', 'getKeywordStaticURL: returns absolute URL to keyword file');
|
||||
|
||||
$archive->update({ url => '/home/mystories.arch' });
|
||||
is($archive->getKeywordStaticURL('bar'), '/home/mystories/keyword_bar.html', '... correct URL with file extension');
|
||||
|
||||
$archive->update({ url => '/home/mystories' });
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Cleanup
|
||||
END {
|
||||
if (defined $archive and ref $archive eq $class) {
|
||||
$archive->purge;
|
||||
}
|
||||
if ($versionTag) {
|
||||
$versionTag->rollback;
|
||||
}
|
||||
foreach my $user ($editor, $reporter, $reader) {
|
||||
$user->delete if defined $user;
|
||||
}
|
||||
$creationDateSth->finish;
|
||||
}
|
||||
|
||||
sub simpleHrefParser {
|
||||
my ($text) = @_;
|
||||
my $p = HTML::TokeParser->new(\$text);
|
||||
my @anchors = ();
|
||||
while (my $token = $p->get_tag('a')) {
|
||||
my $url = $token->[1]{href} || "-";
|
||||
my $label = $p->get_trimmed_text("/a");
|
||||
push @anchors, [ $label, $url ];
|
||||
}
|
||||
return @anchors;
|
||||
}
|
||||
|
||||
sub fetchKeywordAssetList {
|
||||
my ($text) = @_;
|
||||
my @anchors = ();
|
||||
my $p = HTML::TokeParser->new(\$text);
|
||||
TOKEN: while (my $token = $p->get_tag('ul')) {
|
||||
next TOKEN unless $token->[1]->{class} eq 'keywordAssetList';
|
||||
while (my $token = $p->get_tag('/ul', 'a')) {
|
||||
last TOKEN if $token->[0] eq '/ul';
|
||||
my $url = $token->[1]{href} || "-";
|
||||
my $label = $p->get_trimmed_text("/a");
|
||||
push @anchors, [ $label, $url ];
|
||||
}
|
||||
}
|
||||
|
||||
return @anchors;
|
||||
}
|
||||
|
||||
|
||||
323
t/Asset/Wobject/StoryTopic.t
Normal file
323
t/Asset/Wobject/StoryTopic.t
Normal file
|
|
@ -0,0 +1,323 @@
|
|||
# vim:syntax=perl
|
||||
#-------------------------------------------------------------------
|
||||
# WebGUI is Copyright 2001-2009 Plain Black Corporation.
|
||||
#-------------------------------------------------------------------
|
||||
# Please read the legal notices (docs/legal.txt) and the license
|
||||
# (docs/license.txt) that came with this distribution before using
|
||||
# this software.
|
||||
#------------------------------------------------------------------
|
||||
# http://www.plainblack.com info@plainblack.com
|
||||
#------------------------------------------------------------------
|
||||
|
||||
# Write a little about what this script tests.
|
||||
#
|
||||
#
|
||||
|
||||
use FindBin;
|
||||
use strict;
|
||||
use lib "$FindBin::Bin/../../lib";
|
||||
use Test::More;
|
||||
use Test::Deep;
|
||||
use Data::Dumper;
|
||||
|
||||
use WebGUI::Test; # Must use this before any other WebGUI modules
|
||||
use WebGUI::Session;
|
||||
use WebGUI::Text;
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Init
|
||||
my $session = WebGUI::Test->session;
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Tests
|
||||
|
||||
my $tests = 18;
|
||||
plan tests => 1 + $tests;
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# put your tests here
|
||||
|
||||
my $class = 'WebGUI::Asset::Wobject::StoryTopic';
|
||||
my $loaded = use_ok($class);
|
||||
|
||||
my $versionTag = WebGUI::VersionTag->getWorking($session);
|
||||
|
||||
my $archive = WebGUI::Asset->getDefault($session)->addChild({className => 'WebGUI::Asset::Wobject::StoryArchive', title => 'My Stories', url => '/home/mystories'});
|
||||
|
||||
my $now = time();
|
||||
my $nowFolder = $archive->getFolder($now);
|
||||
|
||||
my $yesterday = $now-24*3600;
|
||||
my $newFolder = $archive->getFolder($yesterday);
|
||||
|
||||
my $creationDateSth = $session->db->prepare('update asset set creationDate=? where assetId=?');
|
||||
|
||||
my $pastStory = $newFolder->addChild({ className => 'WebGUI::Asset::Story', title => "Yesterday is history", keywords => 'andy,norton'});
|
||||
$creationDateSth->execute([$yesterday, $pastStory->getId]);
|
||||
|
||||
my @staff = qw/norton hadley mert trout/;
|
||||
my @inmates = qw/bogs red brooks andy heywood tommy jake skeet/;
|
||||
my @characters = (@staff, @inmates, );
|
||||
|
||||
my @stories = ();
|
||||
my $storyHandler = {};
|
||||
|
||||
STORY: foreach my $name (@characters) {
|
||||
my $namedStory = $nowFolder->addChild({ className => 'WebGUI::Asset::Story', title => $name, keywords => $name, } );
|
||||
$storyHandler->{$name} = $namedStory;
|
||||
$creationDateSth->execute([$now, $namedStory->getId]);
|
||||
}
|
||||
|
||||
$storyHandler->{bogs}->update({subtitle => 'drinking his food through a straw'});
|
||||
|
||||
my $topic;
|
||||
|
||||
SKIP: {
|
||||
|
||||
skip "Unable to load module $class", $tests unless $loaded;
|
||||
|
||||
$topic = WebGUI::Asset->getDefault($session)->addChild({ className => 'WebGUI::Asset::Wobject::StoryTopic', title => 'Popular inmates in Shawshank Prison', keywords => join(',', @inmates)});
|
||||
|
||||
isa_ok($topic, 'WebGUI::Asset::Wobject::StoryTopic', 'made a Story Topic');
|
||||
$topic->update({
|
||||
storiesPer => 6,
|
||||
storiesShort => 3,
|
||||
});
|
||||
|
||||
$versionTag->commit;
|
||||
|
||||
################################################################
|
||||
#
|
||||
# viewTemplateVariables
|
||||
#
|
||||
################################################################
|
||||
|
||||
my $templateVars;
|
||||
$templateVars = $topic->viewTemplateVariables();
|
||||
|
||||
cmp_deeply(
|
||||
$templateVars,
|
||||
superhashof({
|
||||
rssUrl => $topic->getRssFeedUrl,
|
||||
atomUrl => $topic->getAtomFeedUrl,
|
||||
}),
|
||||
'viewTemplateVars: RSS and Atom feed template variables'
|
||||
);
|
||||
cmp_deeply(
|
||||
$templateVars->{story_loop},
|
||||
[
|
||||
{
|
||||
title => 'bogs',
|
||||
url => $session->url->append($topic->getUrl, 'func=viewStory;assetId='.$storyHandler->{'bogs'}->getId),
|
||||
creationDate => $now,
|
||||
},
|
||||
{
|
||||
title => 'red',
|
||||
url => $session->url->append($topic->getUrl, 'func=viewStory;assetId='.$storyHandler->{'red'}->getId),
|
||||
creationDate => $now,
|
||||
},
|
||||
{
|
||||
title => 'brooks',
|
||||
url => $session->url->append($topic->getUrl, 'func=viewStory;assetId='.$storyHandler->{'brooks'}->getId),
|
||||
creationDate => $now,
|
||||
},
|
||||
],
|
||||
'viewTemplateVars has right number and contents in the story_loop'
|
||||
);
|
||||
|
||||
ok(
|
||||
! exists $templateVars->{topStoryTitle}
|
||||
&& ! exists $templateVars->{topStoryUrl}
|
||||
&& ! exists $templateVars->{topStoryCreationDate}
|
||||
&& ! exists $templateVars->{topStorySubtitle},
|
||||
'topStory variables not present unless in standalone mode'
|
||||
);
|
||||
ok(! $templateVars->{standAlone}, 'viewTemplateVars: not in standalone mode');
|
||||
|
||||
$topic->{_standAlone} = 1;
|
||||
$templateVars = $topic->viewTemplateVariables();
|
||||
cmp_deeply(
|
||||
$templateVars->{story_loop},
|
||||
[
|
||||
{
|
||||
title => 'red',
|
||||
url => $session->url->append($topic->getUrl, 'func=viewStory;assetId='.$storyHandler->{'red'}->getId),
|
||||
creationDate => $now,
|
||||
},
|
||||
{
|
||||
title => 'brooks',
|
||||
url => $session->url->append($topic->getUrl, 'func=viewStory;assetId='.$storyHandler->{'brooks'}->getId),
|
||||
creationDate => $now,
|
||||
},
|
||||
{
|
||||
title => 'andy',
|
||||
url => $session->url->append($topic->getUrl, 'func=viewStory;assetId='.$storyHandler->{'andy'}->getId),
|
||||
creationDate => $now,
|
||||
},
|
||||
{
|
||||
title => 'heywood',
|
||||
url => $session->url->append($topic->getUrl, 'func=viewStory;assetId='.$storyHandler->{'heywood'}->getId),
|
||||
creationDate => $now,
|
||||
},
|
||||
{
|
||||
title => 'tommy',
|
||||
url => $session->url->append($topic->getUrl, 'func=viewStory;assetId='.$storyHandler->{'tommy'}->getId),
|
||||
creationDate => $now,
|
||||
},
|
||||
],
|
||||
'viewTemplateVars has right number and contents in the story_loop in standalone mode. Top story not present in story_loop'
|
||||
);
|
||||
|
||||
is($templateVars->{topStoryTitle}, 'bogs', '... topStoryTitle');
|
||||
is(
|
||||
$templateVars->{topStorySubtitle},
|
||||
'drinking his food through a straw',
|
||||
'... topStorySubtitle'
|
||||
);
|
||||
is(
|
||||
$templateVars->{topStoryUrl},
|
||||
$session->url->append($topic->getUrl, 'func=viewStory;assetId='.$storyHandler->{'bogs'}->getId),
|
||||
'... topStoryUrl'
|
||||
);
|
||||
is($templateVars->{topStoryCreationDate}, $now, '... topStoryCreationDate');
|
||||
ok($templateVars->{standAlone}, '... standAlone mode=1');
|
||||
|
||||
my $storage = WebGUI::Storage->create($session);
|
||||
WebGUI::Test->storagesToDelete($storage);
|
||||
$storyHandler->{bogs}->setPhotoData([{
|
||||
caption => "Octopus seen at the scene of Mrs. Dufresne's murder.",
|
||||
byLine => 'Elmo Blatch',
|
||||
alt => 'The suspect',
|
||||
}]);
|
||||
|
||||
$templateVars = $topic->viewTemplateVariables();
|
||||
ok(
|
||||
! exists $templateVars->{topStoryImageUrl}
|
||||
&& ! exists $templateVars->{topStoryImageByLine}
|
||||
&& ! exists $templateVars->{topStoryImageAlt}
|
||||
&& ! exists $templateVars->{topStoryImageCaption},
|
||||
'... no photo template variables, since there is no storage location'
|
||||
);
|
||||
my $bogsData = $storyHandler->{bogs}->getPhotoData();
|
||||
$bogsData->[0]->{storageId} = $storage->getId;
|
||||
$storyHandler->{bogs}->setPhotoData($bogsData);
|
||||
$templateVars = $topic->viewTemplateVariables();
|
||||
ok(
|
||||
! exists $templateVars->{topStoryImageUrl}
|
||||
&& ! exists $templateVars->{topStoryImageByLine}
|
||||
&& ! exists $templateVars->{topStoryImageAlt}
|
||||
&& ! exists $templateVars->{topStoryImageCaption},
|
||||
'... no photo template variables, since there is no file in the storage location'
|
||||
);
|
||||
|
||||
$storage->addFileFromFilesystem(WebGUI::Test->getTestCollateralPath('gooey.jpg'));
|
||||
$templateVars = $topic->viewTemplateVariables();
|
||||
cmp_deeply(
|
||||
[ @{ $templateVars }{qw/topStoryImageUrl topStoryImageByline topStoryImageAlt topStoryImageCaption/} ],
|
||||
[
|
||||
$storage->getUrl('gooey.jpg'),
|
||||
'Elmo Blatch',
|
||||
'The suspect',
|
||||
"Octopus seen at the scene of Mrs. Dufresne's murder.",
|
||||
],
|
||||
'... photo template variables set'
|
||||
);
|
||||
|
||||
$topic->update({
|
||||
storiesShort => 20,
|
||||
});
|
||||
|
||||
$topic->{_standAlone} = 0;
|
||||
|
||||
$templateVars = $topic->viewTemplateVariables;
|
||||
my @topicInmates = map { $_->{title} } @{ $templateVars->{story_loop} };
|
||||
cmp_deeply(
|
||||
\@topicInmates,
|
||||
[@inmates, 'Yesterday is history'], #extra for pastStory
|
||||
'viewTemplateVariables: is only finding things with its keywords'
|
||||
);
|
||||
|
||||
$session->scratch->set('isExporting', 1);
|
||||
$topic->update({
|
||||
storiesShort => 3,
|
||||
});
|
||||
$templateVars = $topic->viewTemplateVariables;
|
||||
cmp_deeply(
|
||||
$templateVars->{story_loop},
|
||||
[
|
||||
{
|
||||
title => 'bogs',
|
||||
url => $storyHandler->{'bogs'}->getUrl,
|
||||
creationDate => $now,
|
||||
},
|
||||
{
|
||||
title => 'red',
|
||||
url => $storyHandler->{'red'}->getUrl,
|
||||
creationDate => $now,
|
||||
},
|
||||
{
|
||||
title => 'brooks',
|
||||
url => $storyHandler->{'brooks'}->getUrl,
|
||||
creationDate => $now,
|
||||
},
|
||||
],
|
||||
'... export mode, URLs are the regular story URLs'
|
||||
);
|
||||
cmp_deeply(
|
||||
$templateVars,
|
||||
superhashof({
|
||||
rssUrl => $topic->getStaticRssFeedUrl,
|
||||
atomUrl => $topic->getStaticAtomFeedUrl,
|
||||
}),
|
||||
'... export mode, RSS and Atom feed template variables show the static url'
|
||||
);
|
||||
$session->scratch->delete('isExporting');
|
||||
|
||||
################################################################
|
||||
#
|
||||
# getRssFeedItems
|
||||
#
|
||||
################################################################
|
||||
|
||||
$topic->update({
|
||||
storiesPer => 3,
|
||||
});
|
||||
cmp_deeply(
|
||||
$topic->getRssFeedItems(),
|
||||
[
|
||||
{
|
||||
title => 'bogs',
|
||||
description => ignore(),
|
||||
'link' => ignore(),
|
||||
date => ignore(),
|
||||
author => ignore(),
|
||||
},
|
||||
{
|
||||
title => 'red',
|
||||
description => ignore(),
|
||||
'link' => ignore(),
|
||||
date => ignore(),
|
||||
author => ignore(),
|
||||
},
|
||||
{
|
||||
title => 'brooks',
|
||||
description => ignore(),
|
||||
'link' => ignore(),
|
||||
date => ignore(),
|
||||
author => ignore(),
|
||||
},
|
||||
],
|
||||
'rssFeedItems'
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Cleanup
|
||||
END {
|
||||
$archive->purge if $archive;
|
||||
$topic->purge if $topic;
|
||||
if ($versionTag) {
|
||||
$versionTag->rollback;
|
||||
}
|
||||
}
|
||||
192
t/AssetAspect/RssFeed.t
Normal file
192
t/AssetAspect/RssFeed.t
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
# vim:syntax=perl
|
||||
#-------------------------------------------------------------------
|
||||
# WebGUI is Copyright 2001-2009 Plain Black Corporation.
|
||||
#-------------------------------------------------------------------
|
||||
# Please read the legal notices (docs/legal.txt) and the license
|
||||
# (docs/license.txt) that came with this distribution before using
|
||||
# this software.
|
||||
#------------------------------------------------------------------
|
||||
# http://www.plainblack.com info@plainblack.com
|
||||
#------------------------------------------------------------------
|
||||
|
||||
# Write a little about what this script tests.
|
||||
#
|
||||
#
|
||||
|
||||
use FindBin;
|
||||
use strict;
|
||||
use lib "$FindBin::Bin/../lib";
|
||||
use Test::More;
|
||||
use Test::Deep;
|
||||
use File::Path;
|
||||
use Data::Dumper;
|
||||
|
||||
use WebGUI::Test; # Must use this before any other WebGUI modules
|
||||
use WebGUI::Session;
|
||||
use WebGUI::Asset;
|
||||
|
||||
use WebGUI::Asset::RssAspectDummy;
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Init
|
||||
my $session = WebGUI::Test->session;
|
||||
|
||||
WebGUI::Test->originalConfig('exportPath');
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Tests
|
||||
|
||||
plan tests => 21; # Increment this number for each test you create
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# put your tests here
|
||||
|
||||
my $dummy = WebGUI::Asset->getDefault($session)->addChild({
|
||||
className => 'WebGUI::Asset::RssAspectDummy',
|
||||
url => '/home/shawshank',
|
||||
title => 'Dummy Title',
|
||||
synopsis => 'Dummy Synopsis',
|
||||
description => 'Dummy Description',
|
||||
});
|
||||
|
||||
#####################################################
|
||||
#
|
||||
# get URLs
|
||||
#
|
||||
#####################################################
|
||||
|
||||
can_ok($dummy, qw/getAtomFeedUrl getRssFeedUrl getRdfFeedUrl/);
|
||||
|
||||
is($dummy->getAtomFeedUrl, '/home/shawshank?func=viewAtom', 'getAtomFeedUrl');
|
||||
is($dummy->getRssFeedUrl, '/home/shawshank?func=viewRss', 'getRssFeedUrl');
|
||||
is($dummy->getRdfFeedUrl, '/home/shawshank?func=viewRdf', 'getRdfFeedUrl');
|
||||
|
||||
can_ok($dummy, qw/getStaticAtomFeedUrl getStaticRssFeedUrl getStaticRdfFeedUrl/);
|
||||
|
||||
is($dummy->getStaticAtomFeedUrl, '/home/shawshank.atom', 'getStaticAtomFeedUrl');
|
||||
is($dummy->getStaticRssFeedUrl, '/home/shawshank.rss', 'getStaticRssFeedUrl');
|
||||
is($dummy->getStaticRdfFeedUrl, '/home/shawshank.rdf', 'getStaticRdfFeedUrl');
|
||||
|
||||
$dummy->update({ url => 'dot.extension', });
|
||||
|
||||
is($dummy->getAtomFeedUrl, '/dot.extension?func=viewAtom', 'getAtomFeedUrl, url with extension');
|
||||
is($dummy->getRssFeedUrl, '/dot.extension?func=viewRss', 'getRssFeedUrl, url with extension');
|
||||
is($dummy->getRdfFeedUrl, '/dot.extension?func=viewRdf', 'getRdfFeedUrl, url with extension');
|
||||
|
||||
is($dummy->getStaticAtomFeedUrl, '/dot.extension.atom', 'getStaticAtomFeedUrl, url with extension');
|
||||
is($dummy->getStaticRssFeedUrl, '/dot.extension.rss', 'getStaticRssFeedUrl, url with extension');
|
||||
is($dummy->getStaticRdfFeedUrl, '/dot.extension.rdf', 'getStaticRdfFeedUrl, url with extension');
|
||||
|
||||
$dummy->update({ url => '/home/shawshank', });
|
||||
|
||||
#####################################################
|
||||
#
|
||||
# getFeed
|
||||
#
|
||||
#####################################################
|
||||
|
||||
my $feed = XML::FeedPP::RSS->new();
|
||||
|
||||
my $newFeed = $dummy->getFeed($feed);
|
||||
|
||||
isa_ok($newFeed, 'XML::FeedPP::RSS');
|
||||
is($newFeed, $feed, 'getFeed returns the same object');
|
||||
cmp_deeply(
|
||||
$feed,
|
||||
methods(
|
||||
title => 'Dummy Title',
|
||||
description => 'Dummy Synopsis', ##Not description
|
||||
link => '/home/shawshank',
|
||||
copyright => '',
|
||||
),
|
||||
'... title, description, link inherit from asset by default, copyright unset'
|
||||
);
|
||||
cmp_bag(
|
||||
[ $feed->get_item() ],
|
||||
[
|
||||
methods(
|
||||
title => 'this title',
|
||||
description => 'this description',
|
||||
'link' => 'this link',
|
||||
guid => 'this link',
|
||||
),
|
||||
methods(
|
||||
title => 'another title',
|
||||
description => 'another description', ##Not description
|
||||
guid => re('^[a-zA-Z0-9\-_]{22}'), ##GUID is a GUID since there's no link
|
||||
),
|
||||
|
||||
],
|
||||
'... contains 2 feed items with the correct contents'
|
||||
);
|
||||
|
||||
$dummy->update({
|
||||
feedCopyright => 'copyright 2009 Plain Black Corporation',
|
||||
feedTitle => 'Rita Hayworth and the Shawshank Redemption',
|
||||
feedDescription => 'A good movie, providing loads of testing collateral',
|
||||
});
|
||||
$feed = $dummy->getFeed(XML::FeedPP::RSS->new());
|
||||
|
||||
cmp_deeply(
|
||||
$feed,
|
||||
methods(
|
||||
title => 'Rita Hayworth and the Shawshank Redemption',
|
||||
description => 'A good movie, providing loads of testing collateral',
|
||||
link => '/home/shawshank',
|
||||
copyright => 'copyright 2009 Plain Black Corporation',
|
||||
),
|
||||
'... feed settings override asset defaults, copyright'
|
||||
);
|
||||
|
||||
#####################################################
|
||||
#
|
||||
# exportAssetCollateral
|
||||
#
|
||||
#####################################################
|
||||
|
||||
my $exportStorage = WebGUI::Storage->create($session);
|
||||
WebGUI::Test->storagesToDelete($exportStorage);
|
||||
my $basedir = Path::Class::Dir->new($exportStorage->getPath);
|
||||
my $assetdir = $basedir->subdir('shawshank');
|
||||
my $indexfile = $assetdir->file('index.html');
|
||||
mkpath($assetdir->stringify);
|
||||
$dummy->exportAssetCollateral($indexfile, {}, $session);
|
||||
|
||||
cmp_bag(
|
||||
$exportStorage->getFiles(),
|
||||
[qw/
|
||||
shawshank.rss shawshank
|
||||
shawshank.atom
|
||||
/],
|
||||
'exportAssetCollateral: feed files exported, index.html file'
|
||||
);
|
||||
|
||||
$exportStorage = WebGUI::Storage->create($session);
|
||||
WebGUI::Test->storagesToDelete($exportStorage);
|
||||
$basedir = Path::Class::Dir->new($exportStorage->getPath);
|
||||
my $assetfile = $basedir->file('shawshank.html');
|
||||
$dummy->exportAssetCollateral($assetfile, {}, $session);
|
||||
|
||||
cmp_bag(
|
||||
$exportStorage->getFiles(),
|
||||
[qw/
|
||||
shawshank.html.rss
|
||||
shawshank.html.atom
|
||||
/],
|
||||
'exportAssetCollateral: feed files exported, shawshank.html file'
|
||||
);
|
||||
|
||||
#####################################################
|
||||
#
|
||||
# exportAssetCollateral
|
||||
#
|
||||
#####################################################
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Cleanup
|
||||
END {
|
||||
$dummy->purge;
|
||||
my $tag = WebGUI::VersionTag->getWorking($session, 'noCreate');
|
||||
$tag->rollback if $tag;
|
||||
}
|
||||
#vim:ft=perl
|
||||
28
t/HTML.t
28
t/HTML.t
|
|
@ -96,7 +96,28 @@ my @macroParamSets = (
|
|||
},
|
||||
);
|
||||
|
||||
my $numTests = scalar @filterSets + scalar @macroParamSets;
|
||||
my @htmlTextSets = (
|
||||
{
|
||||
inputText => q|I wish I could tell you that Andy fought the good fight.|,
|
||||
output => q|I wish I could tell you that Andy fought the good fight.|,
|
||||
comment => 'bare text',
|
||||
},
|
||||
{
|
||||
inputText => q|The man likes to play chess; let's get him some rocks. |,
|
||||
output => q|The man likes to play chess; let's get him some rocks.|,
|
||||
comment => 'bare text with ending space has that space removed',
|
||||
},
|
||||
{
|
||||
inputText => q|<p>Do you enjoy working in the laundry?</p>|,
|
||||
output => qq|\nDo you enjoy working in the laundry?\n|,
|
||||
comment => 'text in paragraph tag nested inside newlines',
|
||||
},
|
||||
);
|
||||
|
||||
my $numTests = scalar @filterSets
|
||||
+ scalar @macroParamSets
|
||||
+ scalar @htmlTextSets
|
||||
;
|
||||
|
||||
plan tests => $numTests;
|
||||
|
||||
|
|
@ -109,3 +130,8 @@ foreach my $testSet (@macroParamSets) {
|
|||
WebGUI::HTML::makeParameterSafe(\$testSet->{inputText});
|
||||
is($testSet->{inputText}, $testSet->{output}, $testSet->{comment});
|
||||
}
|
||||
|
||||
foreach my $testSet (@htmlTextSets) {
|
||||
my $text = WebGUI::HTML::html2text($testSet->{inputText});
|
||||
is($text, $testSet->{output}, $testSet->{comment});
|
||||
}
|
||||
|
|
|
|||
135
t/Mail/Send.t
135
t/Mail/Send.t
|
|
@ -18,6 +18,10 @@ use lib "$FindBin::Bin/../lib";
|
|||
use JSON qw( from_json to_json );
|
||||
use Test::More;
|
||||
use File::Spec;
|
||||
use Data::Dumper;
|
||||
use MIME::Parser;
|
||||
use Encode qw/decode/;
|
||||
|
||||
use WebGUI::Test;
|
||||
|
||||
use WebGUI::Mail::Send;
|
||||
|
|
@ -36,19 +40,26 @@ BEGIN {
|
|||
$hasServer = 1 unless $@;
|
||||
}
|
||||
|
||||
# See if we have an SMTP server to use
|
||||
# See if we have an SMTP server to use
|
||||
my ( $smtpd );
|
||||
my $SMTP_HOST = 'localhost';
|
||||
my $SMTP_PORT = '54921';
|
||||
if ($hasServer) {
|
||||
$session->setting->set( 'smtpServer', $SMTP_HOST . ':' . $SMTP_PORT );
|
||||
|
||||
|
||||
my $smtpd = File::Spec->catfile( WebGUI::Test->root, 't', 'smtpd.pl' );
|
||||
open MAIL, "perl $smtpd $SMTP_HOST $SMTP_PORT 5 |"
|
||||
or die "Could not open pipe to SMTPD: $!";
|
||||
sleep 1; # Give the smtpd time to establish itself
|
||||
}
|
||||
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Tests
|
||||
|
||||
plan tests => 6; # Increment this number for each test you create
|
||||
plan tests => 11; # Increment this number for each test you create
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Test create
|
||||
|
|
@ -122,6 +133,7 @@ is( $mime->parts(0)->as_string =~ m/\n/, $newlines,
|
|||
);
|
||||
|
||||
# TODO: Test that addHtml creates a body with the right content type
|
||||
my $smtpServerOk = 0;
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Test emailOverride
|
||||
|
|
@ -137,22 +149,24 @@ SKIP: {
|
|||
if ( !$hasServer ) {
|
||||
skip "Cannot test emailOverride: Module Net::SMTP::Server not loaded!", $numtests;
|
||||
}
|
||||
|
||||
|
||||
$smtpServerOk = 1;
|
||||
|
||||
# Override the emailOverride
|
||||
my $oldEmailOverride = $session->config->get('emailOverride');
|
||||
$session->config->set( 'emailOverride', 'dufresne@localhost' );
|
||||
my $oldEmailToLog = $session->config->get('emailToLog');
|
||||
$session->config->set( 'emailToLog', 0 );
|
||||
|
||||
|
||||
# Send the mail
|
||||
my $mail
|
||||
= WebGUI::Mail::Send->create( $session, {
|
||||
to => 'norton@localhost',
|
||||
} );
|
||||
$mail->addText( 'His judgement cometh and that right soon.' );
|
||||
|
||||
|
||||
my $received = sendToServer( $mail );
|
||||
|
||||
|
||||
if (!$received) {
|
||||
skip "Cannot test emailOverride: No response received from smtpd", $numtests;
|
||||
}
|
||||
|
|
@ -162,17 +176,104 @@ SKIP: {
|
|||
"Email TO: address is overridden",
|
||||
);
|
||||
|
||||
my $parser = MIME::Parser->new();
|
||||
my $parsed_message = $parser->parse_data($received->{contents});
|
||||
my $head = $parsed_message->head;
|
||||
my $messageId = decode('MIME-Header', $head->get('Message-Id'));
|
||||
like ($messageId, qr/^<WebGUI-([a-zA-Z0-9\-_]){22}@\w+\.\w{2,4}>$/, 'Message-Id is valid');
|
||||
|
||||
# Restore the emailOverride
|
||||
$session->config->set( 'emailOverride', $oldEmailOverride );
|
||||
$session->config->set( 'emailToLog', $oldEmailToLog );
|
||||
}
|
||||
|
||||
SKIP: {
|
||||
my $numtests = 4; # Number of tests in this block
|
||||
|
||||
skip "Cannot test message ids", $numtests unless $smtpServerOk;
|
||||
|
||||
# Send the mail
|
||||
my $mail
|
||||
= WebGUI::Mail::Send->create( $session, {
|
||||
to => 'norton@localhost',
|
||||
} );
|
||||
$mail->addText( "I understand you're a man who knows how to get things." );
|
||||
|
||||
my $received = sendToServer( $mail );
|
||||
|
||||
if (!$received) {
|
||||
skip "Cannot test messageIds: No response received from smtpd", $numtests;
|
||||
}
|
||||
|
||||
# Test the mail
|
||||
my $parser = MIME::Parser->new();
|
||||
my $parsed_message = $parser->parse_data($received->{contents});
|
||||
my $head = $parsed_message->head;
|
||||
my $messageId = decode('MIME-Header', $head->get('Message-Id'));
|
||||
chomp $messageId;
|
||||
like ($messageId, qr/^<WebGUI-([a-zA-Z0-9\-_]){22}@\w+\.\w{2,4}>$/, 'generated Message-Id is valid');
|
||||
|
||||
# Send the mail
|
||||
$mail
|
||||
= WebGUI::Mail::Send->create( $session, {
|
||||
to => 'norton@localhost',
|
||||
messageId => '<leadingAngleOnly@localhost.localdomain',
|
||||
} );
|
||||
$mail->addText( "What say you there, fuzzy-britches? Feel like talking?" );
|
||||
|
||||
$received = sendToServer( $mail );
|
||||
|
||||
$parsed_message = $parser->parse_data($received->{contents});
|
||||
$head = $parsed_message->head;
|
||||
$messageId = decode('MIME-Header', $head->get('Message-Id'));
|
||||
chomp $messageId;
|
||||
is($messageId, '<leadingAngleOnly@localhost.localdomain>', 'bad messageId corrected (added ending angle)');
|
||||
|
||||
# Send the mail
|
||||
$mail
|
||||
= WebGUI::Mail::Send->create( $session, {
|
||||
to => 'norton@localhost',
|
||||
messageId => 'endingAngleOnly@localhost.localdomain>',
|
||||
} );
|
||||
$mail->addText( "Dear Warden, You were right. Salvation lies within." );
|
||||
|
||||
$received = sendToServer( $mail );
|
||||
|
||||
$parsed_message = $parser->parse_data($received->{contents});
|
||||
$head = $parsed_message->head;
|
||||
$messageId = decode('MIME-Header', $head->get('Message-Id'));
|
||||
chomp $messageId;
|
||||
is($messageId, '<endingAngleOnly@localhost.localdomain>', 'bad messageId corrected (added starting angle)');
|
||||
|
||||
# Send the mail
|
||||
$mail
|
||||
= WebGUI::Mail::Send->create( $session, {
|
||||
to => 'red@localhost',
|
||||
messageId => 'noAngles@localhost.localdomain',
|
||||
} );
|
||||
$mail->addText( "Neither are they. You have to be human first. They don't qualify." );
|
||||
|
||||
$received = sendToServer( $mail );
|
||||
|
||||
$parsed_message = $parser->parse_data($received->{contents});
|
||||
$head = $parsed_message->head;
|
||||
$messageId = decode('MIME-Header', $head->get('Message-Id'));
|
||||
chomp $messageId;
|
||||
is($messageId, '<noAngles@localhost.localdomain>', 'bad messageId corrected (added both angles)');
|
||||
|
||||
}
|
||||
|
||||
# TODO: Test the emailToLog config setting
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Cleanup
|
||||
END {
|
||||
close MAIL
|
||||
or die "Could not close pipe to SMTPD: $!";
|
||||
sleep 1;
|
||||
|
||||
$session->db->write('delete from mailQueue');
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
|
|
@ -189,21 +290,17 @@ END {
|
|||
# by a MIME::Entity parser
|
||||
sub sendToServer {
|
||||
my $mail = shift;
|
||||
|
||||
my $smtpd = File::Spec->catfile( WebGUI::Test->root, 't', 'smtpd.pl' );
|
||||
open MAIL, "perl $smtpd $SMTP_HOST $SMTP_PORT |"
|
||||
or die "Could not open pipe to SMTPD: $!";
|
||||
sleep 1; # Give the smtpd time to establish itself
|
||||
|
||||
$mail->send;
|
||||
my $status = $mail->send;
|
||||
my $json;
|
||||
while ( my $line = <MAIL> ) {
|
||||
$json .= $line;
|
||||
if ($status) {
|
||||
$json = <MAIL>;
|
||||
}
|
||||
else {
|
||||
$json = ' { "error": "mail not sent" } ';
|
||||
}
|
||||
if (!$json) {
|
||||
$json = ' { "error": "error in getting mail" } ';
|
||||
}
|
||||
|
||||
close MAIL
|
||||
or die "Could not close pipe to SMTPD: $!";
|
||||
|
||||
return from_json( $json );
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,19 +17,23 @@ use FindBin;
|
|||
use strict;
|
||||
use lib "$FindBin::Bin/lib";
|
||||
use Test::More;
|
||||
use Data::Dumper;
|
||||
use WebGUI::Test; # Must use this before any other WebGUI modules
|
||||
use WebGUI::Session;
|
||||
use WebGUI::Form::Text;
|
||||
use WebGUI::Form::HTMLArea;
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Init
|
||||
my $session = WebGUI::Test->session;
|
||||
|
||||
my $newUser = WebGUI::User->create( $session );
|
||||
WebGUI::Test->usersToDelete($newUser);
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Tests
|
||||
|
||||
plan tests => 17; # Increment this number for each test you create
|
||||
plan tests => 20; # Increment this number for each test you create
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Test the creation of ProfileField
|
||||
|
|
@ -66,19 +70,42 @@ like( $ff, qr/value="$ffvalue"[^>]+selected/, 'html returned contains value, uiL
|
|||
$ff = undef;
|
||||
$ffvalue = undef;
|
||||
ok( $ff = $aliasField->formField(undef, undef, $newUser), 'formField method returns something, alias field, defaulted user' );
|
||||
my $ffvalue = $newUser->profileField('alias');
|
||||
$ffvalue = $newUser->profileField('alias');
|
||||
like( $ff, qr/$ffvalue/, 'html returned contains value, alias field, defaulted user' );
|
||||
|
||||
$ff = undef;
|
||||
$ffvalue = undef;
|
||||
ok( $ff = $uilevelField->formField(undef, undef, $newUser), 'formField method returns something, uiLevel field, defaulted user' );
|
||||
my $ffvalue = $newUser->profileField('uiLevel');
|
||||
$ffvalue = $newUser->profileField('uiLevel');
|
||||
like( $ff, qr/$ffvalue/, 'html returned contains value, uiLevel field, defaulted user' );
|
||||
|
||||
#########################################################
|
||||
#
|
||||
# set, changing fieldTypes
|
||||
#
|
||||
#########################################################
|
||||
|
||||
my $newProfileField = WebGUI::ProfileField->create($session, 'testField', {
|
||||
fieldType => 'Text',
|
||||
label => 'Test Field',
|
||||
});
|
||||
|
||||
my $textFieldType = lc WebGUI::Form::Text->getDatabaseFieldType();
|
||||
my $htmlFieldType = lc WebGUI::Form::HTMLArea->getDatabaseFieldType();
|
||||
|
||||
my $fieldSpec = $session->db->quickHashRef('describe userProfileData testField');
|
||||
is (lc $fieldSpec->{Type}, $textFieldType, 'test field created with correct type for text field');
|
||||
|
||||
$newProfileField->set({ fieldType => 'HTMLArea' });
|
||||
is($newProfileField->get('fieldType'), 'HTMLArea', 'test field updated to HTMLArea');
|
||||
|
||||
$fieldSpec = $session->db->quickHashRef('describe userProfileData testField');
|
||||
is (lc $fieldSpec->{Type}, $htmlFieldType, 'database updated along with profile field object');
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
# Cleanup
|
||||
END {
|
||||
$newUser->delete;
|
||||
$newProfileField->delete;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
127
t/Workflow/Activity/ArchiveOldStories.t
Normal file
127
t/Workflow/Activity/ArchiveOldStories.t
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
#-------------------------------------------------------------------
|
||||
# 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 FindBin;
|
||||
use strict;
|
||||
use lib "$FindBin::Bin/../../lib";
|
||||
|
||||
use WebGUI::Test;
|
||||
use WebGUI::Asset;
|
||||
use WebGUI::Asset::Story;
|
||||
use WebGUI::Asset::Wobject::StoryArchive;
|
||||
use WebGUI::Workflow::Activity::ArchiveOldStories;
|
||||
|
||||
use Data::Dumper;
|
||||
use Test::More;
|
||||
use Test::Deep;
|
||||
|
||||
plan tests => 6; # increment this value for each test you create
|
||||
|
||||
my $session = WebGUI::Test->session;
|
||||
$session->user({userId => 3});
|
||||
|
||||
my $home = WebGUI::Asset->getDefault($session);
|
||||
my $wgBday = WebGUI::Test->webguiBirthday;
|
||||
|
||||
my $creationDateSth = $session->db->prepare('update asset set creationDate=? where assetId=?');
|
||||
|
||||
my $archive1 = $home->addChild({
|
||||
className => 'WebGUI::Asset::Wobject::StoryArchive',
|
||||
title => '2001 Stories',
|
||||
archiveAfter => 50*365*24*3600, ##50 years ago
|
||||
});
|
||||
|
||||
my $birthdayFolder = $archive1->getFolder($wgBday);
|
||||
$creationDateSth->execute([$wgBday, $birthdayFolder->getId]);
|
||||
|
||||
my @oldStories = ();
|
||||
push @oldStories, $birthdayFolder->addChild({ className => 'WebGUI::Asset::Story',});
|
||||
push @oldStories, $birthdayFolder->addChild({ className => 'WebGUI::Asset::Story',});
|
||||
foreach my $story (@oldStories) {
|
||||
$creationDateSth->execute([$wgBday, $story->getId]);
|
||||
}
|
||||
|
||||
my $archive2 = $home->addChild({
|
||||
className => 'WebGUI::Asset::Wobject::StoryArchive',
|
||||
title => 'Stories from last week',
|
||||
archiveAfter => 10*24*3600, #10 days ago
|
||||
});
|
||||
|
||||
my $weekAgo = time() - (7*24*3600);
|
||||
my $weekFolder = $archive2->getFolder($weekAgo);
|
||||
my $weekStory = $weekFolder->addChild({className => 'WebGUI::Asset::Story',});
|
||||
$creationDateSth->execute([$weekAgo, $weekFolder->getId]);
|
||||
$creationDateSth->execute([$weekAgo, $weekStory->getId]);
|
||||
|
||||
my $versionTag = WebGUI::VersionTag->getWorking($session);
|
||||
$versionTag->commit;
|
||||
|
||||
my $workflow = WebGUI::Workflow->create($session,
|
||||
{
|
||||
enabled => 1,
|
||||
objectType => 'None',
|
||||
mode => 'realtime',
|
||||
},
|
||||
);
|
||||
my $activity = $workflow->addActivity('WebGUI::Workflow::Activity::ArchiveOldStories');
|
||||
|
||||
my $instance1 = WebGUI::Workflow::Instance->create($session,
|
||||
{
|
||||
workflowId => $workflow->getId,
|
||||
skipSpectreNotification => 1,
|
||||
}
|
||||
);
|
||||
|
||||
my $retVal;
|
||||
|
||||
$retVal = $instance1->run();
|
||||
is($retVal, 'complete', 'First workflow was run');
|
||||
$retVal = $instance1->run();
|
||||
is($retVal, 'done', 'Workflow is done');
|
||||
|
||||
my $archivedAssets = $home->getLineage(
|
||||
['descendants'],
|
||||
{
|
||||
includeOnlyClasses => ['WebGUI::Asset::Story', 'WebGUI::Asset::Wobject::Folder', 'WebGUI::Asset::Wobject::StoryArchive'],
|
||||
statusToInclude => ['archived'],
|
||||
},
|
||||
);
|
||||
|
||||
cmp_bag( $archivedAssets, [ ], 'Nothing archived.');
|
||||
|
||||
$archive2->update({ archiveAfter => 5*24*3600, });
|
||||
|
||||
my $instance2 = WebGUI::Workflow::Instance->create($session,
|
||||
{
|
||||
workflowId => $workflow->getId,
|
||||
skipSpectreNotification => 1,
|
||||
}
|
||||
);
|
||||
$retVal = $instance2->run();
|
||||
is($retVal, 'complete', 'Second workflow was run');
|
||||
$retVal = $instance2->run();
|
||||
is($retVal, 'done', 'Workflow is done');
|
||||
|
||||
$archivedAssets = $home->getLineage(
|
||||
['descendants'],
|
||||
{
|
||||
includeOnlyClasses => ['WebGUI::Asset::Story', 'WebGUI::Asset::Wobject::Folder', 'WebGUI::Asset::Wobject::StoryArchive'],
|
||||
statusToInclude => ['archived'],
|
||||
},
|
||||
);
|
||||
|
||||
cmp_bag( $archivedAssets, [ $weekStory->getId, $weekFolder->getId ], 'Nothing archived.');
|
||||
|
||||
END {
|
||||
$creationDateSth->finish;
|
||||
$versionTag->rollback;
|
||||
$workflow->delete;
|
||||
}
|
||||
|
||||
|
|
@ -90,4 +90,3 @@ END {
|
|||
defined $session &&
|
||||
$session->config->set('macros', $originalMacros);
|
||||
}
|
||||
|
||||
|
|
|
|||
74
t/lib/WebGUI/Asset/RssAspectDummy.pm
Normal file
74
t/lib/WebGUI/Asset/RssAspectDummy.pm
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
package WebGUI::Asset::RssAspectDummy;
|
||||
|
||||
=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 Tie::IxHash;
|
||||
use Class::C3;
|
||||
use base qw/WebGUI::AssetAspect::RssFeed WebGUI::Asset/;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
Package WebGUI::Asset::RssAspectDummy
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
A dummy module for testing the RssAspect. The module really doesn't
|
||||
do anything, except provide suport modules for testing.
|
||||
|
||||
The module inherits directly from WebGUI::Asset.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
use WebGUI::Asset::RssAspectDummy;
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
These methods are available from this class:
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 getRssFeedItems ( )
|
||||
|
||||
Returns an arrayref of hashrefs, containing information on stories
|
||||
for generating an RSS and Atom feeds.
|
||||
|
||||
=cut
|
||||
|
||||
sub getRssFeedItems {
|
||||
|
||||
return [
|
||||
{
|
||||
title => 'this title',
|
||||
description => 'this description',
|
||||
'link' => 'this link',
|
||||
author => 'this author',
|
||||
date => 'this date',
|
||||
},
|
||||
{
|
||||
title => 'another title',
|
||||
description => 'another description',
|
||||
author => 'another author',
|
||||
date => 'another date',
|
||||
},
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
#vim:ft=perl
|
||||
|
|
@ -374,6 +374,20 @@ sub session {
|
|||
return $SESSION;
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
=head2 webguiBirthday ( )
|
||||
|
||||
This constant is used in several tests, so it's reproduced here so it can
|
||||
be found easy. This is the epoch date when WebGUI was released.
|
||||
|
||||
=cut
|
||||
|
||||
sub webguiBirthday {
|
||||
return 997966800 ;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
|
|
|
|||
14
t/smtpd.pl
14
t/smtpd.pl
|
|
@ -1,6 +1,7 @@
|
|||
|
||||
my $HOST = shift;
|
||||
my $PORT = shift;
|
||||
my $EMAILS = shift || 1;
|
||||
|
||||
die "HOST must be first argument"
|
||||
unless $HOST;
|
||||
|
|
@ -13,17 +14,26 @@ use Net::SMTP::Server::Client;
|
|||
|
||||
my $server = Net::SMTP::Server->new( $HOST, $PORT );
|
||||
|
||||
while ( my $conn = $server->accept ) {
|
||||
my $counter = 0;
|
||||
|
||||
$| = 1;
|
||||
|
||||
CONNECTION: while ( my $conn = $server->accept ) {
|
||||
my $client = Net::SMTP::Server::Client->new( $conn );
|
||||
$client->process;
|
||||
print to_json({
|
||||
to => $client->{TO},
|
||||
from => $client->{FROM},
|
||||
contents => $client->{MSG},
|
||||
counter => $counter,
|
||||
emails => $EMAILS,
|
||||
});
|
||||
exit(0);
|
||||
print "\n";
|
||||
last CONNECTION if ++$counter >= $EMAILS;
|
||||
}
|
||||
|
||||
sleep 3;
|
||||
exit(0);
|
||||
|
||||
=head1 NAME
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue