Merge commit 'f2e0fb509a' into WebGUI8. Some tests still failing.

This commit is contained in:
Colin Kuskie 2010-06-27 22:32:31 -07:00
commit 385931aaab
92 changed files with 1966 additions and 650 deletions

View file

@ -1,3 +1,24 @@
7.9.6
- new checkbox in the asset manager for clearing the package flag on import
- fixed #11597: manageTrash and newlines
- fixed #11577: Gallery Album: "Sort by" radio list missing in "Add Archive" view
- fixed #11576: Default WebGUI config has a bad macro
- fixed #11578: Collaboration System: add edit stamp uses wrong user
- added #9774: More owner information in the gallery
- fixed #11581: Calendar problems
- fixed #11583: EMS: Tokens do not follow their permissions
- fixed #11584: Errors on checkout when payment problems occur
- fixed #11582: Registering with a .mobi email address
- fixed #11580: Date not populated for Story Archive RSS feed
- fixed #11587: Thingy, no fields and undefined statement handles
- fixed #11589: Syndicated Content: Return raw text for sentence and word template variables
- fixed #11573: user has no way of knowing what they are currently using
- fixed #11603: Shelf, template variables for sub shelves
- added #11504: Allow search by location in the gallery
- migrate to getLineageIterator to save memory
- add findBrokenAssets.pl to find and fix/delete broken assets
- change to use weaken() to save memory
7.9.5
- Asset->www_copy now has a progress bar
- fixed #11556: New cart doesn't work with other forms on the same page

File diff suppressed because one or more lines are too long

View file

