From ab6f4defe35e80088c3329718fbcf0909f60b6da Mon Sep 17 00:00:00 2001 From: Doug Bell Date: Thu, 20 Mar 2008 18:51:44 +0000 Subject: [PATCH] - moved Gallery utility methods to WebGUI::Utility::Gallery - Added tests for GalleryAlbum RSS - More tests for comments - Test International Macro sprintf as third+ arguments - Add Gallery search limiting by user ID - Remaining i18n for Gallery templates - Fix: Search form now visible in Photo assets Moved a lot of stuff from Photo to GalleryFile --- docs/changelog/7.x.x.txt | 7 + docs/upgrades/_upgrade.skeleton | 11 +- .../root_import_gallery-templates.wgpkg | Bin 0 -> 31730 bytes docs/upgrades/upgrade_7.5.7-7.5.8.pl | 79 ++ lib/WebGUI/Asset/File/GalleryFile.pm | 831 +++++++++++++++++- lib/WebGUI/Asset/File/GalleryFile/Photo.pm | 774 +--------------- lib/WebGUI/Asset/Wobject/Gallery.pm | 13 +- lib/WebGUI/Asset/Wobject/GalleryAlbum.pm | 6 +- lib/WebGUI/Help/Asset_Photo.pm | 18 + .../Gallery/Utility.pm => Utility/Gallery.pm} | 8 +- lib/WebGUI/i18n/English/Asset_GalleryAlbum.pm | 17 + lib/WebGUI/i18n/English/Asset_Photo.pm | 54 ++ sbin/migrateCollabToGallery.pl | 6 +- t/Asset/File/GalleryFile/Photo/comment.t | 71 +- t/Asset/File/GalleryFile/Photo/download.t | 32 +- t/Asset/File/GalleryFile/Photo/editSave.t | 76 +- t/Asset/File/GalleryFile/Photo/makeShortcut.t | 30 +- t/Asset/File/GalleryFile/Photo/setFile.t | 2 +- t/Asset/File/GalleryFile/Photo/view.t | 49 +- t/Asset/Wobject/Gallery/00base.t | 8 +- t/Asset/Wobject/Gallery/permission.t | 83 ++ t/Asset/Wobject/GalleryAlbum/addArchive.t | 23 +- t/Asset/Wobject/GalleryAlbum/rss.t | 41 +- t/Macro/International.t | 45 +- .../Utility => Utility/Gallery}/addAlbum.t | 7 +- 25 files changed, 1386 insertions(+), 905 deletions(-) create mode 100644 docs/upgrades/packages-7.5.8/root_import_gallery-templates.wgpkg rename lib/WebGUI/{Asset/Wobject/Gallery/Utility.pm => Utility/Gallery.pm} (96%) create mode 100644 t/Asset/Wobject/Gallery/permission.t rename t/{Asset/Wobject/Gallery/Utility => Utility/Gallery}/addAlbum.t (98%) diff --git a/docs/changelog/7.x.x.txt b/docs/changelog/7.x.x.txt index b62f60f66..2267dffb2 100644 --- a/docs/changelog/7.x.x.txt +++ b/docs/changelog/7.x.x.txt @@ -1,4 +1,11 @@ 7.5.8 + - moved Gallery utility methods to WebGUI::Utility::Gallery + - Added tests for GalleryAlbum RSS + - More tests for comments + - Test International Macro sprintf as third+ arguments + - Add Gallery search limiting by user ID + - Remaining i18n for Gallery templates + - Fix: Search form now visible in Photo assets 7.5.7 - fixed: HttpProxy mixes original site's content encoding with WebGUI's diff --git a/docs/upgrades/_upgrade.skeleton b/docs/upgrades/_upgrade.skeleton index 0d47257e5..957377ce2 100644 --- a/docs/upgrades/_upgrade.skeleton +++ b/docs/upgrades/_upgrade.skeleton @@ -27,15 +27,16 @@ my $session = start(); # this line required finish($session); # this line required -##------------------------------------------------- +#---------------------------------------------------------------------------- #sub exampleFunction { -# my $session = shift; -# print "\tWe're doing some stuff here that you should know about.\n" unless ($quiet); -# # and here's our code +# 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; #} -# --------------- DO NOT EDIT BELOW THIS LINE -------------------------------- +# -------------- DO NOT EDIT BELOW THIS LINE -------------------------------- #---------------------------------------------------------------------------- # Add a package to the import node diff --git a/docs/upgrades/packages-7.5.8/root_import_gallery-templates.wgpkg b/docs/upgrades/packages-7.5.8/root_import_gallery-templates.wgpkg new file mode 100644 index 0000000000000000000000000000000000000000..183b4117a03565cecd7abf20692002a99c139966 GIT binary patch literal 31730 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 Z0soHW?+pB%f&ZTw_#c%Gx>Nuv0stJ}47&gT literal 0 HcmV?d00001 diff --git a/docs/upgrades/upgrade_7.5.7-7.5.8.pl b/docs/upgrades/upgrade_7.5.7-7.5.8.pl index 40a07ed2d..1921be275 100644 --- a/docs/upgrades/upgrade_7.5.7-7.5.8.pl +++ b/docs/upgrades/upgrade_7.5.7-7.5.8.pl @@ -23,6 +23,9 @@ my $quiet; # this line required my $session = start(); # this line required # upgrade functions go here +removeOldGalleryColumns( $session ); +moveColumnsToGalleryFile( $session ); +moveCommentsToGalleryFile( $session ); finish($session); # this line required @@ -34,6 +37,82 @@ finish($session); # this line required # # and here's our code #} +#---------------------------------------------------------------------------- +sub removeOldGalleryColumns { + my $session = shift; + $session->db->write( + "ALTER TABLE Gallery DROP COLUMN groupIdModerator" + ); +} + +#---------------------------------------------------------------------------- +# moveColumnsToGalleryFile +# Move columns from Photo that are better handled under GalleryFile +sub moveColumnsToGalleryFile { + my $session = shift; + print "\tMoving Photo columns to GalleryFile (its superclass)... " unless $quiet; + + # Add the galleryfile columns + $session->db->write(q{ + CREATE TABLE GalleryFile ( + assetId VARCHAR(22) BINARY NOT NULL, + revisionDate BIGINT NOT NULL, + userDefined1 LONGTEXT, + userDefined2 LONGTEXT, + userDefined3 LONGTEXT, + userDefined4 LONGTEXT, + userDefined5 LONGTEXT, + views BIGINT DEFAULT 0, + friendsOnly INT(1) DEFAULT 0, + rating INT(1) DEFAULT 0, + PRIMARY KEY ( assetId, revisionDate ) + ) + }); + + # Move Photo data to GalleryFile + my $sth = $session->db->read( "SELECT * FROM Photo" ); + while ( my %row = $sth->hash ) { + $session->db->write( + q{ INSERT INTO GalleryFile ( + assetId, revisionDate, userDefined1, userDefined2, userDefined3, userDefined4, + userDefined5, views, friendsOnly, rating ) + VALUES ( ?, ?, ?, ?, ?, ?, ?, ?, ?, ? ) + }, + [ @row{ qw( assetId revisionDate userDefined1 userDefined2 userDefined3 userDefined4 + userDefined5 views friendsOnly rating ) } ], + ); + } + + # Drop the photo columns + $session->db->write( q{ + ALTER TABLE Photo + DROP COLUMN userDefined1, + DROP COLUMN userDefined2, + DROP COLUMN userDefined3, + DROP COLUMN userDefined4, + DROP COLUMN userDefined5, + DROP COLUMN views, + DROP COLUMN friendsOnly, + DROP COLUMN rating + } ); + + print "DONE!\n" unless $quiet; +} + +#---------------------------------------------------------------------------- +# moveCommentsToGalleryFile +# Move comments to a better-described table +sub moveCommentsToGalleryFile { + my $session = shift; + print "\tMoving Photo_comment to GalleryFile_comment... " unless $quiet; + + $session->db->write( q{ + ALTER TABLE Photo_comment RENAME TO GalleryFile_comment + } ); + + print "DONE!\n" unless $quiet; +} + # --------------- DO NOT EDIT BELOW THIS LINE -------------------------------- diff --git a/lib/WebGUI/Asset/File/GalleryFile.pm b/lib/WebGUI/Asset/File/GalleryFile.pm index 52449c897..05ed57cce 100644 --- a/lib/WebGUI/Asset/File/GalleryFile.pm +++ b/lib/WebGUI/Asset/File/GalleryFile.pm @@ -17,6 +17,8 @@ package WebGUI::Asset::File::GalleryFile; use strict; use base 'WebGUI::Asset::File'; +use Carp qw( croak confess ); + =head1 NAME @@ -34,6 +36,311 @@ These methods are available from this class =cut +#------------------------------------------------------------------- + +=head2 definition ( session, definition ) + +Define the properties of all GalleryFile assets. + +=cut + +sub definition { + my $class = shift; + my $session = shift; + my $definition = shift; + my $i18n = __PACKAGE__->i18n($session); + + tie my %properties, 'Tie::IxHash', ( + views => { + defaultValue => 0, + }, + friendsOnly => { + defaultValue => 0, + }, + rating => { + defaultValue => 0, + }, + ); + + # UserDefined Fields + for my $i (1 .. 5) { + $properties{"userDefined".$i} = { + defaultValue => undef, + }; + } + + push @{$definition}, { + assetName => $i18n->get('assetName'), + autoGenerateForms => 0, + tableName => 'GalleryFile', + className => 'WebGUI::Asset::File::GalleryFile', + properties => \%properties, + }; + + return $class->SUPER::definition($session, $definition); +} + +#---------------------------------------------------------------------------- + +=head2 appendTemplateVarsCommentForm ( var [, comment ] ) + +Add the template variables necessary for the comment form to the given hash +reference. Returns the hash reference for convenience. C is a hash +reference of values to populate the form with. + +=cut + +sub appendTemplateVarsCommentForm { + my $self = shift; + my $var = shift; + my $comment = shift || {}; + my $session = $self->session; + + # Default comment + $comment->{ commentId } ||= "new"; + + $var->{ commentForm_start } + = WebGUI::Form::formHeader( $session ) + . WebGUI::Form::hidden( $session, { + name => "func", + value => "editCommentSave" + } ) + . WebGUI::Form::hidden( $session, { + name => "commentId", + value => $comment->{ commentId } + } ) + ; + + # Add hidden fields for editing a comment + if ( $comment->{ commentId } ne "new" ) { + $var->{ commentForm_start } + .= WebGUI::Form::hidden( $session, { + name => "userId", + value => $comment->{ userId } + } ) + . WebGUI::Form::hidden( $session, { + name => "visitorIp", + value => $comment->{ visitorIp } + } ) + . WebGUI::Form::hidden( $session, { + name => "creationDate", + value => $comment->{ creationDate } + } ) + ; + } + + $var->{ commentForm_end } + = WebGUI::Form::formFooter( $session ); + + $var->{ commentForm_bodyText } + = WebGUI::Form::HTMLArea( $session, { + name => "bodyText", + richEditId => $self->getGallery->get("richEditIdComment"), + value => $comment->{ bodyText }, + }); + + $var->{ commentForm_submit } + = WebGUI::Form::submit( $session, { + name => "submit", + value => "Save Comment", + }); + + return $var; +} + +#---------------------------------------------------------------------------- + +=head2 canAdd ( ) + +Override canAdd to ignore its permissions check. Permissions are handled +by the parent Gallery and other permissions methods. + +=cut + +sub canAdd { + return 1; +} + +#---------------------------------------------------------------------------- + +=head2 canComment ( [userId] ) + +Returns true if the user can comment on this asset. C is a WebGUI +user ID. If no userId is passed, check the current user. + +Users can comment on this GalleryFile if they are allowed to view and the album +allows comments. + +=cut + +sub canComment { + my $self = shift; + my $userId = shift || $self->session->user->userId; + my $album = $self->getParent; + + return 0 if !$self->canView($userId); + + return $album->canComment($userId); +} + +#---------------------------------------------------------------------------- + +=head2 canEdit ( [userId] ) + +Returns true if the user can edit this asset. C is a WebGUI user ID. +If no userId is passed, check the current user. + +Users can edit this GalleryFile if they are the owner or if they are able to edit +the parent Album asset. + +=cut + +sub canEdit { + my $self = shift; + my $userId = shift || $self->session->user->userId; + my $album = $self->getParent; + + return 1 if $userId eq $self->get("ownerUserId"); + return $album->canEdit($userId); +} + +#---------------------------------------------------------------------------- + +=head2 canView ( [userId] ) + +Returns true if the user can view this asset. C is a WebGUI user ID. +If no user is passed, checks the current user. + +Users can view this GalleryFile if they can view the parent asset. If this is a +C GalleryFile, then they must also be in the owners friends list. + +=cut + +sub canView { + my $self = shift; + my $userId = shift || $self->session->user->userId; + + my $album = $self->getParent; + return 0 unless $album->canView($userId); + + if ($self->isFriendsOnly && $userId != $self->get("ownerUserId") ) { + my $owner = WebGUI::User->new( $self->session, $self->get("ownerUserId") ); + return 0 + unless WebGUI::Friends->new($self->session, $owner)->isFriend($userId); + } + + # Passed all checks + return 1; +} + +#---------------------------------------------------------------------------- + +=head2 deleteComment ( commentId ) + +Delete a comment from this asset. C is the ID of the comment to delete. + +=cut + +sub deleteComment { + my $self = shift; + my $commentId = shift; + + croak "GalleryFile->deleteComment: No commentId specified." + unless $commentId; + + return $self->session->db->write( + "DELETE FROM GalleryFile_comment WHERE assetId=? AND commentId=?", + [$self->getId, $commentId], + ); +} + +#---------------------------------------------------------------------------- + +=head2 getAutoCommitWorkflowId ( ) + +Returns the workflowId of the Gallery's approval workflow. + +=cut + +sub getAutoCommitWorkflowId { + my $self = shift; + return $self->getGallery->get("workflowIdCommit"); +} + +#---------------------------------------------------------------------------- + +=head2 getComment ( commentId ) + +Get a comment from this asset. C is the ID of the comment to get. Returns +a hash reference of comment information. + +=cut + +sub getComment { + my $self = shift; + my $commentId = shift; + + return $self->session->db->getRow( + "GalleryFile_comment", "commentId", $commentId, + ); +} + +#---------------------------------------------------------------------------- + +=head2 getCommentIds ( ) + +Get an array reference of comment IDs for this GalleryFile, in chronological order. + +=cut + +sub getCommentIds { + my $self = shift; + + return [ + $self->session->db->buildArray( + "SELECT commentId FROM GalleryFile_comment WHERE assetId=?", + [$self->getId], + ) + ]; +} + +#---------------------------------------------------------------------------- + +=head2 getCommentPaginator ( ) + +Get a WebGUI::Paginator for the comments for this GalleryFile. + +=cut + +sub getCommentPaginator { + my $self = shift; + my $session = $self->session; + + my $p = WebGUI::Paginator->new($session, $self->getUrl); + $p->setDataByQuery( + "SELECT * FROM GalleryFile_comment WHERE assetId=? ORDER BY creationDate DESC", + undef, undef, + [$self->getId], + ); + + return $p; +} + +#---------------------------------------------------------------------------- + +=head2 getGallery ( ) + +Gets the Gallery asset this GalleryFile is a member of. + +=cut + +sub getGallery { + my $self = shift; + my $gallery = $self->getParent->getParent; + return $gallery if $gallery->isa("WebGUI::Asset::Wobject::Gallery"); + return undef; +} + #---------------------------------------------------------------------------- =head2 getThumbnailUrl ( ) @@ -46,6 +353,7 @@ overridded by your child class. sub getThumbnailUrl { my $self = shift; + # TODO: Make a "default" thumbnail } #---------------------------------------------------------------------------- @@ -59,14 +367,200 @@ method. sub getTemplateVars { my $self = shift; + my $session = $self->session; my $var = $self->get; + my $owner = WebGUI::User->new( $session, $self->get("ownerUserId") ); $var->{ fileUrl } = $self->getFileUrl; $var->{ thumbnailUrl } = $self->getThumbnailUrl; + # Fix 'undef' vars since HTML::Template does inheritence on them + for my $key ( qw( synopsis ) ) { + unless ( defined $var->{$key} ) { + $var->{ $key } = ''; + } + } + + # Add some things from Gallery + my $galleryVar = $self->getGallery->getTemplateVars; + for my $key ( qw{ url_listFilesForCurrentUser url_search } ) { + $var->{ $key } = $galleryVar->{ $key }; + } + + # Add the search form + $self->getGallery->appendTemplateVarsSearchForm( $var ); + + $var->{ canComment } = $self->canComment; + $var->{ canEdit } = $self->canEdit; + $var->{ numberOfComments } = scalar @{ $self->getCommentIds }; + $var->{ ownerUsername } = $owner->profileField("alias") || $owner->username; + $var->{ url } = $self->getUrl; + $var->{ url_addArchive } = $self->getParent->getUrl('func=addArchive'), + $var->{ url_delete } = $self->getUrl('func=delete'); + $var->{ url_demote } = $self->getUrl('func=demote'); + $var->{ url_edit } = $self->getUrl('func=edit'); + $var->{ url_gallery } = $self->getGallery->getUrl; + $var->{ url_makeShortcut } = $self->getUrl('func=makeShortcut'); + $var->{ url_listFilesForOwner } + = $self->getGallery->getUrl('func=listFilesForUser;userId=' . $self->get("ownerUserId")); + $var->{ url_promote } = $self->getUrl('func=promote'); + return $var; } + +#---------------------------------------------------------------------------- + +=head2 i18n ( session ) + +Get the i18n object for this class. This sub must not be inherited, so always +call it using C<__PACKAGE__>, not C<$self>. + +=cut + +sub i18n { + my $self = shift; + my $session = shift; + # TODO: Make a migration script to move the appropriate parts from + # Asset_Photo to Asset_GalleryFile + return WebGUI::International->new( $session, "Asset_Photo" ); +} + +#---------------------------------------------------------------------------- + +=head2 isFriendsOnly ( ) + +Returns true if this GalleryFile is friends only. Returns false otherwise. + +=cut + +sub isFriendsOnly { + my $self = shift; + return $self->get("friendsOnly"); +} + +#---------------------------------------------------------------------------- + +=head2 makeShortcut ( parentId [, overrides ] ) + +Make a shortcut to this asset under the specified parent, optionally adding +the specified hash reference of C. + +Returns the created shortcut asset. + +=cut + +sub makeShortcut { + my $self = shift; + my $parentId = shift; + my $overrides = shift; + my $session = $self->session; + + croak "GalleryFile->makeShortcut: parentId must be defined" + unless $parentId; + + my $parent = WebGUI::Asset->newByDynamicClass($session, $parentId) + || croak "GalleryFile->makeShortcut: Could not instanciate asset '$parentId'"; + + my $shortcut + = $parent->addChild({ + className => "WebGUI::Asset::Shortcut", + shortcutToAssetId => $self->getId, + }); + + if ($overrides) { + $shortcut->setOverride( $overrides ); + } + + return $shortcut; +} + +#---------------------------------------------------------------------------- + +=head2 prepareView ( ) + +Prepare the template to be used for the C method. + +=cut + +sub prepareView { + my $self = shift; + $self->SUPER::prepareView(); + + my $template + = WebGUI::Asset::Template->new($self->session, $self->getGallery->get("templateIdViewFile")); + $template->prepare; + + $self->{_viewTemplate} = $template; +} + +#---------------------------------------------------------------------------- + +=head2 processCommentEditForm ( ) + +Process the Comment Add / Edit Form. Returns a hash reference of properties +that can be passed to C. + +Will die with an i18n-friendly error message if something is missing or +wrong. + +=cut + +sub processCommentEditForm { + my $self = shift; + my $session = $self->session; + my $form = $self->session->form; + my $now = WebGUI::DateTime->new( $session, time ); + my $i18n = __PACKAGE__->i18n( $session ); + + # Using die here to suppress line number and file path info + die $i18n->get("commentForm error no commentId") . "\n" + unless $form->get("commentId"); + die $i18n->get("commentForm error no bodyText") . "\n" + unless $form->get("bodyText"); + + my $new = $form->get('commentId') eq "new" + ? 1 + : 0 + ; + + my $visitorIp = $session->user->userId eq "1" + ? $session->env->get("REMOTE_ADDR") + : undef + ; + + my $properties = { + commentId => $form->get("commentId"), + assetId => $self->getId, + bodyText => $form->get("bodyText"), + creationDate => ( $new ? $now->toDatabaseDate : $form->get("creationDate") ), + userId => ( $new ? $session->user->userId : $form->get("userId") ), + visitorIp => ( $new ? $visitorIp : $form->get("visitorIp") ), + }; + + return $properties; +} + +#---------------------------------------------------------------------------- + +=head2 processPropertiesFromFormPost ( ) + + +=cut + +sub processPropertiesFromFormPost { + my $self = shift; + my $form = $self->session->form; + my $errors = $self->SUPER::processPropertiesFromFormPost || []; + + # Return if errors + return $errors if @$errors; + + ### Passes all checks + + $self->requestAutoCommit; +} + #---------------------------------------------------------------------------- =head2 processStyle ( html ) @@ -82,6 +576,186 @@ sub processStyle { #---------------------------------------------------------------------------- +=head2 purge ( ) + +Purge the asset. Remove all comments on the GalleryFile. + +=cut + +sub purge { + my $self = shift; + + for my $commentId ( @{ $self->getCommentIds } ) { + $self->deleteComment( $commentId ); + } + + return $self->SUPER::purge; +} + +#---------------------------------------------------------------------------- + +=head2 setComment ( properties ) + +Set a comment. C is a hash reference of comment information with +the following keys: + + assetId - The assetId of the asset this comment is for + commentId - The ID of the comment. If "new", will make a new comment. + bodyText - The body of the comment + userId - The userId of the user who made the comment + visitorIp - If the user was a visitor, the IP address of the user + creationDate - A MySQL-formatted date/time when the comment was posted + +=cut + +sub setComment { + my $self = shift; + my $properties = shift; + + croak "GalleryFile->setComment: properties must be a hash reference" + unless $properties && ref $properties eq "HASH"; + croak "GalleryFile->setComment: commentId must be defined" + unless $properties->{ commentId }; + croak "GalleryFile->setComment: properties must contain a bodyText key" + unless $properties->{ bodyText }; + + $properties->{ creationDate } ||= WebGUI::DateTime->new($self->session, time)->toDatabase; + $properties->{ assetId } = $self->getId; + + return $self->session->db->setRow( + "GalleryFile_comment", "commentId", + $properties, + ); +} + +#---------------------------------------------------------------------------- + +=head2 view ( ) + +method called by the container www_view method. + +=cut + +sub view { + my $self = shift; + my $session = $self->session; + my $var = $self->getTemplateVars; + + $self->appendTemplateVarsCommentForm( $var ); + + # Keywords + my $k = WebGUI::Keyword->new( $session ); + my $keywords = $k->getKeywordsForAsset( { asArrayRef => 1, asset => $self } ); + for my $keyword ( @{ $keywords } ) { + push @{ $var->{keywords} }, { + keyword => $keyword, + url_searchKeyword + => $self->getGallery->getUrl( + "func=search;submit=1;keywords=" . uri_escape($keyword) + ), + url_searchKeywordUser + => $self->getGallery->getUrl( + "func=search;submit=1;" + . "userId=" . $self->get("ownerUserId") . ';' + . 'keywords=' . uri_escape( $keyword ) + ), + }; + } + + # Comments + my $p = $self->getCommentPaginator; + for my $comment ( @{ $p->getPageData } ) { + $comment->{ url_deleteComment } + = $self->getUrl('func=deleteComment;commentId=' . $comment->{commentId} ); + $comment->{ url_editComment } + = $self->getUrl('func=editComment;commentId=' . $comment->{commentId} ); + + my $user = WebGUI::User->new( $session, $comment->{userId} ); + $comment->{ username } = $user->username; + + my $dt = WebGUI::DateTime->new( $session, $comment->{ creationDate } ); + $comment->{ creationDate } = $dt->toUserTimeZone; + + push @{ $var->{commentLoop} }, $comment; + } + $var->{ commentLoop_pageBar } = $p->getBarAdvanced; + + return $self->processTemplate($var, undef, $self->{_viewTemplate}); +} + +#---------------------------------------------------------------------------- + +=head2 www_delete ( ) + +Show the page to confirm the deletion of this GalleryFile. Show a list of albums +this GalleryFile exists in. + +=cut + +sub www_delete { + my $self = shift; + my $session = $self->session; + + return $self->session->privilege->insufficient unless $self->canEdit; + + my $var = $self->getTemplateVars; + $var->{ url_yes } = $self->getUrl("func=deleteConfirm"); + + # TODO Get albums with shortcuts to this asset + + return $self->processStyle( + $self->processTemplate( $var, $self->getGallery->get("templateIdDeleteFile") ) + ); +} + +#---------------------------------------------------------------------------- + +=head2 www_deleteComment ( ) + +Delete a comment immediately. Only those who can edit this GalleryFile can delete +comments on it. + +=cut + +sub www_deleteComment { + my $self = shift; + my $session = $self->session; + + return $session->privilege->insufficient unless $self->canEdit; + + my $i18n = __PACKAGE__->i18n( $session ); + my $commentId = $session->form->get('commentId'); + + $self->deleteComment( $commentId ); + + return $self->www_view; +} + +#---------------------------------------------------------------------------- + +=head2 www_deleteConfirm ( ) + +Confirm the deletion of this GalleryFile. Show a message and a link back to the +album. + +=cut + +sub www_deleteConfirm { + my $self = shift; + + return $self->session->privilege->insufficient unless $self->canEdit; + + my $i18n = __PACKAGE__->i18n( $self->session ); + + $self->purge; + + return $self->processStyle( + sprintf $i18n->get("delete message"), $self->getParent->getUrl, + ); +} + +#---------------------------------------------------------------------------- + =head2 www_demote Override the default demote page to send the user back to the GalleryAlbum @@ -101,6 +775,151 @@ sub www_demote { #---------------------------------------------------------------------------- +=head2 www_editComment ( params ) + +Form to edit a comment. C is a hash reference of parameters +with the following keys: + + errors = An array reference of errors to show the user. + +=cut + +sub www_editComment { + my $self = shift; + my $params = shift; + my $session = $self->session; + + return $session->privilege->insufficient unless $self->canEdit; + + my $var = $self->getTemplateVars; + + if ( $params->{ errors } ) { + $var->{ errors } = [ map { { "error" => $_ } } @{ $params->{errors} } ]; + } + + my $commentId = $session->form->get( "commentId" ); + my $comment = $commentId ne "new" + ? $self->getComment( $commentId ) + : {} + ; + $self->appendTemplateVarsCommentForm( $var, $comment ); + + $var->{ isNew } = $commentId eq "new"; + + return $self->processStyle( + $self->processTemplate( $var, $self->getGallery->get("templateIdEditComment") ) + ); +} + +#---------------------------------------------------------------------------- + +=head2 www_editCommentSave ( ) + +Save a comment being edited + +=cut + +sub www_editCommentSave { + my $self = shift; + my $session = $self->session; + + return $session->privilege->insufficient unless $self->canEdit; + + my $i18n = __PACKAGE__->i18n( $session ); + + my $comment = eval { $self->processCommentEditForm }; + if ( $@ ) { + return $self->www_editComment( { errors => [ $@ ] } ); + } + + # setComment changes commentId, so keep track if we're adding a new comment + my $isNew = $comment->{commentId} eq "new"; + + $self->setComment( $comment ); + + # Return different message for adding and editing + if ( $isNew ) { + return $self->processStyle( + sprintf $i18n->get('comment message'), $self->getUrl + ); + } + else { + return $self->processStyle( + sprintf $i18n->get('editCommentSave message'), $self->getUrl + ); + } +} + +#---------------------------------------------------------------------------- + +=head2 www_makeShortcut ( ) + +Display the form to make a shortcut. + +This page is only available to those who can edit this GalleryFile. + +=cut + +sub www_makeShortcut { + my $self = shift; + my $session = $self->session; + + return $self->session->privilege->insufficient unless $self->canEdit; + + # Create the form to make a shortcut + my $var = $self->getTemplateVars; + + $var->{ form_start } + = WebGUI::Form::formHeader( $session ) + . WebGUI::Form::hidden( $session, { name => "func", value => "makeShortcutSave" }); + $var->{ form_end } + = WebGUI::Form::formFooter( $session ); + + # Albums under this Gallery + my $albums = $self->getGallery->getAlbumIds; + my %albumOptions; + for my $assetId ( @$albums ) { + my $asset = WebGUI::Asset->newByDynamicClass($session, $assetId); + if ($asset->canAddFile) { + $albumOptions{ $assetId } = $asset->get("title"); + } + } + $var->{ form_parentId } + = WebGUI::Form::selectBox( $session, { + name => "parentId", + value => $self->getParent->getId, + options => \%albumOptions, + }); + + return $self->processStyle( + $self->processTemplate($var, $self->getGallery->get("templateIdMakeShortcut")) + ); +} + +#---------------------------------------------------------------------------- + +=head2 www_makeShortcutSave ( ) + +Make the shortcut. + +This page is only available to those who can edit this GalleryFile. + +=cut + +sub www_makeShortcutSave { + my $self = shift; + my $form = $self->session->form; + + return $self->session->privilege->insufficient unless $self->canEdit; + + my $parentId = $form->get('parentId'); + my $shortcut = $self->makeShortcut( $parentId ); + + return $shortcut->www_view; +} + +#---------------------------------------------------------------------------- + =head2 www_promote Override the default promote page to send the user back to the GalleryAlbum @@ -122,12 +941,19 @@ sub www_promote { =head2 www_view ( ) -Show the default view, with content chunking. +Shows the output of L inside of the style provided by the gallery this +GalleryFile is in. =cut sub www_view { - my $self = shift; + my $self = shift; + + return $self->session->privilege->insufficient unless $self->canView; + + # Add to views + $self->update({ views => $self->get('views') + 1 }); + $self->session->http->setLastModified($self->getContentLastModified); $self->session->http->sendHeader; $self->prepareView; @@ -139,4 +965,5 @@ sub www_view { return "chunked"; } + 1; # Who knew the truth would be so obvious? diff --git a/lib/WebGUI/Asset/File/GalleryFile/Photo.pm b/lib/WebGUI/Asset/File/GalleryFile/Photo.pm index 0fab64b66..f3af924b6 100644 --- a/lib/WebGUI/Asset/File/GalleryFile/Photo.pm +++ b/lib/WebGUI/Asset/File/GalleryFile/Photo.pm @@ -73,37 +73,20 @@ sub definition { my $i18n = __PACKAGE__->i18n($session); tie my %properties, 'Tie::IxHash', ( - views => { - defaultValue => 0, - }, exifData => { defaultValue => undef, }, - friendsOnly => { - defaultValue => 0, - }, location => { defaultValue => undef, }, - rating => { - defaultValue => 0, - }, ); - # UserDefined Fields - for my $i (1 .. 5) { - $properties{"userDefined".$i} = { - defaultValue => undef, - }; - } - push @{$definition}, { assetName => $i18n->get('assetName'), autoGenerateForms => 0, icon => 'photo.gif', tableName => 'Photo', className => 'WebGUI::Asset::File::GalleryFile::Photo', - i18n => 'Asset_Photo', properties => \%properties, }; @@ -112,74 +95,6 @@ sub definition { #---------------------------------------------------------------------------- -=head2 appendTemplateVarsCommentForm ( var [, comment ] ) - -Add the template variables necessary for the comment form to the given hash -reference. Returns the hash reference for convenience. C is a hash -reference of values to populate the form with. - -=cut - -sub appendTemplateVarsCommentForm { - my $self = shift; - my $var = shift; - my $comment = shift || {}; - my $session = $self->session; - - # Default comment - $comment->{ commentId } ||= "new"; - - $var->{ commentForm_start } - = WebGUI::Form::formHeader( $session ) - . WebGUI::Form::hidden( $session, { - name => "func", - value => "editCommentSave" - } ) - . WebGUI::Form::hidden( $session, { - name => "commentId", - value => $comment->{ commentId } - } ) - ; - - # Add hidden fields for editing a comment - if ( $comment->{ commentId } ne "new" ) { - $var->{ commentForm_start } - .= WebGUI::Form::hidden( $session, { - name => "userId", - value => $comment->{ userId } - } ) - . WebGUI::Form::hidden( $session, { - name => "visitorIp", - value => $comment->{ visitorIp } - } ) - . WebGUI::Form::hidden( $session, { - name => "creationDate", - value => $comment->{ creationDate } - } ) - ; - } - - $var->{ commentForm_end } - = WebGUI::Form::formFooter( $session ); - - $var->{ commentForm_bodyText } - = WebGUI::Form::HTMLArea( $session, { - name => "bodyText", - richEditId => $self->getGallery->get("richEditIdComment"), - value => $comment->{ bodyText }, - }); - - $var->{ commentForm_submit } - = WebGUI::Form::submit( $session, { - name => "submit", - value => "Save Comment", - }); - - return $var; -} - -#---------------------------------------------------------------------------- - =head2 applyConstraints ( options ) Apply the constraints to the original file. Called automatically by C @@ -211,112 +126,6 @@ sub applyConstraints { $self->updateExifDataFromFile; } -#---------------------------------------------------------------------------- - -=head2 canAdd ( ) - -Override canAdd to ignore its permissions check. Permissions are handled -by the parent Gallery and other permissions methods. - -=cut - -sub canAdd { - return 1; -} - -#---------------------------------------------------------------------------- - -=head2 canComment ( [userId] ) - -Returns true if the user can comment on this asset. C is a WebGUI -user ID. If no userId is passed, check the current user. - -Users can comment on this Photo if they are allowed to view and the album -allows comments. - -=cut - -sub canComment { - my $self = shift; - my $userId = shift || $self->session->user->userId; - my $album = $self->getParent; - - return 0 if !$self->canView($userId); - - return $album->canComment($userId); -} - -#---------------------------------------------------------------------------- - -=head2 canEdit ( [userId] ) - -Returns true if the user can edit this asset. C is a WebGUI user ID. -If no userId is passed, check the current user. - -Users can edit this Photo if they are the owner or if they are able to edit -the parent Album asset. - -=cut - -sub canEdit { - my $self = shift; - my $userId = shift || $self->session->user->userId; - my $album = $self->getParent; - - return 1 if $userId eq $self->get("ownerUserId"); - return $album->canEdit($userId); -} - -#---------------------------------------------------------------------------- - -=head2 canView ( [userId] ) - -Returns true if the user can view this asset. C is a WebGUI user ID. -If no user is passed, checks the current user. - -Users can view this photo if they can view the parent asset. If this is a -C photo, then they must also be in the owners friends list. - -=cut - -sub canView { - my $self = shift; - my $userId = shift || $self->session->user->userId; - - my $album = $self->getParent; - return 0 unless $album->canView($userId); - - if ($self->isFriendsOnly && $userId != $self->get("ownerUserId") ) { - my $owner = WebGUI::User->new( $self->session, $self->get("ownerUserId") ); - return 0 - unless WebGUI::Friends->new($self->session, $owner)->isFriend($userId); - } - - # Passed all checks - return 1; -} - -#---------------------------------------------------------------------------- - -=head2 deleteComment ( commentId ) - -Delete a comment from this asset. C is the ID of the comment to delete. - -=cut - -sub deleteComment { - my $self = shift; - my $commentId = shift; - - croak "Photo->deleteComment: No commentId specified." - unless $commentId; - - return $self->session->db->write( - "DELETE FROM Photo_comment WHERE assetId=? AND commentId=?", - [$self->getId, $commentId], - ); -} - #------------------------------------------------------------------- =head2 generateThumbnail ( ) @@ -336,78 +145,6 @@ sub generateThumbnail { #---------------------------------------------------------------------------- -=head2 getAutoCommitWorkflowId ( ) - -Returns the workflowId of the Gallery's approval workflow. - -=cut - -sub getAutoCommitWorkflowId { - my $self = shift; - return $self->getGallery->get("workflowIdCommit"); -} - -#---------------------------------------------------------------------------- - -=head2 getComment ( commentId ) - -Get a comment from this asset. C is the ID of the comment to get. Returns -a hash reference of comment information. - -=cut - -sub getComment { - my $self = shift; - my $commentId = shift; - - return $self->session->db->getRow( - "Photo_comment", "commentId", $commentId, - ); -} - -#---------------------------------------------------------------------------- - -=head2 getCommentIds ( ) - -Get an array reference of comment IDs for this Photo, in chronological order. - -=cut - -sub getCommentIds { - my $self = shift; - - return [ - $self->session->db->buildArray( - "SELECT commentId FROM Photo_comment WHERE assetId=?", - [$self->getId], - ) - ]; -} - -#---------------------------------------------------------------------------- - -=head2 getCommentPaginator ( ) - -Get a WebGUI::Paginator for the comments for this Photo. - -=cut - -sub getCommentPaginator { - my $self = shift; - my $session = $self->session; - - my $p = WebGUI::Paginator->new($session, $self->getUrl); - $p->setDataByQuery( - "SELECT * FROM Photo_comment WHERE assetId=? ORDER BY creationDate DESC", - undef, undef, - [$self->getId], - ); - - return $p; -} - -#---------------------------------------------------------------------------- - =head2 getDownloadFileUrl ( resolution ) Get the absolute URL to download the requested resolution. Will croak if the @@ -424,7 +161,7 @@ sub getDownloadFileUrl { croak "Photo->getDownloadFileUrl: resolution doesn't exist for this Photo" unless grep /$resolution/, @{ $self->getResolutions }; - return $self->getStorageLocation->getFileUrl( $resolution . ".jpg" ); + return $self->getStorageLocation->getUrl( $resolution . ".jpg" ); } #---------------------------------------------------------------------------- @@ -444,21 +181,6 @@ sub getExifData { #---------------------------------------------------------------------------- -=head2 getGallery ( ) - -Gets the Gallery asset this Photo is a member of. - -=cut - -sub getGallery { - my $self = shift; - my $gallery = $self->getParent->getParent; - return $gallery if $gallery->isa("WebGUI::Asset::Wobject::Gallery"); - return undef; -} - -#---------------------------------------------------------------------------- - =head2 getResolutions ( ) Get an array reference of download resolutions that exist for this image. @@ -471,7 +193,7 @@ sub getResolutions { my $storage = $self->getStorageLocation; # Return a list not including the web view image. - return grep { $_ ne $self->get("filename") } @{ $storage->getFiles }; + return [ grep { $_ ne $self->get("filename") } @{ $storage->getFiles } ]; } #---------------------------------------------------------------------------- @@ -499,32 +221,9 @@ sub getTemplateVars { my $self = shift; my $session = $self->session; my $var = $self->SUPER::getTemplateVars; - my $owner = WebGUI::User->new( $session, $self->get("ownerUserId") ); - - # Fix 'undef' vars since HTML::Template does inheritence on them - for my $key ( qw( synopsis ) ) { - unless ( defined $var->{$key} ) { - $var->{ $key } = ''; - } - } - - $var->{ canComment } = $self->canComment; - $var->{ canEdit } = $self->canEdit; - $var->{ numberOfComments } = scalar @{ $self->getCommentIds }; - $var->{ ownerUsername } = $owner->username; - $var->{ url } = $self->getUrl; - $var->{ url_addArchive } = $self->getParent->getUrl('func=addArchive'), - $var->{ url_delete } = $self->getUrl('func=delete'); - $var->{ url_demote } = $self->getUrl('func=demote'); - $var->{ url_edit } = $self->getUrl('func=edit'); - $var->{ url_gallery } = $self->getGallery->getUrl; - $var->{ url_makeShortcut } = $self->getUrl('func=makeShortcut'); - $var->{ url_listFilesForOwner } - = $self->getGallery->getUrl('func=listFilesForUser;userId=' . $self->get("ownerUserId")); - $var->{ url_promote } = $self->getUrl('func=promote'); ### Download resolutions - for my $resolution ( $self->getResolutions ) { + for my $resolution ( @{ $self->getResolutions } ) { push @{ $var->{ resolutions_loop } }, { url_download => $self->getStorageLocation->getPathFrag($resolution) }; @@ -581,19 +280,6 @@ sub i18n { #---------------------------------------------------------------------------- -=head2 isFriendsOnly ( ) - -Returns true if this Photo is friends only. Returns false otherwise. - -=cut - -sub isFriendsOnly { - my $self = shift; - return $self->get("friendsOnly"); -} - -#---------------------------------------------------------------------------- - =head2 makeResolutions ( [resolutions] ) Create the specified resolutions for this Photo. If resolutions is not @@ -630,182 +316,6 @@ sub makeResolutions { #---------------------------------------------------------------------------- -=head2 makeShortcut ( parentId [, overrides ] ) - -Make a shortcut to this asset under the specified parent, optionally adding -the specified hash reference of C. - -Returns the created shortcut asset. - -=cut - -sub makeShortcut { - my $self = shift; - my $parentId = shift; - my $overrides = shift; - my $session = $self->session; - - croak "Photo->makeShortcut: parentId must be defined" - unless $parentId; - - my $parent = WebGUI::Asset->newByDynamicClass($session, $parentId) - || croak "Photo->makeShortcut: Could not instanciate asset '$parentId'"; - - my $shortcut - = $parent->addChild({ - className => "WebGUI::Asset::Shortcut", - shortcutToAssetId => $self->getId, - }); - - if ($overrides) { - $shortcut->setOverride( $overrides ); - } - - return $shortcut; -} - -#---------------------------------------------------------------------------- - -=head2 prepareView ( ) - -Prepare the template to be used for the C method. - -=cut - -sub prepareView { - my $self = shift; - $self->SUPER::prepareView(); - - my $template - = WebGUI::Asset::Template->new($self->session, $self->getGallery->get("templateIdViewFile")); - $template->prepare; - - $self->{_viewTemplate} = $template; -} - -#---------------------------------------------------------------------------- - -=head2 processCommentEditForm ( ) - -Process the Comment Add / Edit Form. Returns a hash reference of properties -that can be passed to C. - -Will die with an i18n-friendly error message if something is missing or -wrong. - -=cut - -sub processCommentEditForm { - my $self = shift; - my $session = $self->session; - my $form = $self->session->form; - my $now = WebGUI::DateTime->new( $session, time ); - my $i18n = __PACKAGE__->i18n( $session ); - - # Using die here to suppress line number and file path info - die $i18n->get("commentForm error no commentId") . "\n" - unless $form->get("commentId"); - die $i18n->get("commentForm error no bodyText") . "\n" - unless $form->get("bodyText"); - - my $new = $form->get('commentId') eq "new" - ? 1 - : 0 - ; - - my $visitorIp = $session->user->userId eq "1" - ? $session->env->get("REMOTE_ADDR") - : undef - ; - - my $properties = { - commentId => $form->get("commentId"), - assetId => $self->getId, - bodyText => $form->get("bodyText"), - creationDate => ( $new ? $now->toDatabaseDate : $form->get("creationDate") ), - userId => ( $new ? $session->user->userId : $form->get("userId") ), - visitorIp => ( $new ? $visitorIp : $form->get("visitorIp") ), - }; - - return $properties; -} - -#---------------------------------------------------------------------------- - -=head2 processPropertiesFromFormPost ( ) - - -=cut - -sub processPropertiesFromFormPost { - my $self = shift; - my $form = $self->session->form; - my $errors = $self->SUPER::processPropertiesFromFormPost || []; - - # Return if errors - return $errors if @$errors; - - ### Passes all checks - - $self->requestAutoCommit; -} - -#---------------------------------------------------------------------------- - -=head2 purge ( ) - -Purge the asset. Remove all comments on the photo. - -=cut - -sub purge { - my $self = shift; - - for my $commentId ( @{ $self->getCommentIds } ) { - $self->deleteComment( $commentId ); - } - - return $self->SUPER::purge; -} - -#---------------------------------------------------------------------------- - -=head2 setComment ( properties ) - -Set a comment. C is a hash reference of comment information with -the following keys: - - assetId - The assetId of the asset this comment is for - commentId - The ID of the comment. If "new", will make a new comment. - bodyText - The body of the comment - userId - The userId of the user who made the comment - visitorIp - If the user was a visitor, the IP address of the user - creationDate - A MySQL-formatted date/time when the comment was posted - -=cut - -sub setComment { - my $self = shift; - my $properties = shift; - - croak "Photo->setComment: properties must be a hash reference" - unless $properties && ref $properties eq "HASH"; - croak "Photo->setComment: commentId must be defined" - unless $properties->{ commentId }; - croak "Photo->setComment: properties must contain a bodyText key" - unless $properties->{ bodyText }; - - $properties->{ creationDate } ||= WebGUI::DateTime->new($self->session, time)->toDatabase; - $properties->{ assetId } = $self->getId; - - return $self->session->db->setRow( - "Photo_comment", "commentId", - $properties, - ); -} - -#---------------------------------------------------------------------------- - =head2 setFile ( filename ) Extend the superclass setFile to automatically generate thumbnails. @@ -848,124 +358,6 @@ sub updateExifDataFromFile { #---------------------------------------------------------------------------- -=head2 view ( ) - -method called by the container www_view method. - -=cut - -sub view { - my $self = shift; - my $session = $self->session; - my $var = $self->getTemplateVars; - - $self->appendTemplateVarsCommentForm( $var ); - - # Keywords - my $k = WebGUI::Keyword->new( $session ); - my $keywords = $k->getKeywordsForAsset( { asArrayRef => 1, asset => $self } ); - for my $keyword ( @{ $keywords } ) { - push @{ $var->{keywords} }, { - url_searchKeyword - => $self->getGallery->getUrl("func=search;submit=1;keywords=" . uri_escape($keyword) ), - keyword => $keyword, - }; - } - - # Comments - my $p = $self->getCommentPaginator; - for my $comment ( @{ $p->getPageData } ) { - $comment->{ url_deleteComment } - = $self->getUrl('func=deleteComment;commentId=' . $comment->{commentId} ); - $comment->{ url_editComment } - = $self->getUrl('func=editComment;commentId=' . $comment->{commentId} ); - - my $user = WebGUI::User->new( $session, $comment->{userId} ); - $comment->{ username } = $user->username; - - my $dt = WebGUI::DateTime->new( $session, $comment->{ creationDate } ); - $comment->{ creationDate } = $dt->toUserTimeZone; - - push @{ $var->{commentLoop} }, $comment; - } - $var->{ commentLoop_pageBar } = $p->getBarAdvanced; - - return $self->processTemplate($var, undef, $self->{_viewTemplate}); -} - -#---------------------------------------------------------------------------- - -=head2 www_delete ( ) - -Show the page to confirm the deletion of this Photo. Show a list of albums -this Photo exists in. - -=cut - -sub www_delete { - my $self = shift; - my $session = $self->session; - - return $self->session->privilege->insufficient unless $self->canEdit; - - my $var = $self->getTemplateVars; - $var->{ url_yes } = $self->getUrl("func=deleteConfirm"); - - # TODO Get albums with shortcuts to this asset - - return $self->processStyle( - $self->processTemplate( $var, $self->getGallery->get("templateIdDeleteFile") ) - ); -} - -#---------------------------------------------------------------------------- - -=head2 www_deleteComment ( ) - -Delete a comment immediately. Only those who can edit this Photo can delete -comments on it. - -=cut - -sub www_deleteComment { - my $self = shift; - my $session = $self->session; - - return $session->privilege->insufficient unless $self->canEdit; - - my $i18n = __PACKAGE__->i18n( $session ); - my $commentId = $session->form->get('commentId'); - - $self->deleteComment( $commentId ); - - return $self->www_view; -} - -#---------------------------------------------------------------------------- - -=head2 www_deleteConfirm ( ) - -Confirm the deletion of this Photo. Show a message and a link back to the -album. - -=cut - -sub www_deleteConfirm { - my $self = shift; - - return $self->session->privilege->insufficient unless $self->canEdit; - - my $i18n = __PACKAGE__->i18n( $self->session ); - - $self->purge; - - return $self->processStyle( - sprintf $i18n->get("delete message"), $self->getParent->getUrl, - ); -} - -#---------------------------------------------------------------------------- - =head2 www_download Download the Photo with the specified resolution. If no resolution specified, @@ -1101,146 +493,6 @@ sub www_edit { #---------------------------------------------------------------------------- -=head2 www_editComment ( params ) - -Form to edit a comment. C is a hash reference of parameters -with the following keys: - - errors = An array reference of errors to show the user. - -=cut - -sub www_editComment { - my $self = shift; - my $params = shift; - my $session = $self->session; - - return $session->privilege->insufficient unless $self->canEdit; - - my $var = $self->getTemplateVars; - - if ( $params->{ errors } ) { - $var->{ errors } = [ map { { "error" => $_ } } @{ $params->{errors} } ]; - } - - my $commentId = $session->form->get( "commentId" ); - my $comment = $self->getComment( $commentId ); - $self->appendTemplateVarsCommentForm( $var, $comment ); - - return $self->processStyle( - $self->processTemplate( $var, $self->getGallery->get("templateIdEditComment") ) - ); -} - -#---------------------------------------------------------------------------- - -=head2 www_editCommentSave ( ) - -Save a comment being edited - -=cut - -sub www_editCommentSave { - my $self = shift; - my $session = $self->session; - - return $session->privilege->insufficient unless $self->canEdit; - - my $i18n = __PACKAGE__->i18n( $session ); - - my $comment = eval { $self->processCommentEditForm }; - if ( $@ ) { - return $self->www_editComment( { errors => [ $@ ] } ); - } - - # setComment changes commentId, so keep track if we're adding a new comment - my $isNew = $comment->{commentId} eq "new"; - - $self->setComment( $comment ); - - # Return different message for adding and editing - if ( $isNew ) { - return $self->processStyle( - sprintf $i18n->get('comment message'), $self->getUrl - ); - } - else { - return $self->processStyle( - sprintf $i18n->get('editCommentSave message'), $self->getUrl - ); - } -} - -#---------------------------------------------------------------------------- - -=head2 www_makeShortcut ( ) - -Display the form to make a shortcut. - -This page is only available to those who can edit this Photo. - -=cut - -sub www_makeShortcut { - my $self = shift; - my $session = $self->session; - - return $self->session->privilege->insufficient unless $self->canEdit; - - # Create the form to make a shortcut - my $var = $self->getTemplateVars; - - $var->{ form_start } - = WebGUI::Form::formHeader( $session ) - . WebGUI::Form::hidden( $session, { name => "func", value => "makeShortcutSave" }); - $var->{ form_end } - = WebGUI::Form::formFooter( $session ); - - # Albums under this Gallery - my $albums = $self->getGallery->getAlbumIds; - my %albumOptions; - for my $assetId ( @$albums ) { - my $asset = WebGUI::Asset->newByDynamicClass($session, $assetId); - if ($asset->canAddFile) { - $albumOptions{ $assetId } = $asset->get("title"); - } - } - $var->{ form_parentId } - = WebGUI::Form::selectBox( $session, { - name => "parentId", - value => $self->getParent->getId, - options => \%albumOptions, - }); - - return $self->processStyle( - $self->processTemplate($var, $self->getGallery->get("templateIdMakeShortcut")) - ); -} - -#---------------------------------------------------------------------------- - -=head2 www_makeShortcutSave ( ) - -Make the shortcut. - -This page is only available to those who can edit this Photo. - -=cut - -sub www_makeShortcutSave { - my $self = shift; - my $form = $self->session->form; - - return $self->session->privilege->insufficient unless $self->canEdit; - - my $parentId = $form->get('parentId'); - my $shortcut = $self->makeShortcut( $parentId ); - - return $shortcut->www_view; -} - -#---------------------------------------------------------------------------- - =head2 www_showConfirmation ( ) Shows the confirmation message after adding / editing a gallery album. @@ -1260,24 +512,4 @@ sub www_showConfirmation { ); } -#---------------------------------------------------------------------------- - -=head2 www_view ( ) - -Shows the output of L inside of the style provided by the gallery this -photo is in. - -=cut - -sub www_view { - my $self = shift; - - return $self->session->privilege->insufficient unless $self->canView; - - # Add to views - $self->update({ views => $self->get('views') + 1 }); - - return $self->SUPER::www_view; -} - 1; diff --git a/lib/WebGUI/Asset/Wobject/Gallery.pm b/lib/WebGUI/Asset/Wobject/Gallery.pm index 08ac3d984..7207fbb66 100644 --- a/lib/WebGUI/Asset/Wobject/Gallery.pm +++ b/lib/WebGUI/Asset/Wobject/Gallery.pm @@ -82,13 +82,6 @@ sub definition { label => $i18n->get("groupIdAddFile label"), hoverHelp => $i18n->get("groupIdAddFile description"), }, - groupIdModerator => { - tab => "security", - fieldType => "group", - defaultValue => 3, # Admins - label => $i18n->get("groupIdModerator label"), - hoverHelp => $i18n->get("groupIdModerator description"), - }, imageResolutions => { tab => "properties", fieldType => "checkList", @@ -963,6 +956,11 @@ sub www_search { . $db->quote( '%' . $form->get("description") . '%' ) ; } + if ( $form->get("userId") ) { + $where .= q{ AND assetData.ownerUserId = } + . $db->quote( $form->get("userId") ) + ; + } my $joinClass = [ 'WebGUI::Asset::Wobject::GalleryAlbum', @@ -983,6 +981,7 @@ sub www_search { . 'className=' . $form->get('className') . ';' . 'creationDate_after=' . $form->get('creationDate_after') . ';' . 'creationDate_before=' . $form->get('creationDate_before') . ';' + . 'userId=' . $form->get("userId") . ';' ); my $p diff --git a/lib/WebGUI/Asset/Wobject/GalleryAlbum.pm b/lib/WebGUI/Asset/Wobject/GalleryAlbum.pm index 81847bc6d..591e40067 100644 --- a/lib/WebGUI/Asset/Wobject/GalleryAlbum.pm +++ b/lib/WebGUI/Asset/Wobject/GalleryAlbum.pm @@ -120,16 +120,18 @@ sub addArchive { for my $filePath (@files) { my ($volume, $directory, $filename) = File::Spec->splitpath( $filePath ); - $self->session->errorHandler->info( "trying $filename" ); next if $filename =~ m{^[.]}; my $class = $gallery->getAssetClassForFile( $filePath ); next unless $class; # class is undef for those files the Gallery can't handle $self->session->errorHandler->info( "Adding $filename to album!" ); + # Remove the file extention + $filename =~ s{\.[^.]+}{}; + $properties->{ className } = $class; $properties->{ menuTitle } = $filename; $properties->{ title } = $filename; - $properties->{ url } = $self->getUrl . "/" . $filename; + $properties->{ url } = $self->session->url->urlize( $self->getUrl . "/" . $filename ); my $asset = $self->addChild( $properties, undef, undef, { skipAutoCommitWorkflows => 1 } ); $asset->setFile( $filePath ); diff --git a/lib/WebGUI/Help/Asset_Photo.pm b/lib/WebGUI/Help/Asset_Photo.pm index 4b2ace319..391891e72 100644 --- a/lib/WebGUI/Help/Asset_Photo.pm +++ b/lib/WebGUI/Help/Asset_Photo.pm @@ -257,6 +257,24 @@ our $HELP = { name => 'commentLoop_pageBar', description => 'helpvar commentLoop_pageBar', }, + { + name => 'keywords', + description => 'helpvar keywords', + variables => [ + { + name => 'keyword', + description => 'helpvar keyword', + }, + { + name => 'url_searchKeyword', + description => 'helpvar url_searchKeyword', + }, + { + name => 'url_searchKeywordUser', + description => 'helpvar url_searchKeywordUser', + }, + ], + }, ], }, diff --git a/lib/WebGUI/Asset/Wobject/Gallery/Utility.pm b/lib/WebGUI/Utility/Gallery.pm similarity index 96% rename from lib/WebGUI/Asset/Wobject/Gallery/Utility.pm rename to lib/WebGUI/Utility/Gallery.pm index c4f1da162..e9514d7f2 100644 --- a/lib/WebGUI/Asset/Wobject/Gallery/Utility.pm +++ b/lib/WebGUI/Utility/Gallery.pm @@ -1,4 +1,4 @@ -package WebGUI::Asset::Wobject::Gallery::Utility; +package WebGUI::Utility::Gallery; =head1 LEGAL @@ -26,7 +26,7 @@ use WebGUI::Storage::Image; =head1 NAME -WebGUI::Asset::Wobject::Gallery::Utility -- Utility functions for working +WebGUI::Utility::Gallery -- Utility functions for working with Gallery assets. =head1 DESCRIPTION @@ -38,8 +38,8 @@ This module is B to be used by the Gallery asset itself! =head1 SYNOPSIS - use WebGUI::Asset::Wobject::Gallery::Utility; - my $utility = "WebGUI::Asset::Wobject::Gallery::Utility" # <- not as cumbersome + use WebGUI::Utility::Gallery; + my $utility = "WebGUI::Utility::Gallery" # <- not as cumbersome # Add albums from a collaboration system's threads my $gallery = WebGUI::Asset::Wobject::Gallery->new( ... ); diff --git a/lib/WebGUI/i18n/English/Asset_GalleryAlbum.pm b/lib/WebGUI/i18n/English/Asset_GalleryAlbum.pm index 6f7aa0bd1..228dc378a 100644 --- a/lib/WebGUI/i18n/English/Asset_GalleryAlbum.pm +++ b/lib/WebGUI/i18n/English/Asset_GalleryAlbum.pm @@ -442,6 +442,23 @@ our $I18N = { context => 'Body text explaining what kinds of archives can be submitted', }, + 'template delete message' => { + message => q{Are you sure you wish to delete this gallery?}, + lastUpdated => 0, + context => q{The message for the delete page}, + }, + + 'template delete no' => { + message => q{No}, + lastUpdated => 0, + context => q{Label for button to cancel the delete}, + }, + + 'template delete yes' => { + message => q{Yes}, + lastUpdated => 0, + context => q{Label for button to confirm the delete}, + }, }; 1; diff --git a/lib/WebGUI/i18n/English/Asset_Photo.pm b/lib/WebGUI/i18n/English/Asset_Photo.pm index 638894f39..7d2e92f6e 100644 --- a/lib/WebGUI/i18n/English/Asset_Photo.pm +++ b/lib/WebGUI/i18n/English/Asset_Photo.pm @@ -510,6 +510,60 @@ our $I18N = { lastUpdated => 0, context => q{Error message for Photo comments}, }, + + 'helpvar keywords' => { + message => q{A loop over the keywords associated with this photo}, + lastUpdated => 0, + context => q{Description of template loop}, + }, + + 'helpvar keyword' => { + message => q{The keyword}, + lastUpdated => 0, + context => q{Description of template variable}, + }, + + 'helpvar url_searchKeyword' => { + message => q{A URL to the Gallery search page for this keyword}, + lastUpdated => 0, + context => q{Description of template variable}, + }, + + 'helpvar url_searchKeywordUser' => { + message => q{A URL to the Gallery search page for this keyword. Limits the search to Photos by this user.}, + lastUpdated => 0, + context => q{Description of template variable}, + }, + + 'template makeShortcut title' => { + message => q{Cross Publish}, + lastUpdated => 0, + context => q{Title for the make shortcut page}, + }, + + 'template makeShortcut file' => { + message => q{File}, + lastUpdated => 0, + context => q{Label for the file we're making a shortcut of}, + }, + + 'template makeShortcut album' => { + message => q{Album}, + lastUpdated => 0, + context => q{Label for the album in which to make the shortcut}, + }, + + 'template delete message' => { + message => q{Are you sure you wish to delete this gallery?}, + lastUpdated => 0, + context => q{The message for the delete page}, + }, + + 'template delete albums' => { + message => q{Photo is currently in these albums:}, + lastUpdated => 0, + context => q{Label for the albums the photo will be removed from.}, + }, }; diff --git a/sbin/migrateCollabToGallery.pl b/sbin/migrateCollabToGallery.pl index e850bd855..be9ab59ef 100644 --- a/sbin/migrateCollabToGallery.pl +++ b/sbin/migrateCollabToGallery.pl @@ -3,7 +3,7 @@ use lib "../lib"; use strict; use Getopt::Long; use Pod::Usage; -use WebGUI::Asset::Wobject::Gallery::Utility; +use WebGUI::Utility::Gallery; use WebGUI::Session; my $session = start(); @@ -11,7 +11,7 @@ my $session = start(); my $collab = getCollaborationFromArgs(); my $gallery = getGalleryFromArgs(); -WebGUI::Asset::Wobject::Gallery::Utility->addAlbumFromCollaboration( $gallery, $collab ); +WebGUI::Utility::Gallery->addAlbumFromCollaboration( $gallery, $collab ); finish($session); @@ -133,4 +133,4 @@ The WebGUI config file to use. =head1 DESCRIPTION This script migrates a collaboration system's threads into gallery albums. It -uses C for its major features. +uses C for its major features. diff --git a/t/Asset/File/GalleryFile/Photo/comment.t b/t/Asset/File/GalleryFile/Photo/comment.t index 25401a8d2..9c66976d3 100644 --- a/t/Asset/File/GalleryFile/Photo/comment.t +++ b/t/Asset/File/GalleryFile/Photo/comment.t @@ -50,7 +50,7 @@ $versionTags[-1]->commit; #---------------------------------------------------------------------------- # Tests -plan tests => 29; +plan tests => 32; #---------------------------------------------------------------------------- # Test with no comments @@ -184,11 +184,70 @@ ok( ); #---------------------------------------------------------------------------- -# Test appendTemplateVarsForCommentForm -TODO: { - local $TODO = "Test appendTemplateVarsForCommentForm"; - ok(0, "Test template variable generation"); -} +# Test appendTemplateVarsForCommentForm for a new comment +my $var = {}; +my $newVar = $photo->appendTemplateVarsCommentForm( $var ); + +is ( $var, $newVar, "appendTemplateVarsCommentForm returns the same hashref it's given" ); +cmp_deeply( + $var, + superhashof( { + commentForm_start => all( + re( qr/]+name="func"[^>]+value="editCommentSave"[^>]+>/ ), + re( qr/]+name="commentId"[^>]+value="new"[^>]+>/ ), + ), + commentForm_end => all( + re( qr{} ), + ), + commentForm_bodyText => all( + re( qr{]+>} ), + re( qr{TinyMCE}i ), + ), + commentForm_submit => all( + re( qr/]+type="submit"[^>]+name="submit"[^>]+value="Save Comment"[^>]+>/ ), + ), + } ), + "appendTemplateVarsCommentForm returns the correct structure", +); + +#---------------------------------------------------------------------------- +# Test appendTemplateVarsForCommentForm for an existing comment +$var = {}; +my $comment = { + commentId => "new", + bodyText => "New comment", + creationDate => WebGUI::DateTime->new( $session, time )->toDatabase, + userId => "3", +}; + +my $commentId = $photo->setComment( $comment ); + +$newVar = $photo->appendTemplateVarsCommentForm( $var, $photo->getComment( $commentId ) ); + +is ( $var, $newVar, "appendTemplateVarsCommentForm returns the same hashref it's given" ); +cmp_deeply( + $var, + superhashof( { + commentForm_start => all( + re( qr/]+name="func"[^>]+value="editCommentSave"[^>]+>/ ), + re( qr/]+name="commentId"[^>]+value="$commentId"[^>]+>/ ), + re( qr/]+name="creationDate"[^>]+value="$comment->{creationDate}"[^>]+>/ ), + re( qr/]+name="userId"[^>]+value="$comment->{userId}"[^>]+>/ ), + ), + commentForm_end => all( + re( qr{} ), + ), + commentForm_bodyText => all( + re( qr{]+>} ), + re( qr{TinyMCE}i ), + re( qr{$comment->{bodyText}} ), + ), + commentForm_submit => all( + re( qr/]+type="submit"[^>]+name="submit"[^>]+value="Save Comment"[^>]+>/ ), + ), + } ), + "appendTemplateVarsCommentForm returns the correct structure", +); #---------------------------------------------------------------------------- # Test www_editCommentSave page sanity checks diff --git a/t/Asset/File/GalleryFile/Photo/download.t b/t/Asset/File/GalleryFile/Photo/download.t index b69c03058..b67ada59e 100644 --- a/t/Asset/File/GalleryFile/Photo/download.t +++ b/t/Asset/File/GalleryFile/Photo/download.t @@ -19,6 +19,7 @@ use Scalar::Util qw( blessed ); use WebGUI::Test; use WebGUI::Session; use Test::More; +use Test::Deep; use WebGUI::Asset::File::GalleryFile::Photo; #---------------------------------------------------------------------------- @@ -35,6 +36,7 @@ my $gallery = $node->addChild({ className => "WebGUI::Asset::Wobject::Gallery", imageResolutions => "100\n200\n300", + groupIdView => 7, }); my $album = $gallery->addChild({ @@ -46,7 +48,7 @@ my $album skipAutoCommitWorkflows => 1, }); my $photo - = $gallery->addChild({ + = $album->addChild({ className => "WebGUI::Asset::File::GalleryFile::Photo", }, undef, @@ -59,12 +61,30 @@ $versionTags[-1]->commit; #---------------------------------------------------------------------------- # Tests -plan tests => 1; +plan tests => 3; + +#---------------------------------------------------------------------------- +# getResolutions returns an array reference of available resolutions +$photo->setFile( WebGUI::Test->getTestCollateralPath( "lamp.jpg" ) ); +cmp_deeply( + $photo->getResolutions, + bag( "100.jpg", "200.jpg", "300.jpg" ), + "getResolutions returns the correct array reference", +); + +#---------------------------------------------------------------------------- +# getDownloadFileUrl returns the URL to download the resolution +is( + $photo->getDownloadFileUrl("100"), + $photo->getStorageLocation->getUrl( "100.jpg" ), + "getDownloadFileUrl returns the URL to download the resolution", +); + +ok( + !eval{ $photo->getDownloadFileUrl("400"); 1 }, + "getDownloadFileUrl croaks if resolution doesn't exist", +); -TODO: { - local $TODO = 'Write some tests for download testing'; - ok(0, 'No tests yet'); -} #---------------------------------------------------------------------------- # Cleanup diff --git a/t/Asset/File/GalleryFile/Photo/editSave.t b/t/Asset/File/GalleryFile/Photo/editSave.t index 33613743c..a5224f9f1 100644 --- a/t/Asset/File/GalleryFile/Photo/editSave.t +++ b/t/Asset/File/GalleryFile/Photo/editSave.t @@ -19,7 +19,6 @@ use Scalar::Util qw( blessed ); use WebGUI::Test; use WebGUI::Session; use Test::More; -use WebGUI::Test::Maker::HTML; use WebGUI::Asset::File::GalleryFile::Photo; #---------------------------------------------------------------------------- @@ -33,10 +32,20 @@ $versionTags[-1]->set({name=>"Photo Test, add Gallery, Album and 1 Photo"}); $session->user( { userId => 3 } ); # Admins can do everything -my $maker = WebGUI::Test::Maker::HTML->new; +# Create a user for testing purposes +my $user = WebGUI::User->new( $session, "new" ); +$user->username( 'dufresne' ); +$user->addToGroups( ['3'] ); +my $identifier = 'ritahayworth'; +my $auth = WebGUI::Operation::Auth::getInstance( $session, $user->authMethod, $user->userId ); +$auth->saveParams( $user->userId, $user->authMethod, { + 'identifier' => Digest::MD5::md5_base64( $identifier ), +}); + my $gallery = $node->addChild({ className => "WebGUI::Asset::Wobject::Gallery", + groupIdAddFile => 3, # Admins }); my $album = $gallery->addChild({ @@ -61,67 +70,58 @@ $versionTags[-1]->commit; #---------------------------------------------------------------------------- # Tests -plan skip_all => "Tests are not working yet."; +plan skip_all => "Tests not working yet"; +#plan tests => 1; + +use_ok("Test::WWW::Mechanize"); +my $mech; #---------------------------------------------------------------------------- # Test permissions +$mech = Test::WWW::Mechanize->new; # Edit an existing photo -$maker->prepare({ - object => $photo, - method => "www_edit", - userId => "1", - test_privilege => "insufficient", -})->run; +$mech->get( $session->url->getSiteURL . $photo->getUrl("func=edit") ); +$mech->content_contains("permission denied"); + +$mech->get( $session->url->getSiteURL . $photo->getUrl("func=editSave") ); +$mech->content_contains("permission denied"); # Save a new photo -$maker->prepare({ - object => $photo, - method => "www_editSave", - userId => "1", - test_privilege => "insufficient", -})->run; +$mech->get( $session->url->getSiteURL . $album->getUrl("func=add;class=WebGUI::Asset::File::GalleryFile::Photo") ); +$mech->content_contains("permission denied"); + +$mech->get( $session->url->getSiteURL . $album->getUrl("func=editSave;assetId=new;class=WebGUI::Asset::File::GalleryFile::Photo") ); +$mech->content_contains("permission denied"); #---------------------------------------------------------------------------- # Test processPropertiesFromFormPost errors # TODO: This test should use i18n. # TODO: This error / test should occur in File, not Photo -$maker->prepare({ - object => $album, - method => "www_editSave", - formParams => { - assetId => "new", - className => "WebGUI::Asset::File::GalleryFile::Photo", +$mech = Test::WWW::Mechanize->new; +# Login mech object +$mech->get( $session->url->getSiteURL . '?op=auth;method=login;username=dufresne;identifier=ritahayworth' ); + +$mech->get_ok( $album->getUrl('func=add;class=WebGUI::Asset::File::GalleryFile::Photo') ); +$mech->submit_form( + with_fields => { + title => '', + newFile_file => '', }, - test_regex => [ - qr/You must select a file/, - qr/You must enter a title/, - ], -})->run; +); #---------------------------------------------------------------------------- # Test editSave success result # TODO: This test should use i18n -$maker->prepare({ - object => $album, - method => "www_editSave", - formParams => { - assetId => "new", - className => "WebGUI::Asset::File::GalleryFile::Photo", - }, - test_regex => [ - qr/awaiting approval and commit/, - ], -})->run; #---------------------------------------------------------------------------- # Cleanup END { - $gallery->purge; foreach my $versionTag (@versionTags) { $versionTag->rollback; } + $user->delete; } diff --git a/t/Asset/File/GalleryFile/Photo/makeShortcut.t b/t/Asset/File/GalleryFile/Photo/makeShortcut.t index 6f283294d..c41c7fdea 100644 --- a/t/Asset/File/GalleryFile/Photo/makeShortcut.t +++ b/t/Asset/File/GalleryFile/Photo/makeShortcut.t @@ -19,6 +19,7 @@ use Scalar::Util qw( blessed ); use WebGUI::Test; use WebGUI::Session; use Test::More; +use Test::Deep; use WebGUI::Test::Maker::HTML; use WebGUI::Asset::File::GalleryFile::Photo; @@ -48,7 +49,7 @@ $versionTag->commit; #---------------------------------------------------------------------------- # Tests -plan tests => 11; +plan tests => 10; #---------------------------------------------------------------------------- # makeShortcut argument checking @@ -76,7 +77,7 @@ ok( ); is( - blessed $shortcut, "WebGUI::Asset::Shortcut", + Scalar::Util::blessed($shortcut), "WebGUI::Asset::Shortcut", "Photo->makeShortcut returns a WebGUI::Shortcut asset", ); @@ -96,7 +97,7 @@ ok( ); is( - blessed $shortcut, "WebGUI::Asset::Shortcut", + Scalar::Util::blessed($shortcut), "WebGUI::Asset::Shortcut", "Photo->makeShortcut returns a WebGUI::Shortcut asset", ); @@ -105,26 +106,15 @@ is( "Photo->makeShortcut makes a shortcut to the correct asset", ); -SKIP: { - skip "Asset::Shortcut does not have a getShortcutOverrides method", 1; - is_deeply( - {$shortcut->getShortcutOverrides}, $overrides, - "Photo->makeShortcut makes a shortcut with the correct overrides", - ); -} +my %shortcutOverrides = $shortcut->getOverrides; +cmp_deeply( + { map({ $_ => $shortcutOverrides{overrides}->{$_}->{newValue} } keys %{ $overrides }) }, + $overrides, + "Photo->makeShortcut makes a shortcut with the correct overrides", +); #---------------------------------------------------------------------------- # www_makeShortcut is only available to those who can edit the photo -SKIP: { - skip "test_privilege has a bug", 1; - $maker->prepare({ - object => $photo, - method => "www_makeShortcut", - userId => 1, - test_privilege => "insufficient", - }); - $maker->run; -} #---------------------------------------------------------------------------- # www_makeShortcut diff --git a/t/Asset/File/GalleryFile/Photo/setFile.t b/t/Asset/File/GalleryFile/Photo/setFile.t index 30156d45a..4dc21c112 100644 --- a/t/Asset/File/GalleryFile/Photo/setFile.t +++ b/t/Asset/File/GalleryFile/Photo/setFile.t @@ -67,7 +67,7 @@ cmp_deeply( ); ok( - -e $storage->getPath($gallery->get('imageResolutions') . '.jpg'), + -e $storage->getPath($gallery->getImageResolutions->[0] . '.jpg'), "Generated resolution file exists on the filesystem", ); diff --git a/t/Asset/File/GalleryFile/Photo/view.t b/t/Asset/File/GalleryFile/Photo/view.t index 53b97959c..ee646289e 100644 --- a/t/Asset/File/GalleryFile/Photo/view.t +++ b/t/Asset/File/GalleryFile/Photo/view.t @@ -18,7 +18,7 @@ use Scalar::Util qw( blessed ); use WebGUI::Test; use WebGUI::Session; use Test::More; -use WebGUI::Test::Maker::HTML; +use Test::Deep; use WebGUI::Asset::File::GalleryFile::Photo; #---------------------------------------------------------------------------- @@ -27,10 +27,11 @@ my $session = WebGUI::Test->session; my $node = WebGUI::Asset->getImportNode($session); my $versionTag = WebGUI::VersionTag->getWorking($session); $versionTag->set({name=>"Photo Test"}); -my $maker = WebGUI::Test::Maker::HTML->new; my $gallery = $node->addChild({ className => "WebGUI::Asset::Wobject::Gallery", + groupIdAddComment => 7, # Everyone + groupIdAddFile => 2, # Registered Users }); my $album = $gallery->addChild({ @@ -44,6 +45,7 @@ my $album my $photo = $album->addChild({ className => "WebGUI::Asset::File::GalleryFile::Photo", + ownerUserId => 3, }, undef, undef, @@ -57,10 +59,47 @@ $photo->setFile( WebGUI::Test->getTestCollateralPath('page_title.jpg') ); # Tests plan tests => 1; -TODO: { - local $TODO = "Write some tests"; - ok(0, 'No tests here, move on'); +#---------------------------------------------------------------------------- +# Test getTemplateVars +$session->user( { userId => 1 } ); +my $testTemplateVars = { + %{ $photo->get }, + synopsis => '', # Synopsis is not undef, is changed to empty string + canComment => bool( 1 ), + canEdit => bool( 0 ), + ownerUsername => WebGUI::User->new( $session, 3 )->username, + url => $photo->getUrl, + url_addArchive => $album->getUrl('func=addArchive'), + url_delete => $photo->getUrl('func=delete'), + url_demote => $photo->getUrl('func=demote'), + url_edit => $photo->getUrl('func=edit'), + url_gallery => $gallery->getUrl, + url_makeShortcut => $photo->getUrl('func=makeShortcut'), + url_listFilesForOwner + => $gallery->getUrl('func=listFilesForUser;userId=3'), + url_promote => $photo->getUrl('func=promote'), + fileUrl => $photo->getFileUrl, + thumbnailUrl => $photo->getThumbnailUrl, + numberOfComments => scalar @{ $photo->getCommentIds }, + resolutions_loop => ignore(), # Tested elsewhere + exifLoop => ignore(), # Tested elsewhere + # Gallery stuff + url_search => $gallery->getUrl('func=search'), + url_listFilesForCurrentUser => $gallery->getUrl('func=listFilesForUser'), + +}; +# Ignore all EXIF tags +for my $tag ( keys %{ $photo->getExifData } ) { + $testTemplateVars->{ 'exif_' . $tag } = ignore(); } +# Add search vars +$gallery->appendTemplateVarsSearchForm( $testTemplateVars ); + +cmp_deeply( + $photo->getTemplateVars, + $testTemplateVars, + "getTemplateVars is correct and complete", +); #---------------------------------------------------------------------------- # Cleanup diff --git a/t/Asset/Wobject/Gallery/00base.t b/t/Asset/Wobject/Gallery/00base.t index 75f448262..d1f219edc 100644 --- a/t/Asset/Wobject/Gallery/00base.t +++ b/t/Asset/Wobject/Gallery/00base.t @@ -51,7 +51,13 @@ isa_ok( ); #---------------------------------------------------------------------------- -# Test deleting a album +# Test adding children to Gallery + +# Only GalleryAlbums may be added + + +#---------------------------------------------------------------------------- +# Test deleting a gallery my $properties = $gallery->get; $gallery->purge; diff --git a/t/Asset/Wobject/Gallery/permission.t b/t/Asset/Wobject/Gallery/permission.t new file mode 100644 index 000000000..1ae9a558f --- /dev/null +++ b/t/Asset/Wobject/Gallery/permission.t @@ -0,0 +1,83 @@ +#------------------------------------------------------------------- +# WebGUI is Copyright 2001-2007 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"; + +## The goal of this test is to test permissions inside Gallerys + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use WebGUI::Test::Maker::Permission; +use Test::More; + +#---------------------------------------------------------------------------- +# Init +my $session = WebGUI::Test->session; +my $node = WebGUI::Asset->getImportNode($session); +my $versionTag = WebGUI::VersionTag->getWorking($session); +$versionTag->set({name=>"Gallery Test"}); +my $maker = WebGUI::Test::Maker::Permission->new; +my $gallery; + +my $nonAdmin = WebGUI::User->new( $session, "new" ); + + +#---------------------------------------------------------------------------- +# Tests +# Plan is delayed until all tests are prepared + +#---------------------------------------------------------------------------- +my $gallery + = $node->addChild({ + className => 'WebGUI::Asset::Wobject::Gallery', + groupIdAddComment => '7', # Everyone + groupIdAddFile => '2', # Registered Users + groupIdEdit => '3', # Admins + groupIdView => '7', # Everyone + ownerUserId => '3', # Admin + }); + +$maker->prepare( + { + object => $gallery, + method => "canView", + pass => [ '1', '3', $nonAdmin->userId ], + }, + { + object => $gallery, + method => 'canEdit', + pass => [ '3' ], + fail => [ '1', $nonAdmin->userId ], + }, + { + object => $gallery, + method => 'canAddFile', + pass => [ '3', $nonAdmin->userId ], + fail => [ '1' ], + }, + { + object => $gallery, + method => 'canComment', + pass => [ '1', '3', $nonAdmin->userId ], + } +); + +plan tests => $maker->plan; + +$maker->run; + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback; +} diff --git a/t/Asset/Wobject/GalleryAlbum/addArchive.t b/t/Asset/Wobject/GalleryAlbum/addArchive.t index 5616f3cb3..0896309ca 100644 --- a/t/Asset/Wobject/GalleryAlbum/addArchive.t +++ b/t/Asset/Wobject/GalleryAlbum/addArchive.t @@ -52,7 +52,7 @@ $versionTag->commit; #---------------------------------------------------------------------------- # Tests -plan tests => 2; +plan tests => 5; #---------------------------------------------------------------------------- # Test the addArchive sub @@ -65,11 +65,30 @@ cmp_deeply( bag( "Aana1.jpg", "Aana2.jpg", "Aana3.jpg" ), ); +cmp_deeply( + [ map { $_->get("title") } @$images ], + bag( "Aana1", "Aana2", "Aana3" ), +); + +cmp_deeply( + [ map { $_->get("menuTitle") } @$images ], + bag( "Aana1", "Aana2", "Aana3" ), +); + +cmp_deeply( + [ map { $_->get("url") } @$images ], + bag( + $session->url->urlize( $album->getUrl . "/Aana1" ), + $session->url->urlize( $album->getUrl . "/Aana2" ), + $session->url->urlize( $album->getUrl . "/Aana3" ), + ), +); + #---------------------------------------------------------------------------- # Test the www_addArchive page #---------------------------------------------------------------------------- # Cleanup END { - $versionTag->rollback(); + $versionTag->rollback; } diff --git a/t/Asset/Wobject/GalleryAlbum/rss.t b/t/Asset/Wobject/GalleryAlbum/rss.t index 10523b6b7..cf5a97217 100644 --- a/t/Asset/Wobject/GalleryAlbum/rss.t +++ b/t/Asset/Wobject/GalleryAlbum/rss.t @@ -19,11 +19,11 @@ use Scalar::Util qw( blessed ); use WebGUI::Test; use WebGUI::Session; use Test::More; -use WebGUI::Test::Maker::HTML; +use Test::Deep; +use XML::Simple; #---------------------------------------------------------------------------- # Init -my $maker = WebGUI::Test::Maker::HTML->new; my $session = WebGUI::Test->session; my $node = WebGUI::Asset->getImportNode($session); my $versionTag = WebGUI::VersionTag->getWorking($session); @@ -41,6 +41,7 @@ my $album = $gallery->addChild({ className => "WebGUI::Asset::Wobject::GalleryAlbum", ownerUserId => "3", # Admin + description => "An RSS Description", }, undef, undef, @@ -53,6 +54,7 @@ for my $i ( 0 .. 5 ) { = $album->addChild({ className => "WebGUI::Asset::File::GalleryFile::Photo", filename => "$i.jpg", + synopsis => "This is a description for $i.jpg", }, undef, undef, @@ -65,15 +67,38 @@ $versionTag->commit; #---------------------------------------------------------------------------- # Tests -plan tests => 1; +plan tests => 2; + +use_ok("Test::WWW::Mechanize"); +my $mech; #---------------------------------------------------------------------------- # Test www_viewRss - -TODO: { - local $TODO = "Write some tests"; - ok(0, "No tests here"); -} +$mech = Test::WWW::Mechanize->new; +my $url = $session->url->getSiteURL . $session->url->makeAbsolute( $album->getUrl('func=viewRss') ); +$mech->get( $url ); +cmp_deeply( + XMLin( $mech->content ), + { + version => '2.0', + channel => { + link => $session->url->getSiteURL . $album->getUrl, + description => $album->get("description"), + title => $album->get("title"), + item => bag( + map { + superhashof({ + link => $session->url->getSiteURL . $_->getUrl, + title => $_->get("title"), + pubDate => $session->datetime->epochToMail( $_->get("revisionDate") ), + description => $_->get("synopsis"), + }) + } @photos + ), + }, + }, + "RSS Datastructure is complete and correct", +); #---------------------------------------------------------------------------- # Cleanup diff --git a/t/Macro/International.t b/t/Macro/International.t index a057b06ea..da117740d 100644 --- a/t/Macro/International.t +++ b/t/Macro/International.t @@ -21,26 +21,31 @@ use Test::More; # increment this value for each test you create my $session = WebGUI::Test->session; my @testSets = ( - { - input => ['none', 'Asset'], - output => q!None!, - comment => q|explicit namespace|, - }, - { - input => ['change url', 'Asset'], - output => q!Change URL!, - comment => q|space in label|, - }, - { - input => ['webgui', 'WebGUI'], - output => q!WebGUI!, - comment => q|explicit namespace #2|, - }, - { - input => ['webgui', ''], - output => q!WebGUI!, - comment => q|default namespace|, - }, + { + input => ['none', 'Asset'], + output => q!None!, + comment => q|explicit namespace|, + }, + { + input => ['change url', 'Asset'], + output => q!Change URL!, + comment => q|space in label|, + }, + { + input => ['webgui', 'WebGUI'], + output => q!WebGUI!, + comment => q|explicit namespace #2|, + }, + { + input => ['webgui', ''], + output => q!WebGUI!, + comment => q|default namespace|, + }, + { + input => ['template listFilesForUser title', 'Asset_Gallery', 'plainblack'], + output => q{plainblack's Gallery}, + comment => q{Third and more arguments are passed to sprintf()}, + }, ); my $numTests = scalar @testSets; diff --git a/t/Asset/Wobject/Gallery/Utility/addAlbum.t b/t/Utility/Gallery/addAlbum.t similarity index 98% rename from t/Asset/Wobject/Gallery/Utility/addAlbum.t rename to t/Utility/Gallery/addAlbum.t index 6e005344b..194d520f7 100644 --- a/t/Asset/Wobject/Gallery/Utility/addAlbum.t +++ b/t/Utility/Gallery/addAlbum.t @@ -15,13 +15,12 @@ use strict; use FindBin; -use lib "$FindBin::Bin/../../../../../lib"; -use lib "$FindBin::Bin/../../../../lib"; +use lib "$FindBin::Bin/../../lib"; use Test::More; use Test::Deep; +use WebGUI::Test; use WebGUI::Asset; use WebGUI::Session; -use WebGUI::Test; #---------------------------------------------------------------------------- # Init @@ -132,7 +131,7 @@ plan tests => 10 #---------------------------------------------------------------------------- # Test use -my $utility = 'WebGUI::Asset::Wobject::Gallery::Utility'; +my $utility = 'WebGUI::Utility::Gallery'; use_ok($utility); #----------------------------------------------------------------------------