@ -17,6 +17,12 @@ save you many hours of grief.
- Moose
- CHI
7.9.6
--------------------------------------------------------------------
* The javascript check for email addresses has been removed.
7.9.5
--------------------------------------------------------------------
* Starting in WebGUI 7.9.4, the CHI and Cache::FastMmap modules are required.

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,161 @@
#!/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;
my $toVersion = '7.9.6';
my $quiet; # this line required
my $session = start(); # this line required
# upgrade functions go here
fixConvertUTCMacroName($session);
dropOldEMSTableColumn($session);
addIndexForInbox($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;
#}
#----------------------------------------------------------------------------
# Add keys and indicies to groupGroupings to help speed up group queries
sub addIndexForInbox {
my $session = shift;
print "\tAdding index to inbox_messageState... " unless $quiet;
my $sth = $session->db->read('show create table inbox_messageState');
my ($field,$stmt) = $sth->array;
$sth->finish;
unless ($stmt =~ m/KEY `userId_deleted_isRead`/i) {
$session->db->write("alter table inbox_messageState add index userId_deleted_isRead (userId,deleted,isRead)");
}
print "DONE!\n" unless $quiet;
}
#----------------------------------------------------------------------------
# Describe what our function does
sub fixConvertUTCMacroName {
my $session = shift;
print "\tFix the name of the ConvertUTCToTZ macro in the config file... " unless $quiet;
$session->config->deleteFromHash('macros', 'ConvertToUTC');
$session->config->addToHash('macros', 'ConvertUTCToTZ', 'ConvertUTCToTZ');
# and here's our code
print "DONE!\n" unless $quiet;
}
#----------------------------------------------------------------------------
# Describe what our function does
sub dropOldEMSTableColumn {
my $session = shift;
print "\tDrop an old column from the EventMangementSystem table that is no longer used... " unless $quiet;
$session->db->write(q|ALTER TABLE EventManagementSystem DROP COLUMN groupToApproveEvents|);
# and here's our code
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;
print "\tUpgrading package $file\n" unless $quiet;
# 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 = eval {
my $node = WebGUI::Asset->getImportNode($session);
$node->importPackage( $storage, {
overwriteLatest => 1,
clearPackageFlag => 1,
setDefaultTemplate => 1,
} );
};
if ($package eq 'corrupt') {
die "Corrupt package found in $file. Stopping upgrade.\n";
}
if ($@ || !defined $package) {
die "Error during package import on $file: $@\nStopping upgrade\n.";
}
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',".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

View file

@ -808,7 +808,7 @@
"BackToSite" : "BackToSite",
"CanEditText" : "CanEditText",
"CartItemCount" : "CartItemCount",
"ConvertToUTC" : "ConvertToUTC",
"ConvertUTCToTZ" : "ConvertUTCToTZ",
"c" : "c_companyName",
"D" : "D_date",
"DeactivateAccount": "DeactivateAccount",

View file

@ -14,7 +14,7 @@ package WebGUI::Asset;
=cut
use Scalar::Util qw( blessed );
use Scalar::Util qw( blessed weaken );
use Clone qw(clone);
use JSON;
use HTML::Packer;

View file

@ -585,8 +585,7 @@ sub getEventNext {
'assetData.assetId',
);
my $events = $self->getLineage(['siblings'], {
#returnObjects => 1,
my $events = $self->getLineageIterator(['siblings'], {
includeOnlyClasses => ['WebGUI::Asset::Event'],
joinClass => 'WebGUI::Asset::Event',
orderByClause => join(",", @orderByColumns),
@ -594,9 +593,12 @@ sub getEventNext {
limit => 1,
});
return undef unless $events->[0];
return WebGUI::Asset->newById($self->session,$events->[0]);
my $nextEvent;
eval { $nextEvent = $events->() };
if ( WebGUI::Error->caught('WebGUI::Error::ObjecNotFound') ) {
return undef; # Normal error
}
return $nextEvent;
}
@ -642,8 +644,7 @@ sub getEventPrev {
'assetData.assetId DESC',
);
my $events = $self->getLineage(['siblings'], {
#returnObjects => 1,
my $events = $self->getLineageIterator(['siblings'], {
includeOnlyClasses => ['WebGUI::Asset::Event'],
joinClass => 'WebGUI::Asset::Event',
orderByClause => join(",",@orderByColumns),
@ -651,8 +652,12 @@ sub getEventPrev {
limit => 1,
});
return undef unless $events->[0];
return WebGUI::Asset->newById($self->session,$events->[0]);
my $prevEvent;
eval { $prevEvent = $events->() };
if ( WebGUI::Error->caught( 'WebGUI::Error::ObjectNotFound' ) ) {
return undef; # Normal error
}
return $prevEvent;
}
@ -1490,35 +1495,46 @@ override processPropertiesFromFormPost => sub {
# Delete old events
if ($old_id) {
my $events = $self->getLineage(["siblings"], {
returnObjects => 1,
my $events = $self->getLineageIterator(["siblings"], {
includeOnlyClasses => ['WebGUI::Asset::Event'],
joinClass => 'WebGUI::Asset::Event',
whereClause => qq{Event.recurId = "$old_id"},
});
$_->purge for @$events;
while ( 1 ) {
my $event;
eval { $event = $events->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$session->log->error(sprintf "Couldn't instance event asset %s to delete it", $x->id);
next;
}
last unless $event;
$event->purge;
}
}
}
else {
# TODO: Give users a form property to decide what events to update
# TODO: Make a workflow activity to do this, so that updating
# 1 million events doesn't kill the server.
# TODO: Make this use WebGUI::ProgressBar so 1 million events doesn't kill the server.
# Just update related events
my %properties = %{ $self->get };
delete $properties{startDate};
delete $properties{endDate};
delete $properties{url}; # addRevision will create a new url for us
my $events = $self->getLineage(["siblings"], {
#returnObjects => 1,
my $events = $self->getLineageIterator(["siblings"], {
includeOnlyClasses => ['WebGUI::Asset::Event'],
joinClass => 'WebGUI::Asset::Event',
whereClause => q{Event.recurId = "}.$self->recurId.q{"},
});
for my $eventId (@{$events}) {
my $event = WebGUI::Asset->newById($session, $eventId);
while ( 1 ) {
my $event;
eval { $event = $events->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$session->log->error(sprintf "Couldn't instance event asset %s to update it", $x->id);
next;
}
last unless $event;
# Add a revision
$properties{ startDate } = $event->startDate;
$properties{ endDate } = $event->endDate;

View file

@ -371,6 +371,7 @@ around indexContent => sub {
my $self = shift;
my $indexer = $self->$orig(@_);
$indexer->addFile($self->getStorageLocation->getPath($self->filename));
return $indexer;
};

View file

@ -549,7 +549,10 @@ sub getTemplateVars {
$var->{ canComment } = $self->canComment;
$var->{ canEdit } = $self->canEdit;
$var->{ numberOfComments } = scalar @{ $self->getCommentIds };
$var->{ ownerUsername } = $owner->profileField("alias") || $owner->username;
$var->{ ownerUsername } = $owner->get("username");
$var->{ ownerAlias } = $owner->get("alias") || $owner->get("username");
$var->{ ownerId } = $owner->getId;
$var->{ ownerProfileUrl } = $owner->getProfileUrl;
$var->{ url } = $self->getUrl;
$var->{ url_addArchive } = $self->getParent->getUrl('func=addArchive'),
$var->{ url_delete } = $self->getUrl('func=delete');

View file

@ -349,6 +349,22 @@ sub getThumbnailUrl {
);
}
#-------------------------------------------------------------------
=head2 indexContent ( )
Indexing the content of the Photo. See WebGUI::Asset::indexContent() for
additonal details.
=cut
sub indexContent {
my $self = shift;
my $indexer = $self->SUPER::indexContent;
$indexer->addKeywords($self->get("location"));
return $indexer;
}
#----------------------------------------------------------------------------
=head2 makeResolutions ( [resolutions] )

View file

@ -115,14 +115,12 @@ sub _fixReplyCount {
my $self = shift;
my $asset = shift;
my $lastPost = $asset->getLineage( [ qw{ self descendants } ], {
returnObjects => 1,
my $lastPostId = $asset->getLineage( [ qw{ self descendants } ], {
isa => 'WebGUI::Asset::Post',
orderByClause => 'assetData.revisionDate desc',
limit => 1,
} )->[0];
if ($lastPost) {
if (my $lastPost = WebGUI::Asset->newById( $self->session, $lastPostId ) ) {
$asset->incrementReplies( $lastPost->revisionDate, $lastPost->getId );
}
else {
@ -690,23 +688,25 @@ sub getTemplateVars {
unless ($self->storageId eq "") {
my $storage = $self->getStorageLocation;
foreach my $filename (@{$storage->getFiles}) {
if (!$gotImage && $storage->isImage($filename)) {
$var{"image.url"} = $storage->getUrl($filename);
my $isImage = $storage->isImage($filename);
my $fileUrl = $storage->getUrl($filename);
if (!$gotImage && $isImage) {
$var{"image.url"} = $fileUrl;
$var{"image.thumbnail"} = $storage->getThumbnailUrl($filename);
$gotImage = 1;
}
if (!$gotAttachment && !$storage->isImage($filename)) {
$var{"attachment.url"} = $storage->getUrl($filename);
if (!$gotAttachment && !$isImage) {
$var{"attachment.url"} = $fileUrl;
$var{"attachment.icon"} = $storage->getFileIconUrl($filename);
$var{"attachment.name"} = $filename;
$gotAttachment = 1;
}
push(@{$var{"attachment_loop"}}, {
url=>$storage->getUrl($filename),
icon=>$storage->getFileIconUrl($filename),
filename=>$filename,
thumbnail=>$storage->getThumbnailUrl($filename),
isImage=>$storage->isImage($filename)
url =>$fileUrl,
icon =>$var{"attachment.icon"},
filename =>$filename,
thumbnail =>$var{"image.thumbnail"},
isImage =>$isImage
});
}
}
@ -983,10 +983,7 @@ override paste => sub {
super();
# First, figure out what Thread we're under
my $thread = $self->getLineage( [ qw{ self ancestors } ], {
returnObjects => 1,
isa => 'WebGUI::Asset::Post::Thread',
} )->[0];
my $thread = $self->getThread;
# If the pasted asset is not a thread we'll have to update the threadId of it and all posts below it.
if ( $self->threadId ne $self->getId ) {
@ -1083,10 +1080,9 @@ sub postProcess {
$self->trash;
}
}
my $user = WebGUI::User->new($self->session, $self->ownerUserId);
my $i18n = WebGUI::International->new($self->session, "Asset_Post");
if ($self->getThread->getParent->addEditStampToPosts) {
$data{content} .= "<p>\n\n --- (".$i18n->get('Edited_on')." ".$self->session->datetime->epochToHuman(undef,"%z %Z [GMT%O]")." ".$i18n->get('By')." ".$user->profileField("alias").") --- \n</p>";
if ($self->getThread->getParent->get("addEditStampToPosts")) {
$data{content} .= "<p>\n\n --- (".$i18n->get('Edited_on')." ".$self->session->datetime->epochToHuman(undef,"%z %Z [GMT%O]")." ".$i18n->get('By')." ".$self->session->user->profileField("alias").") --- \n</p>";
}
$data{url} = $self->fixUrl($self->getThread->url."/1") if ($self->isReply && $self->isNew);
$data{groupIdView} = $self->getThread->getParent->groupIdView;

View file

@ -481,7 +481,7 @@ Returns a list of the post objects in this thread, including the thread post its
sub getPosts {
my $self = shift;
$self->getLineage(["self","descendants"], {
return $self->getLineage(["self","descendants"], {
returnObjects => 1,
includeArchived => 1,
includeOnlyClasses => ["WebGUI::Asset::Post","WebGUI::Asset::Post::Thread"],

View file

@ -208,7 +208,7 @@ Takes form variable badgeId and add the token to the cart.
sub www_addToCart {
my ($self) = @_;
return $self->session->privilege->noAccess() unless $self->getParent->canView;
return $self->session->privilege->noAccess() unless $self->getParent->canView && $self->canView;
my $badgeId = $self->session->form->get('badgeId');
$self->addToCart({badgeId=>$badgeId});
return $self->getParent->www_buildBadge($badgeId);

View file

@ -329,7 +329,15 @@ sub getThingOptions {
tie my %options, 'Tie::IxHash', ( "" => "" );
my $thingyIter = WebGUI::Asset->getRoot($session)
->getLineageIterator( ['descendants'], { includeOnlyClasses => ['WebGUI::Asset::Wobject::Thingy'], } );
while ( my $thingy = $thingyIter->() ) {
while ( 1 ) {
my $thingy;
eval { $thingy = $thingyIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$session->log->error($x->full_message);
next;
}
last unless $thingy;
tie my %things, 'Tie::IxHash', (
$session->db->buildHash( "SELECT thingId, label FROM Thingy_things WHERE assetId=?", [ $thingy->getId ] ) );
$options{ $thingy->title } = \%things;

View file

@ -506,8 +506,10 @@ sub getRssData {
title => $self->headline || $self->getTitle,
description => $self->story,
'link' => $self->getUrl,
guid => $self->getUrl,
author => $self->byline,
date => $self->lastModified,
pubDate => $self->session->datetime->epochToMail($self->creationDate),
};
return $data;
}

View file

@ -474,13 +474,19 @@ use WebGUI::Workflow::Cron;
#-------------------------------------------------------------------
sub _computePostCount {
my $self = shift;
return scalar @{$self->getLineage(['descendants'], {includeOnlyClasses => ['WebGUI::Asset::Post']})};
return $self->getDescendantCount({
includeOnlyClasses => ['WebGUI::Asset::Post'],
statusToInclude => ['approved'],
});
}
#-------------------------------------------------------------------
sub _computeThreadCount {
my $self = shift;
return scalar @{$self->getLineage(['children'], {includeOnlyClasses => ['WebGUI::Asset::Post::Thread']})};
return $self->getChildCount({
includeOnlyClasses => ['WebGUI::Asset::Post::Thread'],
statusToInclude => ['approved'],
});
}
#-------------------------------------------------------------------
@ -1393,21 +1399,29 @@ and direction.
sub processPropertiesFromFormPost {
my $self = shift;
my $updatePrivs = ($self->session->form->process("groupIdView") ne $self->groupIdView || $self->session->form->process("groupIdEdit") ne $self->groupIdEdit);
my $updatePrivs = ($self->session->form->process("groupIdView") ne $self->groupIdView || $self->session->form->process("groupIdEdit") ne $self->groupIdEdit);
$self->next::method;
if ($self->subscriptionGroupId eq "") {
$self->createSubscriptionGroup;
}
if ($updatePrivs) {
foreach my $descendant (@{$self->getLineage(["descendants"],{returnObjects=>1})}) {
$descendant->update({
groupIdView=>$self->groupIdView,
groupIdEdit=>$self->groupIdEdit
});
}
if ($updatePrivs) {
my $descendantIter = $self->getLineageIterator(['descendants']);
while ( 1 ) {
my $descendant;
eval { $descendant = $descendantIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$self->session->log->error($x->full_message);
next;
}
last unless $descendant;
$descendant->update({
groupIdView=>$self->groupIdView,
groupIdEdit=>$self->groupIdEdit
});
}
$self->session->scratch->delete($self->getId."_sortBy");
$self->session->scratch->delete($self->getId."_sortDir");
}
$self->session->scratch->delete($self->getId."_sortBy");
$self->session->scratch->delete($self->getId."_sortDir");
}
@ -1702,16 +1716,21 @@ sub www_unarchiveAll {
my $pb = WebGUI::ProgressBar->new($session);
my $i18n = WebGUI::International->new($session, 'Asset_Collaboration');
$pb->start($i18n->get('unarchive all'), $self->getUrl('func=edit'));
my $threadIds = $self->getLineage(['children'],{
my $threadIter = $self->getLineageIterator(['children'],{
includeOnlyClasses => [ 'WebGUI::Asset::Post::Thread' ],
statusToInclude => [ 'archived' ],
} );
ASSET: foreach my $threadId (@$threadIds) {
my $thread = WebGUI::Asset->newPending($session, $threadId);
if (!$thread || !$thread->canEdit) {
next ASSET;
ASSET: while ( 1 ) {
my $thread;
eval { $thread = $threadIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$session->log->error($x->full_message);
next;
}
last unless $thread;
if ($thread->canEdit) {
$thread->unarchive;
}
$thread->unarchive;
}
return $pb->finish( $self->getUrl('func=edit') );
}

View file

@ -175,6 +175,45 @@ sub getContentPositionsDefault {
#-------------------------------------------------------------------
=head2 getEditForm
Extend the base method to display lists of assets to hide or show.
=cut
sub getEditForm {
my $self = shift;
my $tabform = $self->SUPER::getEditForm;
my $i18n = WebGUI::International->new($self->session, "Asset_Dashboard");
if ($self->session->form->process("func") ne "add") {
my @assetsToHide = split("\n",$self->getValue("assetsToHide"));
my $childIter = $self->getLineageIterator(["children"],{excludeClasses=>["WebGUI::Asset::Wobject::Layout"]});
my %childIds;
while ( 1 ) {
my $child;
eval { $child = $childIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$self->session->log->error($x->full_message);
next;
}
last unless $child;
$childIds{$child->getId} = $child->getTitle.' ['.ref($child).']';
}
$tabform->getTab("display")->checkList(
-name=>"assetsToHide",
-value=>\@assetsToHide,
-options=>\%childIds,
-label=>$i18n->get('assets to hide'),
-hoverHelp=>$i18n->get('assets to hide description'),
-vertical=>1,
-uiLevel=>9
);
}
return $tabform;
}
#-------------------------------------------------------------------
=head2 initialize
Add the unique profile field that holds content positions for this dashboard.
@ -218,18 +257,25 @@ their templates.
=cut
sub prepareView {
override prepareView => sub {
my $self = shift;
$self->SUPER::prepareView;
my $children = $self->getLineage( ["children"], { returnObjects=>1, excludeClasses=>["WebGUI::Asset::Wobject::Layout","WebGUI::Asset::Wobject::Dashboard"] });
super();
my @hidden = split("\n",$self->assetsToHide);
foreach my $child (@{$children}) {
my $childIter = $self->getLineageIterator( ["children"], {excludeClasses=>["WebGUI::Asset::Wobject::Layout","WebGUI::Asset::Wobject::Dashboard"] });
while ( 1 ) {
my $child;
eval { $child = $childIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$self->session->log->error($x->full_message);
next;
}
last unless $child;
unless (isIn($child->getId, @hidden) || !($child->canView)) {
$self->session->style->setRawHeadTags($child->getExtraHeadTags);
$child->prepareView;
}
}
}
};
#-------------------------------------------------------------------
@ -286,8 +332,9 @@ sub view {
$self->session->style->setScript( $self->session->url->extras('yui/build/utilities/utilities.js'));
my $templateId = $self->templateId;
my $children = $self->getLineage( ["children"], { returnObjects=>1, excludeClasses=>["WebGUI::Asset::Wobject::Layout","WebGUI::Asset::Wobject::Dashboard"] });
# XXX Not using getLineageIterator because we loop over the children three times...
# I'm sure there's a more efficient way to do this. We'll figure it out someday.
my $children = $self->getLineage( ["children"], { returnObjects=>1, excludeClasses=>["WebGUI::Asset::Wobject::Layout","WebGUI::Asset::Wobject::Dashboard"] });
my @positions = split(/\./,$self->getContentPositions);
my @hidden = split("\n",$self->assetsToHide);
foreach my $child (@{$children}) {

View file

@ -615,10 +615,10 @@ returns true if the EMS has subission forms attached
sub hasSubmissionForms {
my $self = shift;
# are there ~any~ forms attached to this ems?
my $res = $self->getLineage(['children'],{ limit => 1,
my $count = $self->getDescendantCount({
includeOnlyClasses => ['WebGUI::Asset::EMSSubmissionForm'],
} );
return scalar(@$res);
return $count;
}
#-------------------------------------------------------------------
@ -1046,12 +1046,12 @@ then call www_editSubmission on it
sub www_editSubmission {
my $self = shift;
my $submissionId = $self->session->form->get('submissionId');
my $asset = $self->getLineage(['descendants'], { returnObjects => 1,
my $asset = $self->getLineageIterator(['descendants'], {
joinClass => "WebGUI::Asset::EMSSubmission",
whereClause => 'submissionId = ' . int($submissionId),
includeOnlyClasses => ['WebGUI::Asset::EMSSubmission'],
} );
return $asset->[0]->www_editSubmission;
return $asset->()->www_editSubmission;
}
@ -1397,6 +1397,7 @@ sub www_getBadgesAsJson {
my ($db, $form) = $session->quick(qw(db form));
my %results = ();
$results{records} = [];
# TODO: Use getLineageIterator here instead
BADGE: foreach my $badge (@{$self->getBadges}) {
next BADGE unless $badge->canView;
push(@{$results{records}}, {
@ -1965,7 +1966,8 @@ sub www_getTokensAsJson {
my ($db, $form) = $session->quick(qw(db form));
my %results = ();
$results{records} = []; ##Initialize to an empty array
foreach my $token (@{$self->getTokens}) {
TOKEN: foreach my $token (@{$self->getTokens}) {
next TOKEN unless $token->canView;
push(@{$results{records}}, {
title => $token->getTitle,
description => $token->description,

View file

@ -104,7 +104,15 @@ Overridden to check the revision dates of children as well
sub getContentLastModified {
my $self = shift;
my $mtime = $self->revisionDate;
foreach my $child (@{ $self->getLineage(["children"],{returnObjects=>1}) }) {
my $childIter = $self->getLineageIterator(["children"]);
while ( 1 ) {
my $child;
eval { $child = $childIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$self->session->log->error($x->full_message);
next;
}
last unless $child;
my $child_mtime = $child->getContentLastModified;
$mtime = $child_mtime if ($child_mtime > $mtime);
}
@ -216,17 +224,24 @@ sub view {
my $vars = $self->getTemplateVars;
# TODO: Getting the children template vars should be a seperate method.
my %rules = ( returnObjects => 1);
my %rules = ( );
if ( $self->sortAlphabetically ) {
$rules{ orderByClause } = "assetData.title " . $self->sortOrder;
$rules{ orderByClause } = "assetData.title " . $self->get( "sortOrder" );
}
else {
$rules{ orderByClause } = "asset.lineage " . $self->sortOrder;
}
my $children = $self->getLineage( ["children"], \%rules);
foreach my $child ( @{ $children } ) {
# TODO: Instead of this it should be using $child->getTemplateVars || $child->get
my $childIter = $self->getLineageIterator( ["children"], \%rules);
while ( 1 ) {
my $child;
eval { $child = $childIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$self->session->log->error($x->full_message);
next;
}
last unless $child;
# TODO: Instead of this it should be using $child->getTemplateVars || $child->get
if ( $child->isa("WebGUI::Asset::Wobject::Folder") ) {
push @{ $vars->{ "subfolder_loop" } }, {
id => $child->getId,

View file

@ -325,8 +325,8 @@ with 'WebGUI::Role::Asset::RssFeed';
use JSON;
use Tie::IxHash;
use WebGUI::International;
use WebGUI::Search;
use XML::Simple;
use WebGUI::HTML;
@ -388,7 +388,13 @@ sub appendTemplateVarsSearchForm {
name => "keywords",
value => scalar $form->get("keywords"),
});
$var->{ searchForm_location }
= WebGUI::Form::text( $session, {
name => "location",
value => $form->get("location"),
});
# Search classes
tie my %searchClassOptions, 'Tie::IxHash', (
'WebGUI::Asset::File::GalleryFile::Photo' => $i18n->get("search class photo"),
@ -763,6 +769,7 @@ Other keys are valid, see C<WebGUI::Search::search()> for details.
sub getSearchPaginator {
my $self = shift;
my $rules = shift;
my $session = $self->session;
$rules->{ lineage } = [ $self->lineage ];
@ -913,17 +920,21 @@ sub hasSpaceAvailable {
# Compile the amount of disk space used
my $maxSpace = $self->maxSpacePerUser * ( 1_024 ** 2 ); # maxSpacePerUser is in MB
my $spaceUsed = 0;
my $fileIds
= $self->getLineage( [ 'descendants' ], {
my $fileIter
= $self->getLineageIterator( [ 'descendants' ], {
joinClass => 'WebGUI::Asset::File::GalleryFile',
whereClause => 'ownerUserId = ' . $db->quote( $userId ),
} );
for my $assetId ( @{ $fileIds } ) {
my $asset = WebGUI::Asset->newById( $self->session, $assetId );
next unless $asset;
$spaceUsed += $asset->assetSize;
while ( 1 ) {
my $file;
eval { $file = $fileIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$self->session->log->error($x->full_message);
next;
}
last unless $file;
$spaceUsed += $file->get( 'assetSize' );
return 0 if ( $spaceUsed + $spaceWanted >= $maxSpace );
}
@ -1340,45 +1351,61 @@ sub www_listAlbumsService {
return JSON->new->pretty->encode($document);
}
#----------------------------------------------------------------------------
=head2 www_search ( )
=head2 search ( )
Search through the GalleryAlbums and files in this gallery. Show the form to
search and display the results if necessary.
Helper method for C<www_search> containing all the search logic. Executes a
search depending on search form parameters. Returns undef if no search was
executed. Otherwise an array is returned containing the following elements:
=head3 paginator
A paginator object containing the search results.
=head3 keywords
Search keywords assembled from search form fields.
=cut
sub www_search {
sub search {
my $self = shift;
my $session = $self->session;
my $form = $session->form;
my $db = $session->db;
my $columns;
my $var = $self->getTemplateVars;
# NOTE: Search form is added as part of getTemplateVars()
# Get search results, if necessary.
# Check whether we have to do a search
my $doSearch
= (
$form->get( 'basicSearch' ) || $form->get( 'keywords' )
|| $form->get( 'title' ) || $form->get( 'description' )
|| $form->get( 'userId' ) || $form->get( 'className' )
|| $form->get( 'creationDate_after' ) || $form->get( 'creationDate_before' )
$form->get( 'basicSearch' ) || $form->get( 'keywords' )
|| $form->get( 'location' ) || $form->get( 'title' )
|| $form->get( 'description' ) || $form->get( 'userId' )
|| $form->get( 'className' ) || $form->get( 'creationDate_after' )
|| $form->get( 'creationDate_before' )
);
if ( $doSearch ) {
# Keywords to search on
# Do not add a space to the
if ( $doSearch ) {
# Keywords to search on.
my $keywords;
FORMVAR: foreach my $formVar (qw/ basicSearch keywords title description /) {
FORMVAR: foreach my $formVar (qw/ basicSearch keywords location title description /) {
my $var = $form->get($formVar);
next FORMVAR unless $var;
$keywords = join ' ', $keywords, $var;
}
# Remove leading whitespace
$keywords =~ s/^\s+//;
# Build a where clause from the advanced options
# Lineage search can capture gallery
# Note that adding criteria to the where clause alone will not work. If
# you want to cover additional properties you need to make sure that
# - the property is added to $keywords above
# - the property is included in index keywords by overriding the indexContent method of respective classes (usually Photo or GalleryFile)
# - the respective table is joined in (usually via joinClass parameter of getSearchPaginator)
# - the column containing the property is included in the query (usually via column parameter of getSearchPaginator)
my $where = q{assetIndex.assetId <> '} . $self->getId . q{'};
if ( $form->get("title") ) {
$where .= q{ AND assetData.title LIKE }
@ -1390,11 +1417,18 @@ sub www_search {
. $db->quote( '%' . $form->get("description") . '%' )
;
}
if ( $form->get("location") && ( $form->get("className") eq 'WebGUI::Asset::File::GalleryFile::Photo'
|| $form->get("className") eq '' ) ) {
$where .= q{ AND Photo.location LIKE }
. $db->quote( '%' . $form->get("location") . '%' )
;
push (@{$columns}, 'Photo.location');
}
if ( $form->get("userId") ) {
$where .= q{ AND assetData.ownerUserId = }
. $db->quote( $form->get("userId") )
;
}
}
my $oneYearAgo = WebGUI::DateTime->new( $session, time )->add( years => -1 )->epoch;
my $dateAfter = $form->get("creationDate_after", "dateTime", $oneYearAgo);
@ -1408,45 +1442,75 @@ sub www_search {
}
# Classes
my $joinClass = [
my $classes = [
'WebGUI::Asset::Wobject::GalleryAlbum',
'WebGUI::Asset::File::GalleryFile::Photo',
];
if ( $form->get("className") ) {
$joinClass = [ $form->get('className') ];
$classes = [ $form->get('className') ];
}
$where .= q{ AND assetIndex.className IN ( } . $db->quoteAndJoin( $joinClass ) . q{ ) };
# Build a URL for the pagination
my $url
= $self->getUrl(
'func=search;'
. 'basicSearch=' . $form->get('basicSearch') . ';'
. 'keywords=' . $form->get('keywords') . ';'
. 'location=' . $form->get('location') . ';'
. 'title=' . $form->get('title') . ';'
. 'description=' . $form->get('description') . ';'
. 'creationDate_after=' . $dateAfter . ';'
. 'creationDate_before=' . $dateBefore . ';'
. 'userId=' . $form->get("userId") . ';'
);
for my $class ( @$joinClass ) {
for my $class ( @$classes ) {
$url .= 'className=' . $class . ';';
}
my $p
my $paginator
= $self->getSearchPaginator( {
url => $url,
keywords => $keywords,
where => $where,
joinClass => $joinClass,
classes => $classes,
joinClass => $classes,
columns => $columns,
creationDate => $creationDate,
} );
} );
return ( $paginator, $keywords );
}
# Return undef to indicate that no search was executed
return undef;
}
#----------------------------------------------------------------------------
=head2 www_search ( )
Search through the GalleryAlbums and files in this gallery. Show the form to
search and display the results if necessary.
=cut
sub www_search {
my $self = shift;
my $session = $self->session;
my $var = $self->getTemplateVars;
# NOTE: Search form is added as part of getTemplateVars()
# Execute search and retrieve search result paginator and keywords
my ( $paginator, $keywords ) = $self->search;
if( $paginator ) {
# Provide search keywords as template variable
$var->{ keywords } = $keywords;
$p->appendTemplateVars( $var );
for my $result ( @{ $p->getPageData } ) {
# Add search results
$paginator->appendTemplateVars( $var );
for my $result ( @{ $paginator->getPageData } ) {
my $asset = WebGUI::Asset->newById( $session, $result->{assetId} );
push @{ $var->{search_results} }, {
%{ $asset->getTemplateVars },

View file

@ -1438,9 +1438,9 @@ sub www_edit {
elsif ( grep { $_ =~ /^rotateLeft-(.{22})$/ } $form->param ) {
my $assetId = ( grep { $_ =~ /^rotateLeft-(.{22})$/ } $form->param )[0];
$assetId =~ s/^rotateLeft-//;
my $asset = WebGUI::Asset->newByDynamicClass( $session, $assetId );
my $asset = eval { WebGUI::Asset->newById( $session, $assetId ); };
if ( $asset ) {
if ( ! Exception::Class->caught() ) {
# Add revision and create a new version tag by doing so
my $newRevision = $asset->addRevision;
# Rotate photo (i.e. all attached image files) by 90° CCW
@ -1456,9 +1456,9 @@ sub www_edit {
elsif ( grep { $_ =~ /^rotateRight-(.{22})$/ } $form->param ) {
my $assetId = ( grep { $_ =~ /^rotateRight-(.{22})$/ } $form->param )[0];
$assetId =~ s/^rotateRight-//;
my $asset = WebGUI::Asset->newByDynamicClass( $session, $assetId );
my $asset = WebGUI::Asset->newById( $session, $assetId );
if ( $asset ) {
if ( Exception::Class->caught() ) {
# Add revision and create a new version tag by doing so
my $newRevision = $asset->addRevision;
# Rotate photo (i.e. all attached image files) by 90° CW

View file

@ -160,9 +160,16 @@ override getEditForm => sub {
}
else {
my @assetsToHide = split("\n",$self->assetsToHide);
my $children = $self->getLineage(["children"],{"returnObjects"=>1, excludeClasses=>["WebGUI::Asset::Wobject::Layout"]});
my $childIter = $self->getLineageIterator(["children"],{excludeClasses=>["WebGUI::Asset::Wobject::Layout"]});
my %childIds;
foreach my $child (@{$children}) {
while ( 1 ) {
my $child;
eval { $child = $childIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$self->session->log->error($x->full_message);
next;
}
last unless $child;
$childIds{$child->getId} = $child->getTitle;
}
$extraFields{assetsToHide} = {
@ -236,11 +243,18 @@ sub prepareView {
my %placeHolder;
my @children;
for my $child ( @{ $self->getLineage( ["children"], {
returnObjects => 1,
my $childIter = $self->getLineageIterator( ["children"], {
excludeClasses => ["WebGUI::Asset::Wobject::Layout"],
} ) } ) {
} );
while ( 1 ) {
my $child;
eval { $child = $childIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$session->log->error($x->full_message);
next;
}
last unless $child;
my $assetId = $child->getId;
next
if ($hidden{$assetId} || ! $child->canView);
@ -393,7 +407,15 @@ override getContentLastModified => sub {
# Buggo: this is a little too conservative. Children that are hidden maybe shouldn't count. Hm.
my $self = shift;
my $mtime = super();
foreach my $child (@{$self->getLineage(["children"],{returnObjects=>1, excludeClasses=>['WebGUI::Asset::Wobject::Layout']})}) {
my $childIter = $self->getLineageIterator(["children"],{excludeClasses=>['WebGUI::Asset::Wobject::Layout']});
while ( 1 ) {
my $child;
eval { $child = $childIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$self->session->log->error($x->full_message);
next;
}
last unless $child;
my $child_mtime = $child->getContentLastModified;
$mtime = $child_mtime if ($child_mtime > $mtime);
}

View file

@ -300,11 +300,17 @@ sub deleteAttribute {
[$attributeId,$self->getId]);
# recalculate scores for MatrixListings
my @listings = @{ $self->getLineage(['descendants'], {
my $listingIter = $self->getLineageIterator(['descendants'], {
includeOnlyClasses => ['WebGUI::Asset::MatrixListing'],
returnObjects => 1,
}) };
foreach my $listing (@listings){
});
while ( 1 ) {
my $listing;
eval { $listing = $listingIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$self->session->log->error($x->full_message);
next;
}
last unless $listing;
$listing->updateScore;
}
@ -618,17 +624,23 @@ sub view {
if ($self->canEdit){
# Get all the MatrixListings that are still pending.
my @pendingListings = @{ $self->getLineage(['descendants'], {
my $pendingIter = $self->getLineageIterator(['descendants'], {
includeOnlyClasses => ['WebGUI::Asset::MatrixListing'],
orderByClause => "revisionDate asc",
returnObjects => 1,
statusToInclude => ['pending'],
}) };
foreach my $pendingListing (@pendingListings){
});
while ( 1 ) {
my $pending;
eval { $pending = $pendingIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$session->log->error($x->full_message);
next;
}
last unless $pending;
push (@{ $var->{pending_loop} }, {
url => $pendingListing->getUrl
."?func=view;revision=".$pendingListing->revisionDate,
name => $pendingListing->title,
url => $pending->getUrl
."?func=view;revision=".$pending->revisionDate,
name => $pending->title,
});
}
}
@ -705,18 +717,23 @@ sub view {
# Get the 5 MatrixListings that were last updated as objects using getLineage.
my @lastUpdatedListings = @{ $self->getLineage(['descendants'], {
my $lastUpdatedIter = $self->getLineageIterator(['descendants'], {
includeOnlyClasses => ['WebGUI::Asset::MatrixListing'],
joinClass => "WebGUI::Asset::MatrixListing",
orderByClause => "lastUpdated desc",
limit => 5,
returnObjects => 1,
}) };
foreach my $lastUpdatedListing (@lastUpdatedListings){
});
for ( 1..5 ) {
my $lastUpdated;
eval { $lastUpdated = $lastUpdatedIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$session->log->error($x->full_message);
next;
}
last unless $lastUpdated;
push (@{ $varStatistics->{last_updated_loop} }, {
url => $lastUpdatedListing->getUrl,
name => $lastUpdatedListing->title,
lastUpdated => $session->datetime->epochToHuman($lastUpdatedListing->lastUpdated,"%z")
url => $lastUpdated->getUrl,
name => $lastUpdated->title,
lastUpdated => $session->datetime->epochToHuman($lastUpdated->lastUpdated,"%z")
});
}
$varStatistics->{lastUpdated_sortButton} = "<span id='sortByUpdated'><button type='button'>"

View file

@ -96,8 +96,15 @@ sub view {
my $first;
my @forum_loop;
my $i18n = WebGUI::International->new($self->session,"Asset_MessageBoard");
my $children = $self->getLineage(["children"],{includeOnlyClasses=>["WebGUI::Asset::Wobject::Collaboration"],returnObjects=>1});
foreach my $child (@{$children}) {
my $childIter = $self->getLineageIterator(["children"],{includeOnlyClasses=>["WebGUI::Asset::Wobject::Collaboration"]});
while ( 1 ) {
my $child;
eval { $child = $childIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$self->session->log->error($x->full_message);
next;
}
last unless $child;
$count++;
next unless ($child->canView);
if ($count == 1) {

View file

@ -369,44 +369,49 @@ sub view {
# we've got to determine what our start point is based upon user conditions
my $start;
$self->session->asset(WebGUI::Asset->newByUrl($self->session)) unless ($self->session->asset);
my $current = $self->session->asset;
my $var = {'page_loop' => []};
my $current = $self->session->asset;
# no current asset is set
unless (defined $current) {
$current = WebGUI::Asset->getDefault($self->session);
}
my @interestingProperties = ('assetId', 'parentId', 'ownerUserId', 'synopsis', 'newWindow');
# Add properties from current asset
foreach my $property (@interestingProperties) {
$var->{'currentPage.'.$property} = $current->$property;
}
$var->{'currentPage.menuTitle'} = $current->getMenuTitle;
$var->{'currentPage.title'} = $current->getTitle;
$var->{'currentPage.isHome'} = ($current->getId eq $self->session->setting->get("defaultPage"));
$var->{'currentPage.url'} = $current->getUrl;
$var->{'currentPage.hasChild'} = $current->hasChildren;
$var->{'currentPage.rank'} = $current->getRank;
$var->{'currentPage.rankIs'.$current->getRank} = 1;
# Build the asset tree
if ($self->startType eq "specificUrl") {
$start = WebGUI::Asset->newByUrl($self->session,$self->startPoint);
} elsif ($self->startType eq "relativeToRoot") {
}
elsif ($self->startType eq "relativeToRoot") {
unless (($self->startPoint+1) >= $current->getLineageLength) {
$start = WebGUI::Asset->newByLineage($self->session,substr($current->lineage,0, ($self->startPoint + 1) * 6));
}
} elsif ($self->startType eq "relativeToCurrentUrl") {
}
elsif ($self->startType eq "relativeToCurrentUrl") {
$start = WebGUI::Asset->newByLineage($self->session,substr($current->lineage,0, ($current->getLineageLength + $self->startPoint) * 6));
}
$start = $current unless (defined $start); # if none of the above results in a start point, then the current page must be it
my @includedRelationships = split("\n",$self->assetsToInclude);
my %rules;
$rules{returnObjects} = 1;
$rules{endingLineageLength} = $start->getLineageLength+$self->descendantEndPoint;
$rules{assetToPedigree} = $current if (isIn("pedigree",@includedRelationships));
$rules{ancestorLimit} = $self->ancestorEndPoint;
$rules{orderByClause} = 'rpad(asset.lineage, 255, 9) desc' if ($self->reversePageLoop);
my @interestingProperties = ('assetId', 'parentId', 'ownerUserId', 'synopsis', 'newWindow');
my $assets = $start->getLineage(\@includedRelationships,\%rules);
my $var = {'page_loop' => []};
foreach my $property (@interestingProperties) {
$var->{'currentPage.'.$property} = $current->$property;
}
$var->{'currentPage.menuTitle'} = $current->getMenuTitle;
$var->{'currentPage.title'} = $current->getTitle;
$var->{'currentPage.isHome'} = ($current->getId eq $self->session->setting->get("defaultPage"));
$var->{'currentPage.url'} = $current->getUrl;
$var->{'currentPage.hasChild'} = $current->hasChildren;
$var->{'currentPage.rank'} = $current->getRank;
$var->{'currentPage.rankIs'.$current->getRank} = 1;
my $currentLineage = $current->lineage;
my $lineageToSkip = "noskip";
my $absoluteDepthOfLastPage;
@ -414,8 +419,7 @@ sub view {
my %lastChildren;
my $previousPageData = undef;
my $eh = $self->session->errorHandler;
foreach my $asset (@{$assets}) {
while ( my $asset = $assets->() ) {
# skip pages we shouldn't see
my $pageLineage = $asset->lineage;
next if ($pageLineage =~ m/^$lineageToSkip/);

View file

@ -266,12 +266,20 @@ sub view {
# get other shelves
my @childShelves = ();
SHELF: foreach my $child (@{$self->getLineage(['children'],{returnObjects=>1,includeOnlyClasses=>['WebGUI::Asset::Wobject::Shelf']})}) {
my $childIter = $self->getLineageIterator(['children'],{includeOnlyClasses=>['WebGUI::Asset::Wobject::Shelf']});
SHELF: while ( 1 ) {
my $child;
eval { $child = $childIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$session->log->error($x->full_message);
next;
}
last unless $child;
next SHELF unless $child->canView;
my $properties = $child->get;
$child->{url} = $child->getUrl;
$child->{title} = $child->getTitle;
push @childShelves, $child;
$properties->{url} = $child->getUrl;
$properties->{title} = $child->getTitle;
push @childShelves, $properties;
}
# get other child skus

View file

@ -369,14 +369,21 @@ for generating an RSS and Atom feeds.
sub getRssFeedItems {
my $self = shift;
my $stories = $self->getLineageIterator(['descendants'],{
my $storyIter = $self->getLineageIterator(['descendants'],{
excludeClasses => ['WebGUI::Asset::Wobject::Folder'],
orderByClause => 'creationDate desc, lineage',
returnObjects => 1,
limit => $self->itemsPerFeed,
});
my $storyData = [];
while (my $story = $stories->()) {
while ( 1 ) {
my $story;
eval { $story = $storyIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$self->session->log->error($x->full_message);
next;
}
last unless $story;
push @{ $storyData }, $story->getRssData;
}
return $storyData;

View file

@ -280,17 +280,19 @@ sub getTemplateVariables {
$item{guid} = WebGUI::HTML::filter(scalar $object->guid, 'javascript');
$item{link} = WebGUI::HTML::filter(scalar $object->link, 'javascript');
my $description = WebGUI::HTML::filter(scalar($object->description), 'javascript');
my $raw_description = WebGUI::HTML::filter($description, 'all');
$raw_description =~ s/^\s+//s;
$item{description} = defined $description ? $description : '';
$item{descriptionFirst100words} = $item{description};
$item{descriptionFirst100words} =~ s/(((\S+)\s+){100}).*/$1/s;
$item{descriptionFirst100words} = $raw_description;
$item{descriptionFirst100words} =~ s/(((\S+)\s+){1,100}).*/$1/ms;
$item{descriptionFirst75words} = $item{descriptionFirst100words};
$item{descriptionFirst75words} =~ s/(((\S+)\s+){75}).*/$1/s;
$item{descriptionFirst75words} =~ s/(((\S+)\s+){1,75}).*/$1/ms;
$item{descriptionFirst50words} = $item{descriptionFirst75words};
$item{descriptionFirst50words} =~ s/(((\S+)\s+){50}).*/$1/s;
$item{descriptionFirst50words} =~ s/(((\S+)\s+){1,50}).*/$1/ms;
$item{descriptionFirst25words} = $item{descriptionFirst50words};
$item{descriptionFirst25words} =~ s/(((\S+)\s+){25}).*/$1/s;
$item{descriptionFirst25words} =~ s/(((\S+)\s+){1,25}).*/$1/ms;
$item{descriptionFirst10words} = $item{descriptionFirst25words};
$item{descriptionFirst10words} =~ s/(((\S+)\s+){10}).*/$1/s;
$item{descriptionFirst10words} =~ s/(((\S+)\s+){1,10}).*/$1/ms;
if ($description =~ /<p>/) {
my $html = $description;
$html =~ tr/\n/ /s;
@ -304,12 +306,12 @@ sub getTemplateVariables {
$item{descriptionFirstParagraph} = $item{descriptionFirst2paragraphs};
$item{descriptionFirstParagraph} =~ s/^(.*?\n).*/$1/s;
}
$item{descriptionFirst4sentences} = $item{description};
$item{descriptionFirst4sentences} =~ s/^((.*?\.){4}).*/$1/s;
$item{descriptionFirst4sentences} = $raw_description;
$item{descriptionFirst4sentences} =~ s/^((.*?\.){1,4}).*/$1/s;
$item{descriptionFirst3sentences} = $item{descriptionFirst4sentences};
$item{descriptionFirst3sentences} =~ s/^((.*?\.){3}).*/$1/s;
$item{descriptionFirst3sentences} =~ s/^((.*?\.){1,3}).*/$1/s;
$item{descriptionFirst2sentences} = $item{descriptionFirst3sentences};
$item{descriptionFirst2sentences} =~ s/^((.*?\.){2}).*/$1/s;
$item{descriptionFirst2sentences} =~ s/^((.*?\.){1,2}).*/$1/s;
$item{descriptionFirstSentence} = $item{descriptionFirst2sentences};
$item{descriptionFirstSentence} =~ s/^(.*?\.).*/$1/s;
push @{$var{item_loop}}, \%item;

View file

@ -3299,18 +3299,20 @@ sequenceNumber');
$self->session->cache->set("query_".$thingId, $query, 30*60);
$paginatePage = $self->session->form->param('pn') || 1;
$currentUrl = $self->session->url->append($currentUrl, "orderBy=".$orderBy) if $orderBy;
$currentUrl = $self->session->url->append($currentUrl, "orderBy=".$orderBy) if $orderBy;
$p = WebGUI::Paginator->new($self->session,$currentUrl,$thingProperties->{thingsPerPage}, undef, $paginatePage);
my $sth = $self->session->db->read($query) if ! $noFields;
my @visibleResults;
while (my $result = $sth->hashRef){
if ($self->canViewThingData($thingId,$result->{thingDataId})){
push(@visibleResults,$result);
if (! $noFields) {
my $sth = $self->session->db->read($query) if ! $noFields;
while (my $result = $sth->hashRef){
if ($self->canViewThingData($thingId,$result->{thingDataId})){
push(@visibleResults,$result);
}
}
}
$p->setDataByArrayRef(\@visibleResults) if ! $noFields;
$p->setDataByArrayRef(\@visibleResults);
$searchResults = $p->getPageData($paginatePage);
foreach my $searchResult (@$searchResults){

View file

@ -253,15 +253,19 @@ sub appendMostPopular {
my $self = shift;
my $var = shift;
my $limit = shift || $self->get("mostPopularCount");
foreach my $asset (@{$self->getLineage(["children"],{returnObjects=>1, limit=>$limit, includeOnlyClasses=>["WebGUI::Asset::WikiPage"], joinClass => 'WebGUI::Asset::WikiPage', orderByClause => 'WikiPage.views DESC'})}) {
if (defined $asset) {
my $assetIter = $self->getLineageIterator(["children"],{limit=>$limit, includeOnlyClasses=>["WebGUI::Asset::WikiPage"], joinClass => 'WebGUI::Asset::WikiPage', orderByClause => 'WikiPage.views DESC'});
while ( 1 ) {
my $asset;
eval { $asset = $assetIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$self->session->log->error($x->full_message);
next;
}
last unless $asset;
push(@{$var->{mostPopular}}, {
title=>$asset->getTitle,
url=>$asset->getUrl,
});
} else {
$self->session->errorHandler->error("Couldn't instanciate wikipage for master ".$self->getId);
}
}
}
@ -729,20 +733,31 @@ Extend the master method to propagate view and edit permissions down to the wiki
=cut
sub processPropertiesFromFormPost {
override processPropertiesFromFormPost => sub {
my $self = shift;
my $groupsChanged =
(($self->session->form->process('groupIdView') ne $self->groupIdView)
or ($self->session->form->process('groupIdEdit') ne $self->groupIdEdit));
my $ret = $self->next::method(@_);
my $ret = super();
if ($groupsChanged) {
foreach my $child (@{$self->getLineage(['children'], {returnObjects => 1})}) {
$child->update({ groupIdView => $self->groupIdView,
groupIdEdit => $self->groupIdEdit });
# XXX Should this do descendants for WikiPage attachments?
my $childIter = $self->getLineageIterator(['children']);
while ( 1 ) {
my $child;
eval { $child = $childIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$self->session->log->error($x->full_message);
next;
}
last unless $child;
$child->update({
groupIdView => $self->get('groupIdView'),
groupIdEdit => $self->get('groupIdEdit')
});
}
}
return $ret;
}
};
#-------------------------------------------------------------------

View file

@ -53,10 +53,19 @@ sub duplicateBranch {
my $childrenOnly = shift;
my $newAsset = $self->duplicate({skipAutoCommitWorkflows=>1,skipNotification=>1});
# Correctly handle positions for Layout assets
my $contentPositions = $self->get("contentPositions");
my $assetsToHide = $self->get("assetsToHide");
foreach my $child (@{$self->getLineage(["children"],{returnObjects=>1})}) {
my $childIter = $self->getLineageIterator(["children"]);
while ( 1 ) {
my $child;
eval { $child = $childIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$self->session->log->error($x->full_message);
next;
}
last unless $child;
my $newChild = $childrenOnly ? $child->duplicate({skipAutoCommitWorkflows=>1, skipNotification=>1}) : $child->duplicateBranch;
$newChild->setParent($newAsset);
my ($oldChildId, $newChildId) = ($child->getId, $newChild->getId);
@ -355,11 +364,18 @@ sub www_editBranchSave {
$urlBase = $form->text("baseUrl");
$endOfUrl = $form->selectBox("endOfUrl");
}
my $descendants = $self->getLineage(["self","descendants"],{returnObjects=>1});
DESCENDANT: foreach my $descendant (@{$descendants}) {
my $descendantIter = $self->getLineageIterator(["self","descendants"]);
while ( 1 ) {
my $descendant;
eval { $descendant = $descendantIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$session->log->error($x->full_message);
next;
}
last unless $descendant;
if ( !$descendant->canEdit ) {
$pb->update(sprintf $i18n->get('skipping %s'), $descendant->getTitle);
next DESCENDANT;
next;
}
$pb->update(sprintf $i18n->get('editing %s'), $descendant->getTitle);
my $url;

View file

@ -68,7 +68,15 @@ sub cut {
$session->db->write("update asset set state='clipboard', stateChangedBy=?, stateChanged=? where assetId=?", [$session->user->userId, time(), $self->getId]);
$session->db->commit;
$self->state("clipboard");
foreach my $asset ($self, @{$self->getLineage(['descendants'], {returnObjects => 1})}) {
my $assetIter = $self->getLineageIterator(['descendants']);
while ( 1 ) {
my $asset;
eval { $asset = $assetIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$session->log->error($x->full_message);
next;
}
last unless $asset;
$asset->purgeCache;
$asset->updateHistory('cut');
}
@ -215,11 +223,18 @@ sub paste {
# Update lineage in search index.
$self->purgeCache;
my $updateAssets = $pastedAsset->getLineage(['self', 'descendants'], {returnObjects => 1});
my $assetIter = $pastedAsset->getLineageIterator(['self', 'descendants']);
while ( 1 ) {
my $asset;
eval { $asset = $assetIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$session->log->error($x->full_message);
next;
}
last unless $asset;
foreach (@{$updateAssets}) {
$outputSub->(sprintf $i18n->get('indexing %s'), $pastedAsset->getTitle) if defined $outputSub;
$_->indexContent();
$asset->indexContent();
}
return 1;
@ -489,7 +504,7 @@ sub www_duplicateList {
foreach my $assetId ($session->form->param("assetId")) {
my $asset = WebGUI::Asset->newById($session,$assetId);
if ($asset->canEdit) {
my $newAsset = $asset->duplicate;
my $newAsset = $asset->duplicate({skipAutoCommitWorkflows => 1, });
$newAsset->update({ title=>$newAsset->getTitle.' (copy)'});
}
}

View file

@ -308,20 +308,22 @@ sub exportBranch {
my $indexFileName = $options->{indexFileName};
my $extrasUploadAction = $options->{extrasUploadAction};
my $rootUrlAction = $options->{rootUrlAction};
my $exportedCount = 0;
my $i18n;
if ( $reportSession ) {
$i18n = WebGUI::International->new($self->session, 'Asset');
}
my $exportedCount = 0;
my $assetIds = $self->exportGetDescendants(undef, $depth);
foreach my $assetId ( @{$assetIds} ) {
my $exportAsset = sub {
my ( $assetId ) = @_;
# Must be created once for each asset, since session is supposed to only handle
# one main asset
my $outputSession = $self->session->duplicate;
my $osGuard = Scope::Guard->new(sub {
$outputSession->close;
$outputSession = undef;
});
my $asset = WebGUI::Asset->newById($outputSession, $assetId);
@ -376,6 +378,16 @@ sub exportBranch {
if ( $reportSession ) {
$reportSession->output->print($i18n->get('done'));
}
#use Devel::Cycle;
#warn "CHECKING on " . ref( $asset ) . ' ID: ' . $asset->getId . "\n";
#find_cycle( $asset );
};
my $assetIds = $self->exportGetDescendants(undef, $depth);
foreach my $assetId ( @{$assetIds} ) {
$exportAsset->( $assetId );
}
# handle symlinking
@ -435,13 +447,23 @@ boolean indicating whether or not this asset is exportable.
sub exportCheckExportable {
my $self = shift;
# We have ourself already, check it first
return 0 unless $self->get('isExportable');
# get this asset's ancestors. return objects as a shortcut since we'd be
# instantiating them all anyway.
my $assets = $self->getLineage( ['ancestors'], { returnObjects => 1 } );
my $assetIter = $self->getLineageIterator( ['self','ancestors'] );
# process each one. return false if any of the assets in the lineage, or
# this asset itself, isn't exportable.
foreach my $asset ( @{$assets}, $self ) {
while ( 1 ) {
my $asset;
eval { $asset = $assetIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$self->session->log->error($x->full_message);
next;
}
last unless $asset;
return 0 unless $asset->get('isExportable');
}
@ -523,6 +545,9 @@ sub exportGetDescendants {
orderByClause => 'assetData.url DESC',
} );
#use Data::Dumper;
#warn "Assets: " . scalar( @$assetIds );
return $assetIds;
}

View file

@ -223,36 +223,106 @@ Returns the number of children this asset has. This excludes assets in the trash
=head3 opts
A hashref of options. Currently only one option is supported.
A hashref of options.
=head4 includeTrash
If this value of this hash key is true, then assets in any state will be counted. Normally,
only those that are published or achived are counted.
=head4 includeOnlyClasses
Only count these classes. Arrayref of class names
=head4 statusToInclude
Arrayref of status to include
=cut
sub getChildCount {
my $self = shift;
my $opts = shift || {};
my $stateWhere = $opts->{includeTrash} ? '' : "asset.state='published' and";
my ($count) = $self->session->db->quickArray("select count(distinct asset.assetId) from asset, assetData where asset.assetId=assetData.assetId and $stateWhere parentId=? and (assetData.status in ('approved', 'archived') or assetData.tagId=?)", [$self->getId, $self->session->scratch->get('versionTag')]);
my $opt = shift || {};
$opt->{ statusToInclude } ||= [ 'approved', 'archived' ];
my $db = $self->session->db;
my $sql = "select count(distinct asset.assetId)
from asset, assetData
where asset.assetId=assetData.assetId
and parentId=?
and (assetData.status in (" . $db->quoteAndJoin( $opt->{statusToInclude} ) . ")
or assetData.tagId=?)";
my @params = ( $self->getId, $self->session->scratch->get('versionTag') );
if ( !$opt->{ includeTrash } ) {
$sql .= ' AND asset.state=?';
push @params, "published";
}
# XXX This code is duplicated in getLineageSql and getDescendantCount. Merge the three ways?
if ( $opt->{ includeOnlyClasses } ) {
my @classes;
if ( !ref $opt->{includeOnlyClasses} eq 'ARRAY' ) {
@classes = $opt->{includeOnlyClasses};
}
else {
@classes = @{ $opt->{includeOnlyClasses} };
}
$sql .= "AND className IN (" . join( ',', ("?") x @classes ) . ")";
push @params, @classes;
}
my $count = $self->session->db->quickScalar($sql, \@params);
return $count;
}
#-------------------------------------------------------------------
=head2 getDescendantCount ( )
=head2 getDescendantCount ( opts )
Returns the number of descendants this asset has. This includes only assets that are published, and not
in the clipboard or trash.
=head3 opts
=head4 includeOnlyClasses
Only count these classes. Arrayref of class names
=head4 statusToInclude
Arrayref of status to include
=cut
sub getDescendantCount {
my $self = shift;
my ($count) = $self->session->db->quickArray("select count(distinct asset.assetId) from asset, assetData where asset.assetId=assetData.assetId and asset.state = 'published' and assetData.status in ('approved','archived') and asset.lineage like ?", [$self->get("lineage")."%"]);
$count--; # have to subtract self
my $opt = shift || {};
$opt->{ statusToInclude } ||= [ 'approved', 'archived' ];
my $db = $self->session->db;
my $sql = "select count(distinct asset.assetId)
from asset, assetData
where asset.assetId = assetData.assetId
and asset.assetId != ?
and asset.state = 'published'
and assetData.status in (" . $db->quoteAndJoin( $opt->{statusToInclude} ) . ")
and asset.lineage like ? ";
my @params = ( $self->getId, $self->get("lineage")."%" );
# XXX This code is duplicated in getLineageSql and getChildCount. Merge the three ways?
if ( $opt->{includeOnlyClasses} ) {
my @classes;
if ( !ref $opt->{includeOnlyClasses} eq 'ARRAY' ) {
@classes = $opt->{includeOnlyClasses};
}
else {
@classes = @{ $opt->{includeOnlyClasses} };
}
$sql .= "AND className IN (" . join( ',', ("?") x @classes ) . ")";
push @params, @classes;
}
my $count = $self->session->db->quickScalar($sql, \@params);
return $count;
}
@ -927,14 +997,21 @@ sub setRank {
my $parentLineage = $self->getParentLineage;
my $reverse = ($newRank < $currentRank) ? 1 : 0;
my $siblings = $self->getLineage(["siblings"],{returnObjects=>1, invertTree=>$reverse});
my $temp = substr($self->session->id->generate(),0,6);
my $previous = $self->lineage;
$self->session->db->beginTransaction;
$outputSub->('moving %s aside', $self->getTitle);
$self->cascadeLineage($temp);
foreach my $sibling (@{$siblings}) {
my $siblingIter = $self->getLineageIterator(["siblings"],{invertTree=>$reverse});
while ( 1 ) {
my $sibling;
eval { $sibling = $siblingIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$self->session->log->error($x->full_message);
next;
}
last unless $sibling;
if (isBetween($sibling->getRank, $newRank, $currentRank)) {
$outputSub->('moving %s', $sibling->getTitle);
$sibling->cascadeLineage($previous);

View file

@ -64,7 +64,15 @@ Turns this package into a package file and returns the storage location object o
sub exportPackage {
my $self = shift;
my $storage = WebGUI::Storage->createTemp($self->session);
foreach my $asset (@{$self->getLineage(["self","descendants"],{returnObjects=>1, statusToInclude=>['approved', 'archived']})}) {
my $assetIter = $self->getLineageIterator(["self","descendants"],{statusToInclude=>['approved', 'archived']});
while ( 1 ) {
my $asset;
eval { $asset = $assetIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$self->session->log->error($x->full_message);
next;
}
last unless $asset;
my $data = $asset->exportAssetData;
$storage->addFileFromScalar($data->{properties}{lineage}.".json", JSON->new->utf8->pretty->encode($data));
foreach my $storageId (@{$data->{storage}}) {
@ -348,10 +356,12 @@ sub www_exportPackage {
=cut
sub www_importPackage {
my $self = shift;
return $self->session->privilege->insufficient() unless ($self->canEdit && $self->session->user->isInGroup(4));
my $storage = WebGUI::Storage->createTemp($self->session);
my $inheritPermissions = $self->session->form->process('inheritPermissions');
my $self = shift;
my $session = $self->session;
return $session->privilege->insufficient() unless ($self->canEdit && $session->user->isInGroup(4));
my $form = $session->form;
my $storage = WebGUI::Storage->createTemp($session);
##This is a hack. It should use the WebGUI::Form::File API to insulate
##us from future form name changes.
@ -359,19 +369,25 @@ sub www_importPackage {
my $error = "";
if ($storage->getFileExtension($storage->getFiles->[0]) eq "wgpkg") {
$error = $self->importPackage($storage, {inheritPermissions => $inheritPermissions});
$error = $self->importPackage(
$storage, {
inheritPermissions => $form->get('inheritPermissions'),
clearPackageFlag => $form->get('clearPackageFlag'),
}
);
}
if (!blessed $error) {
my $i18n = WebGUI::International->new($self->session, "Asset");
if ($error eq 'corrupt') {
return $self->session->style->userStyle($i18n->get("package corrupt"));
}
else {
return $self->session->style->userStyle($i18n->get("package extract error"));
}
my $i18n = WebGUI::International->new($session, "Asset");
my $style = $session->style;
if ($error eq 'corrupt') {
return $style->userStyle($i18n->get("package corrupt"));
}
else {
return $style->userStyle($i18n->get("package extract error"));
}
}
# Handle autocommit workflows
if (WebGUI::VersionTag->autoCommitWorkingIfEnabled($self->session, {
if (WebGUI::VersionTag->autoCommitWorkingIfEnabled($session, {
allowComments => 1,
returnUrl => $self->getUrl,
}) eq 'redirect') {

View file

@ -16,6 +16,7 @@ package WebGUI::Asset;
use strict;
use WebGUI::Utility qw(isIn formatBytes);
use JSON;
=head1 NAME
@ -140,24 +141,23 @@ sub purge {
}
# assassinate the offspring
my $kids = $self->getLineage(["children"],{
returnObjects => 1,
my $childIter = $self->getLineageIterator(["children"],{
statesToInclude => [qw(published clipboard clipboard-limbo trash trash-limbo)],
statusToInclude => [qw(approved archived pending)],
});
foreach my $kid (@{$kids}) {
# Technically get lineage should never return an undefined object from getLineage when called like this, but it did so this saves the world from destruction.
if (defined $kid) {
unless ($kid->purge) {
while ( 1 ) {
my $child;
eval { $child = $childIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$session->log->error($x->full_message);
next;
}
last unless $child;
unless ($child->purge) {
$session->errorHandler->security("delete one of (".$self->getId.")'s children which is a system protected page");
$outputSub->(sprintf $i18n->get('Trying to delete system page %s. Aborting'), $self->getTitle);
return 0;
}
}
else {
$outputSub->($i18n->get('Undefined child'));
$session->errorHandler->error("getLineage returned an undefined object in the AssetTrash->purge method. Unable to purge asset.");
}
}
# Delete shortcuts to this asset
@ -265,7 +265,15 @@ sub trash {
return undef;
}
foreach my $asset ($self, @{$self->getLineage(['descendants'], {returnObjects => 1})}) {
my $assetIter = $self->getLineageIterator(['self','descendants']);
while ( 1 ) {
my $asset;
eval { $asset = $assetIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$session->log->error($x->full_message);
next;
}
last unless $asset;
$outputSub->($i18n->get('Clearing search index'));
my $index = WebGUI::Search::Index->new($asset);
$index->delete;
@ -418,21 +426,37 @@ sub www_manageTrash {
assetManager.AddColumn('".$i18n->get("last updated")."','','center','');
assetManager.AddColumn('".$i18n->get("size")."','','right','');
\n";
# To avoid string escaping issues
my $json = JSON->new;
my $amethod = sub {
my ($method, @args) = @_;
my $array = $json->encode(\@args);
$array =~ s/^\[//;
$array =~ s/\]$//;
$output .= "assetManager.$method($array);\n";
};
foreach my $child (@{$self->getAssetsInTrash($limit)}) {
my $title = $child->getTitle;
$title =~ s/\'/\\\'/g;
my $title = $child->getTitle;
my $plus =$child->getChildCount({includeTrash => 1}) ? "+ " : "&nbsp;&nbsp;&nbsp;&nbsp;";
$output .= "assetManager.AddLine('"
.WebGUI::Form::checkbox($self->session, {
name=>'assetId',
value=>$child->getId
})
."','" . $plus . "<a href=\"".$child->getUrl("op=assetManager")."\">" . $title
."</a>','<p style=\"display:inline;vertical-align:middle;\"><img src=\"".$child->getIcon(1)."\" style=\"vertical-align:middle;border-style:none;\" alt=\"".$child->getName."\" /></p> ".$child->getName
."','".$self->session->datetime->epochToHuman($child->get("revisionDate"))
."','".formatBytes($child->get("assetSize"))."');\n";
$output .= "assetManager.AddLineSortData('','".$title."','".$child->getName
."','".$child->get("revisionDate")."','".$child->get("assetSize")."');\n";
$amethod->('AddLine',
WebGUI::Form::checkbox($self->session, {
name=>'assetId',
value=>$child->getId
}),
qq($plus<a href=").$child->getUrl("op=assetManager")
.qq(">$title</a>),
'<p style="display:inline;vertical-align:middle;"><img src="'
.$child->getIcon(1)
.'" style="vertical-align:middle;border-style:none;" alt='
.$child->getName .'" /></p> ' . $child->getName,
$self->session->datetime->epochToHuman($child->get("revisionDate")),
formatBytes($child->get("assetSize"))
);
$amethod->('AddLineSortData',
'', $title, $child->getName,
$child->get('revisionDate'), $child->get('assetSize')
);
}
$output .= '
assetManager.AddButton("'.$i18n->get("restore").'","restoreList","manageTrash");

View file

@ -170,7 +170,6 @@ around new => sub {
}
};
#-------------------------------------------------------------------
=head2 readAllConfigs ( )

View file

@ -49,20 +49,25 @@ sub handler {
# Only handle op=ajaxGetI18N
return undef unless ( $session->form->get( "op" ) eq "ajaxGetI18N" );
my $json = $session->form->get( "request" );
my $namespaces = JSON->new->decode( $json );
my $i18n = WebGUI::International->new( $session );
my $response = {};
my $json = $session->form->get( "request" );
my $namespaces = eval { JSON->new->decode( $json ) };
unless ($@) {
my $i18n = WebGUI::International->new( $session );
for my $ns ( keys %{ $namespaces } ) {
for my $key ( @{ $namespaces->{ $ns } } ) {
$response->{ $ns }->{ $key } = $i18n->get( $key, $ns );
for my $ns ( keys %{ $namespaces } ) {
for my $key ( @{ $namespaces->{ $ns } } ) {
$response->{ $ns }->{ $key } = $i18n->get( $key, $ns );
}
}
}
else {
$session->log->warn("User ".$session->user->username." tried to execute ajaxGetI18n but could not decode JSON string: $json");
}
$session->http->setMimeType( "application/json" );
return JSON->new->encode( $response );
}
1;

View file

@ -384,13 +384,20 @@ ENDHTML
### Crumbtrail
my $crumb_markup = '<li><a href="%s">%s</a> &gt;</li>';
my $ancestors = $currentAsset->getLineage( ['ancestors'], { returnObjects => 1 } );
my $ancestorIter = $currentAsset->getLineageIterator( ['ancestors'] );
$output .= '<ol id="crumbtrail">';
for my $asset ( @{ $ancestors } ) {
while ( 1 ) {
my $ancestor;
eval { $ancestor = $ancestorIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$session->log->error($x->full_message);
next;
}
last unless $ancestor;
$output .= sprintf $crumb_markup,
$asset->getUrl( 'op=assetManager;method=manage' ),
$asset->get( "menuTitle" ),
$ancestor->getUrl( 'op=assetManager;method=manage' ),
$ancestor->get( "menuTitle" ),
;
}
@ -476,6 +483,8 @@ EOHTML
. WebGUI::Form::hidden($session, {name=>"func", value=>"importPackage"})
. '<div><input type="file" name="packageFile" size="30" style="font-size: 10px;" /></div>'
. '<div style="font-size: 10px">'
. WebGUI::Form::checkbox($session, { label => $i18n->get('clear package flag'), checked => 0, name => 'clearPackageFlag', value => 1 })
. '<br />'
. WebGUI::Form::checkbox($session, { label => $i18n->get('inherit parent permissions'), checked => 1, name => 'inheritPermissions', value => 1 })
. ' &nbsp; ' . WebGUI::Form::submit($session, { value=>$i18n->get("import"), 'extras' => ' ' })
. '</div>'

View file

@ -61,7 +61,6 @@ sub handler {
}
my $pages = WebGUI::Asset->getRoot($session)->getLineageIterator(["self","descendants"],{
returnObjects => 1,
includeOnlyClasses => ["WebGUI::Asset::Wobject::Layout"],
whereClause => $whereClause,
limit => 20000

View file

@ -184,8 +184,15 @@ sub www_assetTree {
$session->http->setCacheControl("none");
my $base = WebGUI::Asset->newByUrl($session) || WebGUI::Asset->getRoot($session);
my @crumb;
my $ancestors = $base->getLineage(["self","ancestors"],{returnObjects=>1});
foreach my $ancestor (@{$ancestors}) {
my $ancestorIter = $base->getLineageIterator(["self","ancestors"]);
while ( 1 ) {
my $ancestor;
eval { $ancestor = $ancestorIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$session->log->error($x->full_message);
next;
}
last unless $ancestor;
my $url = $ancestor->getUrl("op=formHelper;sub=assetTree;class=Asset;formId=".$session->form->process("formId"));
$url .= ";classLimiter=".$session->form->process("classLimiter","className") if ($session->form->process("classLimiter","className"));
push(@crumb,'<a href="'.$url.'" class="crumb">'.$ancestor->get("menuTitle").'</a>');
@ -224,10 +231,17 @@ sub www_assetTree {
</style></head><body>
<div class="base">
<div class="crumbTrail">'.join(" &gt; ", @crumb)."</div><br />\n";
my $children = $base->getLineage(["children","self"],{returnObjects=>1});
my $childIter = $base->getLineageIterator(["children","self"]);
my $i18n = WebGUI::International->new($session);
my $limit = $session->form->process("classLimiter","className");
foreach my $child (@{$children}) {
while ( 1 ) {
my $child;
eval { $child = $childIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$session->log->error($x->full_message);
next;
}
last unless $child;
next unless $child->canView;
if ($limit eq "" || $child->get("className") =~ /^$limit/) {
my $tempChild = $child->get("title");

View file

@ -99,20 +99,4 @@ sub isDynamicCompatible {
return 1;
}
#-------------------------------------------------------------------
=head2 toHtml ( )
Renders an email address field.
=cut
sub toHtml {
my $self = shift;
$self->session->style->setScript($self->session->url->extras('emailCheck.js'));
$self->{_params}{extras} .= ' onchange="emailCheck(this.value)" ';
return $self->SUPER::toHtml;
}
1;

View file

@ -208,13 +208,27 @@ JS
my $output = '<div class="nav">';
my $base = WebGUI::Asset->newByUrl($session) || WebGUI::Asset->getRoot($session);
my @crumb;
my $ancestors = $base->getLineage(["self","ancestors"],{returnObjects=>1});
foreach my $ancestor (@{$ancestors}) {
my $ancestorIter = $base->getLineageIterator(["self","ancestors"]);
while ( 1 ) {
my $ancestor;
eval { $ancestor = $ancestorIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$session->log->error($x->full_message);
next;
}
last unless $ancestor;
push(@crumb,'<a href="'.$ancestor->getUrl("op=formHelper;class=HTMLArea;sub=pageTree").'" class="crumb">'.$ancestor->get("menuTitle").'</a>');
}
$output .= '<div class="crumbTrail">'.join(" &gt; ", @crumb)."</div>\n<ul>";
my $children = $base->getLineage(["children"],{returnObjects=>1});
foreach my $child (@{$children}) {
my $childIter = $base->getLineageIterator(["children"]);
while ( 1 ) {
my $child;
eval { $child = $childIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$session->log->error($x->full_message);
next;
}
last unless $child;
next unless $child->canView;
$output .= '<li><a href="#" class="selectLink" onclick="selectLink(\'' . $child->get('url') . '\'); return false;">['
. $i18n->get("select") . ']</a> <a href="' . $child->getUrl("op=formHelper;class=HTMLArea;sub=pageTree")
@ -253,8 +267,15 @@ JS
my @crumb;
my $media;
my $ancestors = $base->getLineage(["self","ancestors"],{returnObjects=>1});
foreach my $ancestor (@{$ancestors}) {
my $ancestorIter = $base->getLineageIterator(["self","ancestors"]);
while ( 1 ) {
my $ancestor;
eval { $ancestor = $ancestorIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$session->log->error($x->full_message);
next;
}
last unless $ancestor;
push(@crumb,'<a href="'.$ancestor->getUrl("op=formHelper;class=HTMLArea;sub=imageTree").'" class="crumb">'.$ancestor->get("menuTitle").'</a>');
if ($ancestor->get('assetId') eq 'PBasset000000000000003') {
$media = $ancestor;
@ -276,8 +297,8 @@ JS
$output .= '<div class="crumbTrail">'.join(" &gt; ", @crumb)."</div>\n<ul>";
my $useAssetUrls = $session->config->get("richEditorsUseAssetUrls");
my $children = $base->getLineage(["children"],{returnObjects=>1});
foreach my $child (@{$children}) {
my $children = $base->getLineage(["children"]);
while ( my $child = $children->() ) {
next unless $child->canView;
$output .= '<li>';
if ($child->isa('WebGUI::Asset::File::Image')) {

View file

@ -125,8 +125,18 @@ the available Rich Text Editor assets.
sub getOptions {
my $self = shift;
my $editors = WebGUI::Asset->getRoot($self->session)->getLineage(['descendants'], {includeOnlyClasses => ['WebGUI::Asset::RichEdit'], returnObjects => 1});
my %options = map { $_->getId => $_->getTitle } @$editors;
my $editorIter = WebGUI::Asset->getRoot($self->session)->getLineageIterator( ['descendants'], {includeOnlyClasses => ['WebGUI::Asset::RichEdit']});
my %options;
while ( 1 ) {
my $editor;
eval { $editor = $editorIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$self->session->log->error($x->full_message);
next;
}
last unless $editor;
$options{ $editor->getId } = $editor->getTitle;
}
return \%options;
}

View file

@ -22,6 +22,7 @@ use WebGUI::Pluggable;
require WebGUI::Asset;
use WebGUI::International;
use WebGUI::DatabaseLink;
use Scalar::Util qw( weaken );
=head1 NAME
@ -1504,6 +1505,7 @@ sub new {
my $class = shift;
$self->{_session} = shift;
weaken( $self->{_session} );
$self->{_groupId} = shift;
my $override = shift;
my $noAdmin = shift;

View file

@ -29,6 +29,10 @@ our $HELP = {
name => 'searchForm_keywords',
description => 'helpvar searchForm_keywords',
},
{
name => 'searchForm_location',
description => 'helpvar searchForm_location',
},
{
name => 'searchForm_className',
description => 'helpvar searchForm_className',

View file

@ -62,6 +62,18 @@ our $HELP = {
name => 'ownerUsername',
description => 'helpvar ownerUsername',
},
{
name => 'ownerAlias',
description => 'helpvar ownerAlias',
},
{
name => 'ownerId',
description => 'helpvar ownerId',
},
{
name => 'ownerProfileUrl',
description => 'helpvar ownerProfileUrl',
},
{
name => 'thumbnailUrl',
description => 'helpvar thumbnailUrl',

View file

@ -24,6 +24,9 @@ our $HELP = {
{
name => "language_langEng",
},
{
name => "language_isCurrent",
},
],
},
{

View file

@ -44,20 +44,25 @@ This macro takes a templateId to show the links
sub process {
my $session = shift;
my $templateId = shift || "_aE16Rr1-bXBf8SIaLZjCg";
my $template = eval { WebGUI::Asset::Template->newById($session, $templateId); };
my $template = eval { WebGUI::Asset->newById($session, $templateId); };
if (Exception::Class->caught()) {
return "Could not instanciate template with id [$templateId]" unless $template;
}
my $i18n = WebGUI::International->new($session);
my $languages = $i18n->getLanguages();
my $currentLanguage = $session->scratch->get('language')
? $session->scratch->get('language')
: $session->user->profileField('language');
my @lang_loop = ();
foreach my $language ( keys %$languages ) {
my $isCurrentLanguage = $currentLanguage eq $language ? 1 : 0;
push @lang_loop, {
language_url => '?op=setLanguage;language=' . $language,
language_lang => $i18n->getLanguage($language , 'label'),
language_langAbbr => $i18n->getLanguage($language, 'languageAbbreviation'),
language_langAbbrLoc => $i18n->getLanguage($language, 'locale'),
language_langEng => $language,
language_isCurrent => $isCurrentLanguage,
};
}
my %vars = (

View file

@ -24,6 +24,7 @@ use WebGUI::Exception;
use Scalar::Util ();
use Try::Tiny;
use namespace::clean;
use Scalar::Util qw( weaken );
=head1 NAME

View file

@ -447,13 +447,10 @@ sub search {
if ( ! eval { WebGUI::Pluggable::load($className) } ) {
$self->session->errorHandler->fatal($@);
}
foreach my $definition (@{$className->definition($self->session)}) {
unless ($definition->{tableName} eq "asset") {
my $tableName = $definition->{tableName};
push @$join,
"left join $tableName on assetData.assetId=".$tableName.".assetId and assetData.revisionDate=".$tableName.".revisionDate";
}
last;
TABLE: foreach my $tableName ($className->meta->get_tables) {
next TABLE if $tableName eq 'assetData';
push @{ $join },
"left join $tableName on assetData.assetId=".$tableName.".assetId and assetData.revisionDate=".$tableName.".revisionDate";
}
}
# Get only the latest revision

View file

@ -19,6 +19,7 @@ use 5.010;
use CHI;
use File::Temp qw( tempdir );
use Scalar::Util qw( weaken );
use WebGUI::Config;
use WebGUI::SQL;
use WebGUI::User;

View file

@ -19,6 +19,7 @@ use DateTime;
use DateTime::Format::Strptime;
use DateTime::Format::Mail;
use DateTime::TimeZone;
use Scalar::Util qw( weaken );
use Tie::IxHash;
use WebGUI::International;
use WebGUI::Utility qw(round isIn);

View file

@ -292,4 +292,3 @@ sub warn {
}
1;

View file

@ -19,7 +19,6 @@ use WebGUI::International;
use Tie::IxHash;
use Scalar::Util qw(weaken);
=head1 NAME
Package WebGUI::Session::Icon

View file

@ -17,6 +17,7 @@ package WebGUI::Session::Id;
use strict;
use Digest::MD5 ();
use Scalar::Util qw( weaken );
use Time::HiRes qw( gettimeofday usleep );
use MIME::Base64 qw(encode_base64 decode_base64);
use Scalar::Util qw(weaken);

View file

@ -15,6 +15,7 @@ package WebGUI::Session::Privilege;
=cut
use strict;
use Scalar::Util qw( weaken );
use WebGUI::International;
use WebGUI::Operation::Auth;
use Scalar::Util qw(weaken);

View file

@ -16,6 +16,8 @@ package WebGUI::Session::Style;
use strict;
use Tie::CPHash;
use Scalar::Util qw( weaken );
use WebGUI::International;
use WebGUI::Macro;
require WebGUI::Asset;

View file

@ -18,6 +18,7 @@ package WebGUI::Session::Url;
use strict;
use URI;
use URI::Escape;
use Scalar::Util qw( weaken );
use WebGUI::International;
use WebGUI::Utility;
use Scalar::Util qw(weaken);

View file

@ -905,6 +905,38 @@ sub www_ajaxSetCartItemShippingId {
#-------------------------------------------------------------------
=head2 www_checkout ( )
All checks and work for checking out are contained in here.
=cut
sub www_checkout {
my $self = shift;
my $session = $self->session;
##Setting a shipping address greatly simplifies the Transaction
if (! $self->requiresShipping && ! $self->get('shippingAddressId')) {
$self->update({shippingAddressId => $self->get('billingAddressId')});
}
if ($self->readyForCheckout()) {
my $total = $self->calculateTotal;
##Handle rounding errors, and checkout immediately if the amount is 0 since
##at least the ITransact driver won't accept $0 checkout.
if (sprintf('%.2f', $total + $self->calculateShopCreditDeduction($total)) eq '0.00') {
my $transaction = WebGUI::Shop::Transaction->create($session, {cart => $self});
$transaction->completePurchase('zero', 'success', 'success');
$self->onCompletePurchase;
$transaction->sendNotifications();
return $transaction->thankYou();
}
my $gateway = $self->getPaymentGateway;
return $gateway->www_getCredentials;
}
return $self->www_view;
}
#-------------------------------------------------------------------
=head2 www_lookupPosUser ( )
Adds a Point of Sale user to the cart.
@ -950,24 +982,8 @@ sub www_update {
return undef;
}
elsif ($session->form->get('checkout')) {
##Setting a shipping address greatly simplifies the Transaction
if (! $self->requiresShipping && ! $self->get('shippingAddressId')) {
$self->update({shippingAddressId => $self->get('billingAddressId')});
}
if ($self->readyForCheckout()) {
my $total = $self->calculateTotal;
##Handle rounding errors, and checkout immediately if the amount is 0 since
##at least the ITransact driver won't accept $0 checkout.
if (sprintf('%.2f', $total + $self->calculateShopCreditDeduction($total)) eq '0.00') {
my $transaction = WebGUI::Shop::Transaction->create($session, {cart => $self});
$transaction->completePurchase('zero', 'success', 'success');
$self->onCompletePurchase;
$transaction->sendNotifications();
return $transaction->thankYou();
}
my $gateway = $self->getPaymentGateway;
return $gateway->www_getCredentials;
}
##Shortcut method for checkout, so that the cart form contents don't get lost.
return $self->www_checkout();
}
return $self->www_view;
}

View file

@ -21,6 +21,7 @@ use WebGUI::Workflow::Instance;
use JSON ();
use WebGUI::ProfileField;
use Tie::CPHash;
use Scalar::Util qw( weaken );
=head1 NAME
@ -1070,6 +1071,7 @@ sub new {
my $self = $session->cache->get("user_" . $userId) || {};
bless $self, $class;
$self->{_session} = $session;
weaken( $self->{_session} );
unless ($self->{_userId} && $self->{_user}{username}) {
my %user;
tie %user, 'Tie::CPHash';

View file

@ -136,7 +136,7 @@ sub www_pickStyle {
my @styles;
for my $styleId ( @styleIds ) {
next if grep { $_ eq $styleId } @skipStyleIds;
my $style = WebGUI::Asset->newByDynamicClass( $session, $styleId );
my $style = WebGUI::Asset->newById( $session, $styleId );
push @styles, $style;
}
@ -256,10 +256,16 @@ sub www_chooseContentSave {
# update default site style
$session->setting->set( "userFunctionStyleId", $self->get('styleTemplateId') );
foreach my $asset ( @{ $home->getLineage( [ "self", "descendants" ], { returnObjects => 1 } ) } ) {
if ( defined $asset ) {
$asset->update( { styleTemplateId => $self->get("styleTemplateId") } );
my $assetIter = $home->getLineageIterator( [ "self", "descendants" ] );
while ( 1 ) {
my $asset;
eval { $asset = $assetIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$session->log->error($x->full_message);
next;
}
last unless $asset;
$asset->update( { styleTemplateId => $self->get("styleTemplateId") } );
}
# add new pages

View file

@ -363,7 +363,15 @@ sub www_defaultStyleSave {
# update default site style
$session->setting->set( "userFunctionStyleId", $self->get('styleTemplateId') );
my $home = WebGUI::Asset->getDefault( $session );
foreach my $asset ( @{ $home->getLineage( [ "self", "descendants" ], { returnObjects => 1 } ) } ) {
my $assetIter = $home->getLineageIterator( [ "self", "descendants" ] );
while ( 1 ) {
my $asset;
eval { $asset = $assetIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$session->log->error($x->full_message);
next;
}
last unless $asset;
if ( defined $asset ) {
$asset->update( { styleTemplateId => $self->get("styleTemplateId") } );
}

View file

@ -72,26 +72,39 @@ sub execute {
my $self = shift;
my $session = $self->session;
my $epoch = time();
my $expireTime = $epoch + $self->getTTL();
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(
my $folderIter = $archive->getLineageIterator(
['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;
FOLDER: while ( 1 ) {
my $folder;
eval { $folder = $folderIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$session->log->error($x->full_message);
next;
}
last unless $folder;
my $storyIter = $folder->getLineageIterator( ['children'] );
STORY: while ( 1 ) {
my $story;
eval { $story = $storyIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$session->log->error($x->full_message);
next;
}
last unless $story;
$story->update({ status => 'archived' });
if (time() > $expireTime) {
return $self->WAITING(1);
}
}
$folder->update({ status => 'archived' });
}

View file

@ -107,19 +107,25 @@ sub execute {
# kill temporary assets
my $tempspace = WebGUI::Asset->getTempspace($self->session);
my $children = $tempspace->getLineage(["children"], {
returnObjects => 1,
my $childIter = $tempspace->getLineageIterator(["children"], {
statesToInclude => [qw(trash clipboard published)],
statusToInclude => [qw(pending archived approved)],
});
foreach my $asset (@{$children}) {
if (time() - $asset->get("revisionDate") > $self->get("storageTimeout")) {
unless ($asset->purge) {
while ( 1 ) {
my $child;
eval { $child = $childIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$self->session->log->error($x->full_message);
next;
}
last unless $child;
if (time() - $child->get("revisionDate") > $self->get("storageTimeout")) {
unless ($child->purge) {
return $self->ERROR;
}
}
# taking too long, give up
return $self->WAITING(1) if (time() - $start > 50);
return $self->WAITING(1) if (time() - $start > $self->getTTL);
}
# kill temporary files

View file

@ -76,13 +76,20 @@ sub execute {
# keep track of how much time it's taking
my $start = time;
my $limit = 2_500;
my $timeLimit = 60;
my $timeLimit = $self->getTTL;
my $list = $root->getLineage( ['descendants'], { returnObjects => 1,
my $emsFormIter = $root->getLineageIterator( ['descendants'], {
includeOnlyClasses => ['WebGUI::Asset::EMSSubmissionForm'],
} );
for my $emsForm ( @$list ) {
while ( 1 ) {
my $emsForm;
eval { $emsForm = $emsFormIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$session->log->error($x->full_message);
next;
}
last unless $emsForm;
my $daysBeforeCleanup = $emsForm->get('daysBeforeCleanup') ;
next if ! $daysBeforeCleanup;
my $whereClause = q{ submissionStatus='denied' };
@ -91,12 +98,19 @@ sub execute {
}
my $checkDate = time - ( 60*60*24* $daysBeforeCleanup );
$whereClause .= q{ and assetData.lastModified < } . $checkDate;
my $res = $emsForm->getLineage(['children'],{ returnObjects => 1,
my $submissionIter = $emsForm->getLineageIterator(['children'],{
joinClass => 'WebGUI::Asset::EMSSubmission',
includeOnlyClasses => ['WebGUI::Asset::EMSSubmission'],
whereClause => $whereClause,
} );
for my $submission ( @$res ) {
while ( 1 ) {
my $submission;
eval { $submission = $submissionIter->() };
if ( my $x = WebGUI::Error->caught('WebGUI::Error::ObjectNotFound') ) {
$session->log->error($x->full_message);
next;
}
last unless $submission;
$submission->purge;
$limit--;
return $self->WAITING(1) if ! $limit or time > $start + $timeLimit;

View file

@ -87,6 +87,7 @@ sub execute {
}
}
my $sth = $self->session->db->read("select groupId,deleteOffset,groupCacheTimeout from groups");
my $expireTime = time() + $self->getTTL();
while (my $data = $sth->hashRef) {
if ($data->{groupCacheTimeout} > 0) {
# there is no need to wait deleteOffset days for expired external group cache data
@ -94,6 +95,10 @@ sub execute {
} else {
$self->session->db->write("delete from groupings where groupId=? and expireDate < ?", [$data->{groupId}, time()-$data->{deleteOffset}]);
}
if (time() > $expireTime) {
$sth->finish;
return $self->WAITING(1);
}
}
return $self->COMPLETE;
}

View file

@ -78,8 +78,9 @@ sub execute {
my $limit = 2_500;
my $timeLimit = 60;
my $list = $root->getLineage( ['descendants'], { returnObjects => 1,
my $list = $root->getLineage( ['descendants'], {
includeOnlyClasses => ['WebGUI::Asset::EMSSubmissionForm'],
returnObjects => 1,
} );
for my $emsForm ( @$list ) {
@ -88,6 +89,7 @@ sub execute {
joinClass => 'WebGUI::Asset::EMSSubmission',
includeOnlyClasses => ['WebGUI::Asset::EMSSubmission'],
whereClause => $whereClause,
returnObjects => 1,
} );
for my $submission ( @$res ) {
my $properties = { className => 'WebGUI::Asset::Sku::EMSTicket' };

View file

@ -1271,6 +1271,12 @@ Couldn't open %-s because %-s <br />
context => q{Error message in Asset.pm},
},
'clear package flag' => {
message => q{Clear package flag},
lastUpdate => 1275419384,
context => q{Package import option in admin console},
},
'need a userId parameter' => {
message => q{need a userId parameter},
lastUpdated => 0,

View file

@ -419,6 +419,11 @@ our $I18N = {
message => 'Search by keyword.',
lastUpdated => 0,
},
'helpvar searchForm_location' => {
message => 'Search by location.',
lastUpdated => 0,
},
'helpvar searchForm_className' => {
message => 'Search by class name.',
@ -616,6 +621,12 @@ our $I18N = {
lastUpdated => 0,
context => "Label for the 'Keywords' input for the search form. 'Tags' is used because Keywords may be confused with the generic, all-inclusive search box.",
},
'template search field location' => {
message => "Location",
lastUpdated => 0,
context => "Label for the 'Location' input for the search form.",
},
'template search field className' => {
message => "Search Type",

View file

@ -88,17 +88,17 @@ our $I18N = {
},
'helpvar isNewPhoto' => {
message => 'This variable is true if the user is adding a new Photo',
message => 'This variable is true if the user is adding a new Photo.',
lastUpdated => 0,
},
'helpvar commentForm_start' => {
message => 'Begin the comment form',
message => 'Begin the comment form.',
lastUpdated => 0,
},
'helpvar commentForm_end' => {
message => 'End the comment form',
message => 'End the comment form.',
lastUpdated => 0,
},
@ -108,37 +108,52 @@ our $I18N = {
},
'helpvar commentForm_submit' => {
message => 'Submit the comment form',
message => 'Submit the comment form.',
lastUpdated => 0,
},
'helpvar canComment' => {
message => 'This is true if the current user can comment on this photo',
message => 'This is true if the current user can comment on this photo.',
lastUpdated => 0,
},
'helpvar canEdit' => {
message => 'This is true if the current user can edit this photo',
message => 'This is true if the current user can edit this photo.',
lastUpdated => 0,
},
'helpvar fileUrl' => {
message => 'The URL to the normal-sized photo',
message => 'The URL to the normal-sized photo.',
lastUpdated => 0,
},
'helpvar numberOfComments' => {
message => 'The total number of comments on this photo',
message => 'The total number of comments on this photo.',
lastUpdated => 0,
},
'helpvar ownerUsername' => {
message => 'The username of the user who posted this photo',
message => 'The username of the user who posted this photo.',
lastUpdated => 0,
},
'helpvar ownerAlias' => {
message => 'The alias of the user who posted this photo. Defaults to the username if not available.',
lastUpdated => 0,
},
'helpvar ownerId' => {
message => 'The Id of the user who posted this photo.',
lastUpdated => 0,
},
'helpvar ownerProfileUrl' => {
message => 'The URL to the profile of the user who posted this photo.',
lastUpdated => 0,
},
'helpvar thumbnailUrl' => {
message => 'The URL to the thumbnail of this photo',
message => 'The URL to the thumbnail of this photo.',
lastUpdated => 0,
},
@ -148,12 +163,12 @@ our $I18N = {
},
'helpvar url_demote' => {
message => 'The URL to demote this photo in rank. Will return the user directly to the parent GalleryAlbum edit form',
message => 'The URL to demote this photo in rank. Will return the user directly to the parent GalleryAlbum edit form.',
lastUpdated => 0,
},
'helpvar url_edit' => {
message => 'The URL to edit this photo',
message => 'The URL to edit this photo.',
lastUpdated => 0,
},
@ -168,12 +183,12 @@ our $I18N = {
},
'helpvar url_listFilesForOwner' => {
message => 'The URL to list files and albums posted by the owner of this photo',
message => 'The URL to list files and albums posted by the owner of this photo.',
lastUpdated => 0,
},
'helpvar url_promote' => {
message => 'The URL to promote this photo in rank. Will return the user directly to the parent GalleryAlbum edit form',
message => 'The URL to promote this photo in rank. Will return the user directly to the parent GalleryAlbum edit form.',
lastUpdated => 0,
},
@ -193,22 +208,22 @@ our $I18N = {
},
'helpvar exifLoop' => {
message => 'A loop of EXIF tags',
message => 'A loop of EXIF tags.',
lastUpdated => 0,
},
'helpvar exifLoop tag' => {
message => 'The name of the EXIF tag',
message => 'The name of the EXIF tag.',
lastUpdated => 0,
},
'helpvar exifLoop value' => {
message => 'The value of the EXIF tag',
message => 'The value of the EXIF tag.',
lastUpdated => 0,
},
'helpvar url_addArchive' => {
message => 'The URL to add an archive to the parent Album',
message => 'The URL to add an archive to the parent Album.',
lastUpdated => 0,
},
@ -263,7 +278,7 @@ our $I18N = {
},
'helpvar commentLoop' => {
message => 'Loop over a page of comments to this photo',
message => 'Loop over a page of comments to this photo.',
lastUpdated => 0,
},
@ -278,12 +293,12 @@ our $I18N = {
},
'helpvar commentLoop creationDate' => {
message => 'The creation date of the comment',
message => 'The creation date of the comment.',
lastUpdated => 0,
},
'helpvar commentLoop bodyText' => {
message => 'The body of the comment',
message => 'The body of the comment.',
lastUpdated => 0,
},
@ -298,7 +313,7 @@ our $I18N = {
},
'helpvar commentLoop_pageBar' => {
message => 'The bar to navigate through pages of comments',
message => 'The bar to navigate through pages of comments.',
lastUpdated => 0,
},
@ -370,13 +385,13 @@ our $I18N = {
'template view title' => {
message => 'Photo Details',
lastUpdated => 0,
context => 'The title of the default view of Photo assets',
context => 'The title of the default view of Photo assets.',
},
'template view details' => {
message => 'Details',
lastUpdated => 0,
context => 'List of information about the photo',
context => 'List of information about the photo.',
},
'more details' => {
@ -492,6 +507,12 @@ our $I18N = {
lastUpdated => 0,
context => 'Label for photos that are not friends only',
},
'template filesForUser' => {
message => 'more photos',
lastUpdated => 0,
context => 'Label for the link to the users\' photos.',
},
'template assetName' => {
message => 'Photo',

View file

@ -280,48 +280,48 @@ Feed Order: Items will be in the order they appeared in the feed}
},
'descriptionFirst100words' => {
message => q{The first 100 words of the description.},
lastUpdated => 0,
message => q{The first 100 words of the description, with all HTML removed.},
lastUpdated => 1274815483,
},
'descriptionFirst75words' => {
message => q{The first 75 words of the description.},
lastUpdated => 0,
message => q{The first 75 words of the description, with all HTML removed.},
lastUpdated => 1274815485,
},
'descriptionFirst50words' => {
message => q{The first 50 words of the description.},
lastUpdated => 0,
message => q{The first 50 words of the description, with all HTML removed.},
lastUpdated => 1274815486,
},
'descriptionFirst25words' => {
message => q{The first 25 words of the description.},
lastUpdated => 0,
message => q{The first 25 words of the description, with all HTML removed.},
lastUpdated => 1274815488,
},
'descriptionFirst10words' => {
message => q{The first 10 words of the description.},
lastUpdated => 0,
message => q{The first 10 words of the description, with all HTML removed.},
lastUpdated => 1274815490,
},
'descriptionFirst2paragraphs' => {
message => q{The first 2 paragraphs of the description.},
message => q{The first 2 paragraphs of the description, with all HTML removed.},
lastUpdated => 0,
},
'descriptionFirstParagraph' => {
message => q{The first paragraph of the description.},
message => q{The first paragraph of the description, with all HTML removed.},
lastUpdated => 0,
},
'descriptionFirst4sentences' => {
message => q{The first 4 sentences of the description.},
lastUpdated => 0,
message => q{The first 4 sentences of the description, with all HTML removed.},
lastUpdated => 1274816277,
},
'descriptionFirst3sentences' => {
message => q{The first 3 sentences of the description.},
lastUpdated => 0,
message => q{The first 3 sentences of the description, with all HTML removed.},
lastUpdated => 1274816276,
},
'descriptionFirst2sentences' => {
message => q{The first 2 sentences of the description.},
lastUpdated => 0,
message => q{The first 2 sentences of the description, with all HTML removed.},
lastUpdated => 1274816274,
},
'descriptionFirstSentence' => {
message => q{The first sentence of the description.},
lastUpdated => 0,
message => q{The first sentence of the description, with all HTML removed.},
lastUpdated => 1274816273,
},
};

View file

@ -34,7 +34,12 @@ our $I18N = { ##hashref of hashes
message => q|The English name of the language.|,
lastUpdated => 1131394072,
context => q|A label of the language to use in the template|
},
},
'language_isCurrent' => {
message => q|A boolean which is true if this language is the current language.|,
lastUpdated => 1131394072,
context => q|A label of the language to use in the template|
},
'language_url' => {
message => q|The url that sets the WebGUI language to the selected language.|,
lastUpdated => 1131394072,

265
sbin/findBrokenAssets.pl Normal file
View file

@ -0,0 +1,265 @@
#!/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
#-------------------------------------------------------------------
use strict;
use File::Basename ();
use File::Spec;
my $webguiRoot;
BEGIN {
$webguiRoot = File::Spec->rel2abs( File::Spec->catdir( File::Basename::dirname(__FILE__), File::Spec->updir ) );
unshift @INC, File::Spec->catdir( $webguiRoot, 'lib' );
}
$|++; # disable output buffering
our ( $configFile, $help, $man, $fix, $delete );
use Pod::Usage;
use Getopt::Long;
use WebGUI::Session;
# Get parameters here, including $help
GetOptions(
'configFile=s' => \$configFile,
'help' => \$help,
'man' => \$man,
'fix' => \$fix,
'delete' => \$delete,
);
pod2usage( verbose => 1 ) if $help;
pod2usage( verbose => 2 ) if $man;
pod2usage( msg => "Must specify a config file!" ) unless $configFile;
my $session = start( $webguiRoot, $configFile );
sub progress {
my ( $total, $current ) = @_;
local $| = 1;
my $done = int( ( ( $current / $total ) * 100 ) / 2 );
$done &&= $done - 1; # Fit the >
my $space = 49 - $done;
print "\r[", '=' x $done, '>', ' ' x $space, ']';
printf ' (%d/%d)', $current, $total;
}
my $totalAsset = $session->db->quickScalar('SELECT COUNT(*) FROM asset');
my $totalAssetData = $session->db->quickScalar('SELECT COUNT( DISTINCT( assetId ) ) FROM assetData' );
my $total = $totalAsset >= $totalAssetData ? $totalAsset : $totalAssetData;
# Order by to put corrupt parents before corrupt children
# Join assetData to get all asset and assetData
my $sql = "SELECT * FROM asset LEFT JOIN assetData USING ( assetId ) GROUP BY assetId ORDER BY lineage ASC";
my $sth = $session->db->read($sql);
my $count = 1;
my %classTables; # Cache definition lookups
while ( my %row = $sth->hash ) {
eval { WebGUI::Asset->newPending( $session, $row{assetId} ) };
if ( $@ ) {
# Replace the progress bar with a message
printf "\r%-68s", "-- Corrupt: $row{assetId}";
# Should we do something?
if ($fix) {
my $classTables = $classTables{ $row{className} } ||= do {
eval "require $row{className}";
[ map { $_->{tableName} } reverse @{ $row{className}->definition($session) } ];
};
$row{revisionDate} ||= time;
for my $table ( @{$classTables} ) {
my $sqlFind = "SELECT * FROM $table WHERE assetId=? ORDER BY revisionDate DESC";
my @params = @row{qw( assetId )};
my $insertRow = $session->db->quickHashRef( $sqlFind, \@params ) || {};
if ( $row{revisionDate} != $insertRow->{revisionDate} ) {
$insertRow->{ assetId } = $row{assetId};
$insertRow->{ revisionDate } = $row{revisionDate};
my $cols = join ",", keys %$insertRow;
my @values = values %$insertRow;
my $places = join ",", ('?') x @values;
my $sqlFix = "INSERT INTO $table ($cols) VALUES ($places)";
$session->db->write( $sqlFix, \@values );
}
}
print "Fixed.\n";
# Make sure we have a valid parent
unless ( WebGUI::Asset->newByDynamicClass( $session, $row{parentId} ) ) {
my $asset = WebGUI::Asset->newByDynamicClass( $session, $row{assetId} );
$asset->setParent( WebGUI::Asset->getImportNode( $session ) );
print "\tNOTE: Invalid parent. Asset moved to Import Node\n";
}
} ## end if ($fix)
elsif ($delete) {
my $classTables = $classTables{ $row{className} } ||= do {
eval "require $row{className}";
[ map { $_->{tableName} } reverse @{ $row{className}->definition($session) } ];
};
my @params = @row{qw( assetId revisionDate )};
for my $table ( @{$classTables} ) {
my $sqlDelete = "DELETE FROM $table WHERE assetId=? AND revisionDate=?";
$session->db->write( $sqlDelete, \@params );
}
$session->db->write( "DELETE FROM asset WHERE assetId=?", [$row{assetId}] );
print "Deleted.\n";
} ## end elsif ($delete)
else { # report
print "\n";
if ( $row{revisionDate} ) {
printf "%10s: %s\n", "revised", scalar( localtime $row{revisionDate} );
}
# Parent
if ( my $parent = WebGUI::Asset->newByDynamicClass( $session, $row{parentId} ) ) {
printf "%10s: %s (%s)\n", "parent", $parent->getTitle, $parent->getId;
}
elsif ( $session->db->quickScalar( "SELECT * FROM asset WHERE assetId=?", [$row{parentId}] ) ) {
print "Parent corrupt ($row{parentId}).\n";
}
else {
print "Parent missing ($row{parentId}).\n";
}
# More properties
if ( $row{revisionDate} ) {
my %assetData = $session->db->quickHash( "SELECT * FROM assetData WHERE assetId=? AND revisionDate=?",
[ @row{ "assetId", "revisionDate" } ] );
for my $key (qw( title url )) {
printf "%10s: %s\n", $key, $assetData{$key};
}
}
else {
print "No current asset data.\n";
}
# Previous revisions
my %lastRev
= $session->db->quickHash(
"SELECT * FROM assetData WHERE assetId=? AND revisionDate != ? ORDER BY revisionDate DESC",
[ $row{assetId}, $row{revisionDate} ]
);
if ( $lastRev{assetId} ) {
print "Previous revision:\n";
for my $key (qw( title url )) {
printf "%10s: %s\n", $key, $lastRev{$key};
}
}
else {
print "No previous revisions.\n";
}
# Asset History
my $history = $session->db->buildArrayRefOfHashRefs(
"SELECT * FROM assetHistory LEFT JOIN users USING (userId) WHERE assetId=? ORDER BY dateStamp DESC",
[ $row{assetId} ],
);
if ( $history->[0] ) {
my $username = $history->[0]{username} || "<Unknown User>";
printf "Last action '%s'\n\tby %s\n\ton %s\n",
$history->[0]{actionTaken},
$username,
scalar( localtime $history->[0]{dateStamp} ),
;
}
} ## end else [ if ($fix) ]
} ## end if ( !$asset )
progress( $total, $count++ );
} ## end while ( my %row = $sth->hash)
finish($session);
print "\n";
#----------------------------------------------------------------------------
# Your sub here
#----------------------------------------------------------------------------
sub start {
my $webguiRoot = shift;
my $configFile = shift;
my $session = WebGUI::Session->open( $webguiRoot, $configFile );
$session->user( { userId => 3 } );
return $session;
}
#----------------------------------------------------------------------------
sub finish {
my $session = shift;
$session->var->end;
$session->close;
}
__END__
=head1 NAME
findBrokenAssets.pl -- Find and fix broken assets
=head1 SYNOPSIS
findBrokenAssets.pl --configFile config.conf [--fix|--delete]
utility --help
=head1 DESCRIPTION
This utility will find any broken assets that cannot be instantiated and are
causing undesired operation of your website.
It can also automatically delete them or fix them so you can restore missing data.
=head1 ARGUMENTS
=head1 OPTIONS
=over
=item B<--configFile config.conf>
The WebGUI config file to use. Only the file name needs to be specified,
since it will be looked up inside WebGUI's configuration directory.
This parameter is required.
=item B<--delete>
Delete any corrupted assets.
=item B<--fix>
Try to fix any corrupted assets.
=item B<--help>
Shows a short summary and usage
=item B<--man>
Shows this document
=back
=head1 AUTHOR
Copyright 2001-2009 Plain Black Corporation.
=cut
#vim:ft=perl

View file

@ -23,7 +23,7 @@ use WebGUI::VersionTag;
use Test::More; # increment this value for each test you create
use Test::MockObject;
plan tests => 14;
plan tests => 16;
my $session = WebGUI::Test->session;
$session->user({userId => 3});
@ -127,6 +127,30 @@ cmp_ok( $updatedSnippet->get('revisionDate'), '>', $snippetRev->get('revisionDat
my $lastTag = WebGUI::VersionTag->getWorking($session);
WebGUI::Test->addToCleanup($lastTag);
{
# Test clearPackageFlag
my $flagTag = WebGUI::VersionTag->create($session);
WebGUI::Test->addToCleanup($flagTag);
$flagTag->setWorking;
my $tempspace = WebGUI::Asset->getTempspace($session);
my $snippet = $tempspace->addChild(
{ url => 'pflagt',
title => 'package flag test',
menuTitle => 'package flag test',
className => 'WebGUI::Asset::Snippet',
snippet => 'This is a test asset',
isPackage => 1,
}
);
my $storage = $snippet->exportPackage;
WebGUI::Test->addToCleanup($storage);
my $without = $tempspace->importPackage($storage);
ok $without->get('isPackage'), 'has flag with no option';
my $with = $tempspace->importPackage($storage, { clearPackageFlag => 1 });
ok !$with->get('isPackage'), 'no flag with option';
}
TODO: {
local $TODO = "Tests to make later";
ok(0, 'Check package deployment with 2-level package and look for new style templates propagating down the tree');

View file

@ -24,15 +24,20 @@ use WebGUI::Asset::File::GalleryFile::Photo;
#----------------------------------------------------------------------------
# Init
my $session = WebGUI::Test->session;
my $user = WebGUI::User->new( $session, 3 );
my $node = WebGUI::Asset->getImportNode($session);
my $versionTag = WebGUI::VersionTag->getWorking($session);
$versionTag->set({name=>"Photo Test"});
addToCleanup($versionTag);
my $gallery
= $node->addChild({
className => "WebGUI::Asset::Wobject::Gallery",
groupIdAddComment => 7, # Everyone
groupIdAddFile => 2, # Registered Users
});
my $album
= $gallery->addChild({
className => "WebGUI::Asset::Wobject::GalleryAlbum",
@ -42,36 +47,40 @@ my $album
{
skipAutoCommitWorkflows => 1,
});
my $previousPhoto
= $album->addChild({
className => "WebGUI::Asset::File::GalleryFile::Photo",
ownerUserId => 3,
ownerUserId => $user->getId,
},
undef,
undef,
{
skipAutoCommitWorkflows => 1,
});
my $photo
= $album->addChild({
className => "WebGUI::Asset::File::GalleryFile::Photo",
ownerUserId => 3,
ownerUserId => $user->getId,
},
undef,
undef,
{
skipAutoCommitWorkflows => 1,
});
my $nextPhoto
= $album->addChild({
className => "WebGUI::Asset::File::GalleryFile::Photo",
ownerUserId => 3,
ownerUserId => $user->getId,
},
undef,
undef,
{
skipAutoCommitWorkflows => 1,
});
$versionTag->commit;
foreach my $asset ($gallery, $album, $photo) {
$asset = $asset->cloneFromDb;
@ -91,7 +100,10 @@ my $testTemplateVars = {
synopsis => '', # Synopsis is not undef, is changed to empty string
canComment => bool( 1 ),
canEdit => bool( 0 ),
ownerUsername => WebGUI::User->new( $session, 3 )->username,
ownerUsername => $user->get("username"),
ownerAlias => $user->get("alias") || $user->get("username"),
ownerId => $user->getId,
ownerProfileUrl => $user->getProfileUrl,
synopsis_textonly => WebGUI::HTML::filter( $photo->get('synopsis'), "all" ),
url => $photo->getUrl,
url_addArchive => $album->getUrl('func=addArchive'),

View file

@ -275,8 +275,10 @@ cmp_deeply(
title => 'Story 1',
description => 'WebGUI was originally called Web Done Right.',
'link' => re('story-1$'),
guid => re('story-1$'),
author => 'JT Smith',
date => $story->lastModified,
pubDate => $session->datetime->epochToMail($story->creationDate),
},
'getRssData: returns correct data'
);

View file

@ -75,7 +75,7 @@ my $template3 = $importNode->addChild({
className => "WebGUI::Asset::Template",
title => 'headBlock test',
template => "this is a template",
});
}, undef, time()-5);
my @atts = (
{type => 'headScript', sequence => 1, url => 'bar'},
@ -97,7 +97,7 @@ ok(exists $session->style->{_javascript}->{$_}, "$_ in style") for qw(foo bar bo
# revision-ness of attachments
# sleep so the revisiondate isn't duplicated
sleep 1;
#sleep 1;
my $template3dup = $template3->duplicate;
my @atts3dup = map { delete @{ $_ }{qw/attachId templateId revisionDate/}; $_; } @{ $template3dup->getAttachments };

View file

@ -0,0 +1,318 @@
#-------------------------------------------------------------------
# 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";
# Test of the Gallery basic and advanced search. In non-live tests, the Gallery
# search is accessed via the "search" method. Form parameters are passed in via
# the pseudo request object of the test session.
use Test::More;
use Test::Deep;
use WebGUI::Test; # Must use this before any other WebGUI modules
use WebGUI::Asset::Wobject::Gallery;
use WebGUI::Asset::Wobject::GalleryAlbum;
use WebGUI::Asset::File::GalleryFile::Photo;
use WebGUI::DateTime;
use WebGUI::Session;
#----------------------------------------------------------------------------
# Init
my $session = WebGUI::Test->session;
my $node = WebGUI::Asset->getImportNode($session);
my $versionTag = WebGUI::VersionTag->getWorking($session);
$versionTag->set( { name=>"Gallery Search Test" } );
addToCleanup( $versionTag );
# Create gallery and a single album
my $gallery
= $node->addChild({
className => "WebGUI::Asset::Wobject::Gallery",
},
undef,
undef,
{
skipAutoCommitWorkflows => 1,
});
my $album
= $gallery->addChild({
className => "WebGUI::Asset::Wobject::GalleryAlbum",
title => "album",
synopsis => "synopsis2",
keywords => "group2",
},
undef,
undef,
{
skipAutoCommitWorkflows => 1,
});
my $albumId = $album->getId;
# Populate album with different photos
my $photo1
= $album->addChild({
className => "WebGUI::Asset::File::GalleryFile::Photo",
title => "photo1",
synopsis => "synopsis1",
keywords => "group1",
location => "Heidelberg",
},
undef,
undef,
{
skipAutoCommitWorkflows => 1,
});
my $id1 = $photo1->getId;
my $photo2
= $album->addChild({
className => "WebGUI::Asset::File::GalleryFile::Photo",
title => "photo2",
synopsis => "synopsis2",
keywords => "group1",
location => "Mannheim",
},
undef,
undef,
{
skipAutoCommitWorkflows => 1,
});
my $id2 = $photo2->getId;
my $photo3
= $album->addChild({
className => "WebGUI::Asset::File::GalleryFile::Photo",
title => "photo3",
synopsis => "synopsis1",
keywords => "group2",
location => "Mannheim",
},
undef,
undef,
{
skipAutoCommitWorkflows => 1,
});
my $id3 = $photo3->getId;
# Commit all changes
$versionTag->commit;
# Make gallery default asset
$session->asset( $gallery );
# Define some general variables
my $result;
#----------------------------------------------------------------------------
# Tests
plan tests => 32;
#----------------------------------------------------------------------------
# Basic search
note( "Basic gallery search" );
# Search by title
my $hits = search( { basicSearch => "album" } );
# Basic search will behave differently from advanced search. The album and all
# photos of the album will be returned, since the name of the album is added to
# index keywords of photos.
cmp_bag( $hits, [ $albumId, $id1, $id2, $id3 ], "Search for album entitled 'album' (basic search)" );
my $hits = search( { basicSearch => "photo1" } );
cmp_bag( $hits, [ $id1 ], "Search for photo entitled 'photo1' (basic search)" );
my $hits = search( { title => "photo4" } );
cmp_bag( $hits, [ ], "Search for non-existing photo entitled 'photo4' (basic search)" );
# Search by keywords
my $hits = search( { basicSearch => "group1" } );
cmp_bag( $hits, [ $id1, $id2 ], "Search for albums/photos with keywords 'group1' (basic search)" );
my $hits = search( { basicSearch => "group2" } );
cmp_bag( $hits, [ $albumId, $id3 ], "Search for albums/photos with keywords 'group2' (basic search)" );
# Search by description
my $hits = search( { basicSearch => "synopsis1" } );
cmp_bag( $hits, [ $id1, $id3 ], "Search for albums/photos with synopsis 'synopsis1' (basic search)" );
my $hits = search( { basicSearch => "synopsis2" } );
cmp_bag( $hits, [ $albumId, $id2 ], "Search for albums/photos with synopsis 'synopsis2' (basic search)" );
# Warning: Tried to use 'here' and 'there' as locations for the following test.
# For unknown reasons the test failed. It seems that these and possibly other
# keywords are either filtered out by MySQL and/or are reserved words. Needs to
# be checked!!!
my $hits = search( { basicSearch => "Mannheim" } );
cmp_bag( $hits, [ $id2, $id3 ], "Search for photos taken at location 'Mannheim' (basic search)" );
my $hits = search( { basicSearch => "Heidelberg" } );
cmp_bag( $hits, [ $id1 ], "Search for photos taken at location 'Heidelberg' (basic search)" );
# Search by multiple criteria
my $hits = search({ basicSearch => "group1 synopsis1" });
cmp_bag( $hits, [ $id1 ], "Search for photo with keywords 'group1' and synopsis 'synopsis1' (basic search)" );
my $hits = search({ basicSearch => "group2 Mannheim" });
cmp_bag( $hits, [ $id3 ], "Search for photo with keywords 'group2' and location 'Mannheim' (basic search)" );
my $hits = search({ basicSearch => "synopsis1 Mannheim" });
cmp_bag( $hits, [ $id3 ], "Search for photo with synopsis 'synopsis1' and location 'Mannheim' (basic search)" );
#----------------------------------------------------------------------------
# Advanced search
note( "Advanced gallery search" );
my $hits = search( { } );
cmp_bag( $hits, [ ], "Empty search (advanced search)" );
# Search by class
my $hits = search( { className => "WebGUI::Asset::File::GalleryFile::Photo" } );
cmp_bag( $hits, [ $id1, $id2, $id3 ], "Search for all photos (advanced search)" );
my $hits = search( { className => "WebGUI::Asset::Wobject::GalleryAlbum" } );
cmp_bag( $hits, [ $albumId ], "Search for all albums (advanced search)" );
# Search by date
my $oneYearAgo = WebGUI::DateTime->new( $session, time )->add( years => -1 )->epoch;
my $hits
= search({
creationDate_after => $oneYearAgo,
creationDate_before => time(),
});
cmp_bag( $hits, [ $albumId, $id1, $id2, $id3 ], "Search by date, all included (advanced search)" );
my $hits
= search({
creationDate_after => time() + 1,
creationDate_before => time() + 1,
});
cmp_bag( $hits, [ ], "Search by date, all excluded (advanced search)" );
# Search by title
my $hits = search( { title => "album" } );
cmp_bag( $hits, [ $albumId ], "Search for album entitled 'album' (advanced search)" );
my $hits = search( { title => "photo1" } );
cmp_bag( $hits, [ $id1 ], "Search for photo entitled 'photo1' (advanced search)" );
my $hits = search( { title => "photo4" } );
cmp_bag( $hits, [ ], "Search for non-existing photo entitled 'photo4' (advanced search)" );
# Search by keywords
my $hits = search( { keywords => "group1" } );
cmp_bag( $hits, [ $id1, $id2 ], "Search for albums/photos with keywords 'group1' (advanced search)" );
my $hits = search( { keywords => "group2" } );
cmp_bag( $hits, [ $albumId, $id3 ], "Search for albums/photos with keywords 'group2' (advanced search)" );
my $hits = search( { keywords => "group3" } );
cmp_bag( $hits, [ ], "Search for non-existing albums/photos with keywords 'group3' (advanced search)" );
# Search by description
my $hits = search( { description => "synopsis1" } );
cmp_bag( $hits, [ $id1, $id3 ], "Search for albums/photos with synopsis 'synopsis1' (advanced search)" );
my $hits = search( { description => "synopsis2" } );
cmp_bag( $hits, [ $albumId, $id2 ], "Search for albums/photos with synopsis 'synopsis2' (advanced search)" );
my $hits = search( { description => "synopsis3" } );
cmp_bag( $hits, [ ], "Search for non-existing albums/photos with synopsis 'synopsis3' (advanced search)" );
# Search by location
# Warning: Tried to use 'here' and 'there' as locations for the following test.
# For unknown reasons the test failed. It seems that these and possibly other
# keywords are either filtered out by MySQL and/or are reserved words. Needs to
# be checked!!!
my $hits = search( { location => "Mannheim" } );
cmp_bag( $hits, [ $id2, $id3 ], "Search for photos taken at location 'Mannheim' (advanced search)" );
my $hits = search( { location => "Heidelberg" } );
cmp_bag( $hits, [ $id1 ], "Search for photos taken at location 'Heidelberg' (advanced search)" );
my $hits = search( { location => "Frankfurt" } );
cmp_bag( $hits, [ ], "Search for non-existing photos taken at location 'Frankfurt' (advanced search)" );
# Search by multiple criteria
my $hits
= search({
keywords => "group1",
description => "synopsis1",
});
cmp_bag( $hits, [ $id1 ], "Search for photo with keywords 'group1' and synopsis 'synopsis1' (advanced search)" );
my $hits
= search({
keywords => "group2",
location => "Mannheim",
});
cmp_bag( $hits, [ $id3 ], "Search for photo with keywords 'group2' and location 'Mannheim' (advanced search)" );
my $hits
= search({
description => "synopsis1",
location => "Mannheim",
});
cmp_bag( $hits, [ $id3 ], "Search for photo with synopsis 'synopsis1' and location 'Mannheim' (advanced search)" );
#----------------------------------------------------------------------------
# search( formParams )
# Execute a search for photos and albums in the test gallery.
#
# Accepts a hash ref as single parameter. All key/value pairs in the hash are
# added as form parameters to the pseudo request object before the search is
# executed. See the Gallery search method for valid form fields.
#
# Returns a reference pointing an array containg the asset Ids of all hits.
sub search {
my $formParams = shift;
my $hits = [];
# Setup the mock request object
$session->request->method( 'GET' );
$session->request->setup_param( $formParams );
# Call gallery search function
my ( $paginator, $keywords ) = $gallery->search;
# Return ref to empty array if search could not be executed
return $hits unless $paginator;
# Extract asset Ids from search results and compile array.
for ( my $i = 1; $i <= $paginator->getNumberOfPages; $i++ ) {
for my $result ( @{ $paginator->getPageData( $i ) } ) {
push @{ $hits }, $result->{ assetId };
}
}
return $hits;
}

View file

@ -544,6 +544,8 @@ cmp_deeply(
description => ignore(),
'link' => ignore(),
date => ignore(),
guid => ignore(),
pubDate => ignore(),
author => ignore(),
},
{
@ -552,6 +554,8 @@ cmp_deeply(
'link' => ignore(),
date => ignore(),
author => ignore(),
guid => ignore(),
pubDate => ignore(),
},
{
title => 'Story 3',
@ -559,6 +563,8 @@ cmp_deeply(
'link' => ignore(),
date => ignore(),
author => ignore(),
guid => ignore(),
pubDate => ignore(),
},
],
'rssFeedItems'

View file

@ -297,6 +297,8 @@ cmp_deeply(
'link' => ignore(),
date => ignore(),
author => ignore(),
guid => ignore(),
pubDate => ignore(),
},
{
title => 'red',
@ -304,6 +306,8 @@ cmp_deeply(
'link' => ignore(),
date => ignore(),
author => ignore(),
guid => ignore(),
pubDate => ignore(),
},
{
title => 'brooks',
@ -311,6 +315,8 @@ cmp_deeply(
'link' => ignore(),
date => ignore(),
author => ignore(),
guid => ignore(),
pubDate => ignore(),
},
],
'rssFeedItems'

View file

@ -276,37 +276,37 @@ cmp_deeply(
[
{
'extras' => undef,
'url' => '/home?func=edit',
'url' => re('func=edit$'),
'label' => 'Edit'
},
{
'extras' => undef,
'url' => '/home?func=editSurvey',
'url' => re('func=editSurvey$'),
'label' => 'Edit Survey'
},
{
'extras' => undef,
'url' => '/home?func=takeSurvey',
'url' => re('func=takeSurvey$'),
'label' => 'Take Survey'
},
{
'extras' => undef,
'url' => '/home?func=graph',
'url' => re('func=graph$'),
'label' => 'Visualize'
},
{
'extras' => undef,
'url' => '/home?func=editTestSuite',
'url' => re('func=editTestSuite$'),
'label' => 'Test Suite'
},
{
'extras' => undef,
'url' => '/home?func=runTests',
'url' => re('func=runTests$'),
'label' => 'Run All Tests'
},
{
'extras' => undef,
'url' => '/home?func=runTests;format=tap',
'url' => re('func=runTests;format=tap$'),
'label' => 'Run All Tests (TAP)'
}
],

View file

@ -20,7 +20,7 @@ use Data::Dumper;
use WebGUI::Test;
use WebGUI::Session;
use Test::More tests => 27; # increment this value for each test you create
use Test::More tests => 29; # increment this value for each test you create
use Test::Deep;
use WebGUI::Asset::Wobject::SyndicatedContent;
use XML::FeedPP;
@ -175,7 +175,14 @@ is $vars->{item_loop}->[0]->{descriptionFirstParagraph},
'... first paragraph, when HTML is used';
is $vars->{item_loop}->[0]->{descriptionFirst2paragraphs},
"<p>In the attached feed, there is a hidden return line character from the Rich Text editor in the first sentence of the description. When using a Syndicated Content for the feed, the variable descriptionFirstParagraph variable cuts off at this return line character, creating invalid markup.</p><p>No more text is shown of the first paragraph beyond the bold characters of the first line.</p>",
'... first paragraph, when HTML is used';
'... first two paragraphs, when HTML is used';
is $vars->{item_loop}->[0]->{descriptionFirst10words},
"In the attached feed, there is a hidden return line ",
'... first 10 words, with HTML stripped';
is $vars->{item_loop}->[0]->{descriptionFirstSentence},
"In the attached feed, there is a hidden return line character from the
Rich Text editor in the first sentence of the description.",
'... first sentence, with HTML stripped';
####################################################################
#

View file

@ -203,7 +203,7 @@ foreach my $idx (0..$#ldapTests) {
});
}
WebGUI::Test->usersToDelete(@shawshank);
WebGUI::Test->addToCleanup(@shawshank);
my $lGroup = WebGUI::Group->new($session, 'new');
@ -504,8 +504,7 @@ cmp_bag(
$session->db->write('delete from myUserTable where userId=?',[$mob[0]->getId]);
my $inDb = $session->db->quickScalar("select count(*) from myUserTable where userId=?",[$mob[0]->getId]);
ok ( !$inDb, 'mob[0] no longer in myUserTable');
WebGUI::Cache->new($session, ["groupMembers",$gY->getId])->delete; #Delete cache so we get a good test
$session->stow->delete("isInGroup"); #Delete stow so we get a good test
$session->cache->remove("isInGroup"); #Delete stow so we get a good test
is_deeply(
[ (map { $gY->hasDatabaseUser($_->getId) } @mob) ],
@ -637,8 +636,7 @@ foreach my $scratchTest (@scratchTests) {
is($scratchTest->{user}->isInGroup($gS->getId), $scratchTest->{expect}, $scratchTest->{comment});
}
WebGUI::Cache->new($session, $gS->getId)->delete(); ##Delete cached key for testing
$session->stow->delete("isInGroup");
$session->cache->remove("isInGroup");
#hasScratchUser test
foreach my $scratchTest (@scratchTests) {

View file

@ -49,11 +49,12 @@ $templateMock->mock('process', sub { $templateVars = $_[1]; } );
$templateVars,
{
lang_loop => [
{ 'language_url' => '?op=setLanguage;language=English',
'language_lang' => 'English',
{ 'language_url' => '?op=setLanguage;language=English',
'language_lang' => 'English',
'language_langAbbr' => 'en',
'language_langAbbrLoc' => 'US',
'language_langEng' => 'English',
'language_langEng' => 'English',
'language_isCurrent' => 1,
},
],
delete_url => '?op=setLanguage;language=delete;',
@ -82,6 +83,7 @@ $templateMock->mock('process', sub { $templateVars = $_[1]; } );
'language_langAbbr' => 'en',
'language_langAbbrLoc' => 'US',
'language_langEng' => 'English',
'language_isCurrent' => 1,
},
],
delete_url => '?op=setLanguage;language=delete;',

View file

@ -56,7 +56,7 @@ cmp_deeply (
{
assetId => $article->getId,
title => $article->get('title'),
synopsis => '' . $article->get('synopsis'),
synopsis => $article->get('synopsis') || '',
url => $article->get('url'),
revisionDate => $article->get('revisionDate'),
creationDate => $article->get('creationDate'),

View file

@ -1,155 +0,0 @@
function emailCheck (emailStr) {
/* The following variable tells the rest of the function whether or not
to verify that the address ends in a two-letter country or well-known
TLD. 1 means check it, 0 means don't. */
var checkTLD=1;
/* The following is the list of known TLDs that an e-mail address must end with. */
var knownDomsPat=/^(com|net|org|edu|int|mil|gov|arpa|biz|aero|name|coop|info|pro|museum)$/i;
/* The following pattern is used to check if the entered e-mail address
fits the user@domain format. It also is used to separate the username
from the domain. */
var emailPat=/^(.+)@(.+)$/;
/* The following string represents the pattern for matching all special
characters. We don't want to allow special characters in the address.
These characters include ( ) < > @ , ; : \ " . [ ] */
var specialChars="\\(\\)><@,;:\\\\\\\"\\.\\[\\]";
/* The following string represents the range of characters allowed in a
username or domainname. It really states which chars aren't allowed.*/
var validChars="\[^\\s" + specialChars + "\]";
/* The following pattern applies if the "user" is a quoted string (in
which case, there are no rules about which characters are allowed
and which aren't; anything goes). E.g. "jiminy cricket"@disney.com
is a legal e-mail address. */
var quotedUser="(\"[^\"]*\")";
/* The following pattern applies for domains that are IP addresses,
rather than symbolic names. E.g. joe@[123.124.233.4] is a legal
e-mail address. NOTE: The square brackets are required. */
var ipDomainPat=/^\[(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})\]$/;
/* The following string represents an atom (basically a series of non-special characters.) */
var atom=validChars + '+';
/* The following string represents one word in the typical username.
For example, in john.doe@somewhere.com, john and doe are words.
Basically, a word is either an atom or quoted string. */
var word="(" + atom + "|" + quotedUser + ")";
// The following pattern describes the structure of the user
var userPat=new RegExp("^" + word + "(\\." + word + ")*$");
/* The following pattern describes the structure of a normal symbolic
domain, as opposed to ipDomainPat, shown above. */
var domainPat=new RegExp("^" + atom + "(\\." + atom +")*$");
/* Finally, let's start trying to figure out if the supplied address is valid. */
/* Begin with the coarse pattern to simply break up user@domain into
different pieces that are easy to analyze. */
var matchArray=emailStr.match(emailPat);
if (matchArray==null) {
/* Too many/few @'s or something; basically, this address doesn't
even fit the general mould of a valid e-mail address. */
alert("Email address seems incorrect (check @ and .'s)");
return false;
}
var user=matchArray[1];
var domain=matchArray[2];
// Start by checking that only basic ASCII characters are in the strings (0-127).
for (i=0; i<user.length; i++) {
if (user.charCodeAt(i)>127) {
alert("Ths username contains invalid characters.");
return false;
}
}
for (i=0; i<domain.length; i++) {
if (domain.charCodeAt(i)>127) {
alert("Ths domain name contains invalid characters.");
return false;
}
}
// See if "user" is valid
if (user.match(userPat)==null) {
// user is not valid
alert("The username doesn't seem to be valid.");
return false;
}
/* if the e-mail address is at an IP address (as opposed to a symbolic
host name) make sure the IP address is valid. */
var IPArray=domain.match(ipDomainPat);
if (IPArray!=null) {
// this is an IP address
for (var i=1;i<=4;i++) {
if (IPArray[i]>255) {
alert("Destination IP address is invalid!");
return false;
}
}
return true;
}
// Domain is symbolic name. Check if it's valid.
var atomPat=new RegExp("^" + atom + "$");
var domArr=domain.split(".");
var len=domArr.length;
for (i=0;i<len;i++) {
if (domArr[i].search(atomPat)==-1) {
alert("The domain name does not seem to be valid.");
return false;
}
}
/* domain name seems valid, but now make sure that it ends in a
known top-level domain (like com, edu, gov) or a two-letter word,
representing country (uk, nl), and that there's a hostname preceding
the domain or country. */
if (checkTLD && domArr[domArr.length-1].length!=2 &&
domArr[domArr.length-1].search(knownDomsPat)==-1) {
alert("The address must end in a well-known domain or two letter " + "country.");
return false;
}
// Make sure there's a host name preceding the domain.
if (len<2) {
alert("This address is missing a hostname!");
return false;
}
// If we've gotten this far, everything's valid!
return true;
}

View file

@ -31,7 +31,7 @@
function fillIn(dom, a) {
_.each(a, function (v, k) {
if (dom[k]) {
dom[k].value = v;
dom[k].value = v || '';
}
});
}
@ -60,7 +60,8 @@
calculateSummary: function () {
var e = this.elements,
prices = this.prices,
shipping = prices.shipping[e.shipper.value],
shipper = e.shipper,
shipping = shipper && prices.shipping[shipper.value],
shipPrice = (shipping ?
(shipping.hasPrice ?
parseFloat(shipping.price) :
@ -138,7 +139,7 @@
return function () {
var address = {},
label = e.label.value,
copy = o && o.label.value === label,
copy = o.label && o.label.value === label,
cache = c[label],
dirty;
@ -147,7 +148,7 @@
}
_.each(e, function (v, k) {
v = v.value;
v = v.value || '';
address[k] = v;
if (cache[k] !== v) {
dirty = true;
@ -306,7 +307,7 @@
if (!opt) {
opt = document.createElement('option');
opt.text = label;
opt.innerHTML = label;
dropdown.appendChild(opt);
}
@ -314,14 +315,16 @@
}
updateOne(d.billing);
updateOne(d.shipping);
if (d.shipping) {
updateOne(d.shipping);
if (name === 'billing' && self.sameShipping()) {
self.copyBilling();
}
else {
self.computePerItemShippingOptions();
}
}
d[name].value = id;
if (name === 'billing' && self.sameShipping()) {
self.copyBilling();
}
else {
self.computePerItemShippingOptions();
}
self.updateSummary();
});
},
@ -335,10 +338,9 @@
e = this.elements,
tax = e.tax,
shipper = e.shipper,
selected = shipper.value,
d = e.dropdowns,
shipping = d.shipping.value,
billing = d.billing.value,
selected, shipping,
params = {
shop: 'cart',
method: 'ajaxPrices'
@ -348,10 +350,14 @@
params.billingId = billing;
}
if (this.sameShipping()) {
params.shippingId = params.billingId;
} else if (addressIdCounts(shipping)) {
params.shippingId = shipping;
if (shipper) {
selected = shipper.value; // save so we can restore later
shipping = d.shipping.value;
if (this.sameShipping()) {
params.shippingId = params.billingId;
} else if (addressIdCounts(shipping)) {
params.shippingId = shipping;
}
}
this.request('GET', params, function (o) {
@ -360,30 +366,32 @@
if (response.error) {
return;
}
if (shipper) {
// We need to repopulate the shipper options dropdown
// (but only if we have a shipper at all)
_(shipper.options).chain().select(function (o) {
return o.value;
}).each(function (o) {
o.parentNode.removeChild(o);
});
_.each(response.shipping, function (o, id) {
var opt = document.createElement('option'),
label = o.label;
if (o.hasPrice) {
label += ' (' + formatCurrency(o.price) + ')';
}
opt.innerHTML = label;
opt.value = id;
shipper.appendChild(opt);
});
shipper.value = selected;
}
self.prices = response;
tax.innerHTML = formatCurrency(response.tax);
_(shipper.options).chain().select(function (o) {
return o.value;
}).each(function (o) {
o.parentNode.removeChild(o);
});
_.each(response.shipping, function (o, id) {
var opt = document.createElement('option'),
label = o.label;
if (o.hasPrice) {
label += ' (' + formatCurrency(o.price) + ')';
}
opt.innerHTML = label;
opt.value = id;
shipper.appendChild(opt);
});
shipper.value = selected;
self.calculateSummary();
});
},
@ -405,7 +413,8 @@
},
sameShipping: function () {
return this.elements.same.checked;
var same = this.elements.same;
return same && same.checked;
},
setCartItemShippingId: function (select) {