diff --git a/docs/changelog/7.x.x.txt b/docs/changelog/7.x.x.txt index f5391111b..cead3ae4a 100644 --- a/docs/changelog/7.x.x.txt +++ b/docs/changelog/7.x.x.txt @@ -1,5 +1,4 @@ 7.5.0 - - rfe: Default log level to ERROR - rfe: Search Asset returns URLs - Added link to return to inbox from message in inbox message template (Diona Kidd, Knowmad Technologies) - fix: Cannot delete private message (Diona Kidd, Knowmad Technologies) diff --git a/docs/upgrades/_upgrade.skeleton b/docs/upgrades/_upgrade.skeleton index 16973c96d..7821157a7 100644 --- a/docs/upgrades/_upgrade.skeleton +++ b/docs/upgrades/_upgrade.skeleton @@ -12,6 +12,8 @@ use lib "../../lib"; use strict; use Getopt::Long; use WebGUI::Session; +use WebGUI::Storage; +use WebGUI::Asset; my $toVersion = "0.0.0"; # make this match what version you're going to @@ -33,8 +35,71 @@ finish($session); # this line required #} +# --------------- DO NOT EDIT BELOW THIS LINE -------------------------------- -# ---- DO NOT EDIT BELOW THIS LINE ---- +#---------------------------------------------------------------------------- +# Add a package to the import node +sub addPackage { + my $session = shift; + my $file = shift; + + # Make a storage location for the package + my $storage = WebGUI::Storage->createTemp( $session ); + $storage->addFileFromFilesystem( $file ); + + # Import the package into the import node + WebGUI::Asset->getImportNode($session)->importPackage( $storage ); + + # Make the package not a package anymore +} + +#---------------------------------------------------------------------------- +# Add a template from a file +sub addTemplate { + my $session = shift; + my $file = shift; + my $newFolder = shift; + + open(FILE,"<",$file); + my $first = 1; + my $create = 0; + my $head = 0; + my %properties = (className=>"WebGUI::Asset::Template"); + while (my $line = ) { + if ($first) { + $line =~ m/^\#(.*)$/; + $properties{id} = $1; + $first = 0; + } + elsif ($line =~ m/^\#create$/) { + $create = 1; + } + elsif ($line =~ m/^\#(.*):(.*)$/) { + $properties{$1} = $2; + } + elsif ($line =~ m/^~~~$/) { + $head = 1; + } + elsif ($head) { + $properties{headBlock} .= $line; + } + else { + $properties{template} .= $line; + } + } + close(FILE); + if ($create) { + $$newFolder = createNewTemplatesFolder(WebGUI::Asset->getImportNode($session)) + unless (defined $$newFolder); + my $template = $$newFolder->addChild(\%properties, $properties{id}); + } + else { + my $template = WebGUI::Asset->new($session,$properties{id}, "WebGUI::Asset::Template"); + if (defined $template) { + my $newRevision = $template->addRevision(\%properties); + } + } +} #------------------------------------------------- sub start { @@ -63,49 +128,24 @@ sub finish { #------------------------------------------------- sub updateTemplates { - my $session = shift; - return undef unless (-d "templates-".$toVersion); - print "\tUpdating templates.\n" unless ($quiet); - opendir(DIR,"templates-".$toVersion); - my @files = readdir(DIR); - closedir(DIR); - my $importNode = WebGUI::Asset->getImportNode($session); - my $newFolder = undef; - foreach my $file (@files) { - next unless ($file =~ /\.tmpl$/); - open(FILE,""WebGUI::Asset::Template"); - while (my $line = ) { - if ($first) { - $line =~ m/^\#(.*)$/; - $properties{id} = $1; - $first = 0; - } elsif ($line =~ m/^\#create$/) { - $create = 1; - } elsif ($line =~ m/^\#(.*):\s*(.*)$/) { - $properties{$1} = $2; - } elsif ($line =~ m/^~~~$/) { - $head = 1; - } elsif ($head) { - $properties{headBlock} .= $line; - } else { - $properties{template} .= $line; - } - } - close(FILE); - if ($create) { - $newFolder = createNewTemplatesFolder($importNode) unless (defined $newFolder); - my $template = $newFolder->addChild(\%properties, $properties{id}); - } else { - my $template = WebGUI::Asset->new($session,$properties{id}, "WebGUI::Asset::Template"); - if (defined $template) { - my $newRevision = $template->addRevision(\%properties); - } - } - } + my $session = shift; + return undef unless (-d "templates-".$toVersion); + print "\tUpdating templates.\n" unless ($quiet); + opendir(DIR,"templates-".$toVersion); + my @files = readdir(DIR); + closedir(DIR); + my $newFolder = undef; + foreach my $file (@files) { + next unless ($file =~ /\.(tmpl|wgpkg)$/); + my $type = $1; + $file = "templates-" . $toVersion . "/" . $file; + if ($type eq "tmpl") { + addTemplate( $session, $file, \$newFolder ); + } + elsif ($type eq "wgpkg") { + addPackage( $session, $file ); + } + } } #------------------------------------------------- diff --git a/docs/upgrades/templates-7.5.0/root_import_gallery-templates-2.wgpkg b/docs/upgrades/templates-7.5.0/root_import_gallery-templates-2.wgpkg new file mode 100644 index 000000000..8b71ce9d9 Binary files /dev/null and b/docs/upgrades/templates-7.5.0/root_import_gallery-templates-2.wgpkg differ diff --git a/docs/upgrades/upgrade_7.4.18-7.5.0.pl b/docs/upgrades/upgrade_7.4.18-7.5.0.pl index 5bc10a811..7b3cd5bd6 100644 --- a/docs/upgrades/upgrade_7.4.18-7.5.0.pl +++ b/docs/upgrades/upgrade_7.4.18-7.5.0.pl @@ -25,6 +25,9 @@ addUrlAndContentHandlers($session); addFriendsNetwork($session); addSearchWithContainers($session); addGroupToEditPost($session); +installGalleryAsset($session); +installGalleryAlbumAsset($session); +installPhotoAsset($session); finish($session); # this line required @@ -143,8 +146,189 @@ EOSQL print "OK\n" unless $quiet; } +#---------------------------------------------------------------------------- +# Install the GalleryAlbum asset +sub installGalleryAlbumAsset { + my $session = shift; + print "\tInstalling GalleryAlbum asset..." unless $quiet; + + $session->db->write(<<'ENDSQL'); +CREATE TABLE IF NOT EXISTS GalleryAlbum ( + assetId VARCHAR(22) BINARY NOT NULL, + revisionDate BIGINT NOT NULL, + allowComments INT, + assetIdThumbnail VARCHAR(22) BINARY, + othersCanAdd INT, + PRIMARY KEY (assetId, revisionDate) +) +ENDSQL -# ---- DO NOT EDIT BELOW THIS LINE ---- + print "DONE!\n" unless $quiet; +} + +#---------------------------------------------------------------------------- +# Install the Gallery asset +sub installGalleryAsset { + my $session = shift; + print "\tInstalling Gallery asset..." unless $quiet; + + $session->db->write(<<'ENDSQL'); +CREATE TABLE IF NOT EXISTS Gallery ( + assetId VARCHAR(22) BINARY NOT NULL, + revisionDate BIGINT NOT NULL, + groupIdAddComment VARCHAR(22) BINARY, + groupIdAddFile VARCHAR(22) BINARY, + groupIdModerator VARCHAR(22) BINARY, + imageResolutions TEXT, + imageViewSize INT, + imageThumbnailSize INT, + maxSpacePerUser VARCHAR(20), + richEditIdComment VARCHAR(22) BINARY, + templateIdAddArchive VARCHAR(22) BINARY, + templateIdDeleteAlbum VARCHAR(22) BINARY, + templateIdDeleteFile VARCHAR(22) BINARY, + templateIdEditAlbum VARCHAR(22) BINARY, + templateIdEditFile VARCHAR(22) BINARY, + templateIdListAlbums VARCHAR(22) BINARY, + templateIdListAlbumsRss VARCHAR(22) BINARY, + templateIdListFilesForUser VARCHAR(22) BINARY, + templateIdListFilesForUserRss VARCHAR(22) BINARY, + templateIdMakeShortcut VARCHAR(22) BINARY, + templateIdSearch VARCHAR(22) BINARY, + templateIdViewSlideshow VARCHAR(22) BINARY, + templateIdViewThumbnails VARCHAR(22) BINARY, + templateIdViewAlbum VARCHAR(22) BINARY, + templateIdViewAlbumRss VARCHAR(22) BINARY, + templateIdViewFile VARCHAR(22) BINARY, + viewAlbumAssetId VARCHAR(22), + viewDefault ENUM('album','list'), + viewListOrderBy VARCHAR(40), + viewListOrderDirection ENUM('ASC','DESC'), + workflowIdCommit VARCHAR(22) BINARY, + PRIMARY KEY (assetId, revisionDate) +) +ENDSQL + + $session->config->addToArray("assets","WebGUI::Asset::Wobject::Gallery"); + + print "DONE!\n" unless $quiet; +} + +#---------------------------------------------------------------------------- +# Install the photo asset +sub installPhotoAsset { + my $session = shift; + print "\tInstalling Photo asset..." unless $quiet; + + # Photo Asset + $session->db->write(<<'ENDSQL'); +CREATE TABLE IF NOT EXISTS Photo ( + assetId VARCHAR(22) BINARY NOT NULL, + revisionDate BIGINT NOT NULL, + exifData LONGTEXT, + friendsOnly INT, + location VARCHAR(255), + rating INT, + userDefined1 TEXT, + userDefined2 TEXT, + userDefined3 TEXT, + userDefined4 TEXT, + userDefined5 TEXT, + PRIMARY KEY (assetId, revisionDate) +) +ENDSQL + + $session->db->write(<<'ENDSQL'); +CREATE TABLE IF NOT EXISTS Photo_comment ( + assetId VARCHAR(22) BINARY NOT NULL, + commentId VARCHAR(22) BINARY NOT NULL, + userId VARCHAR(22) BINARY, + visitorIp VARCHAR(255), + creationDate DATETIME, + bodyText LONGTEXT, + INDEX (commentId), + PRIMARY KEY (assetId, commentId) +) +ENDSQL + + $session->db->write(<<'ENDSQL'); +CREATE TABLE IF NOT EXISTS Photo_rating ( + assetId VARCHAR(22) BINARY NOT NULL, + userId VARCHAR(22) BINARY, + visitorIp VARCHAR(255), + rating INT, + INDEX (assetId) +) +ENDSQL + + print "DONE!\n" unless $quiet; +} + +# --------------- DO NOT EDIT BELOW THIS LINE -------------------------------- + +#---------------------------------------------------------------------------- +# Add a package to the import node +sub addPackage { + my $session = shift; + my $file = shift; + + # Make a storage location for the package + my $storage = WebGUI::Storage->createTemp( $session ); + $storage->addFileFromFilesystem( $file ); + + # Import the package into the import node + WebGUI::Asset->getImportNode($session)->importPackage( $storage ); + + # Make the package not a package anymore +} + +#---------------------------------------------------------------------------- +# Add a template from a file +sub addTemplate { + my $session = shift; + my $file = shift; + my $newFolder = shift; + + open(FILE,"<",$file); + my $first = 1; + my $create = 0; + my $head = 0; + my %properties = (className=>"WebGUI::Asset::Template"); + while (my $line = ) { + if ($first) { + $line =~ m/^\#(.*)$/; + $properties{id} = $1; + $first = 0; + } + elsif ($line =~ m/^\#create$/) { + $create = 1; + } + elsif ($line =~ m/^\#(.*):(.*)$/) { + $properties{$1} = $2; + } + elsif ($line =~ m/^~~~$/) { + $head = 1; + } + elsif ($head) { + $properties{headBlock} .= $line; + } + else { + $properties{template} .= $line; + } + } + close(FILE); + if ($create) { + $$newFolder = createNewTemplatesFolder(WebGUI::Asset->getImportNode($session)) + unless (defined $$newFolder); + my $template = $$newFolder->addChild(\%properties, $properties{id}); + } + else { + my $template = WebGUI::Asset->new($session,$properties{id}, "WebGUI::Asset::Template"); + if (defined $template) { + my $newRevision = $template->addRevision(\%properties); + } + } +} #------------------------------------------------- sub start { @@ -173,49 +357,24 @@ sub finish { #------------------------------------------------- sub updateTemplates { - my $session = shift; - return undef unless (-d "templates-".$toVersion); - print "\tUpdating templates.\n" unless ($quiet); - opendir(DIR,"templates-".$toVersion); - my @files = readdir(DIR); - closedir(DIR); - my $importNode = WebGUI::Asset->getImportNode($session); - my $newFolder = undef; - foreach my $file (@files) { - next unless ($file =~ /\.tmpl$/); - open(FILE,""WebGUI::Asset::Template"); - while (my $line = ) { - if ($first) { - $line =~ m/^\#(.*)$/; - $properties{id} = $1; - $first = 0; - } elsif ($line =~ m/^\#create$/) { - $create = 1; - } elsif ($line =~ m/^\#(.*):(.*)$/) { - $properties{$1} = $2; - } elsif ($line =~ m/^~~~$/) { - $head = 1; - } elsif ($head) { - $properties{headBlock} .= $line; - } else { - $properties{template} .= $line; - } - } - close(FILE); - if ($create) { - $newFolder = createNewTemplatesFolder($importNode) unless (defined $newFolder); - my $template = $newFolder->addChild(\%properties, $properties{id}); - } else { - my $template = WebGUI::Asset->new($session,$properties{id}, "WebGUI::Asset::Template"); - if (defined $template) { - my $newRevision = $template->addRevision(\%properties); - } - } - } + my $session = shift; + return undef unless (-d "templates-".$toVersion); + print "\tUpdating templates.\n" unless ($quiet); + opendir(DIR,"templates-".$toVersion); + my @files = readdir(DIR); + closedir(DIR); + my $newFolder = undef; + foreach my $file (@files) { + next unless ($file =~ /\.(tmpl|wgpkg)$/); + my $type = $1; + $file = "templates-" . $toVersion . "/" . $file; + if ($type eq "tmpl") { + addTemplate( $session, $file, \$newFolder ); + } + elsif ($type eq "wgpkg") { + addPackage( $session, $file ); + } + } } #------------------------------------------------- diff --git a/etc/log.conf.original b/etc/log.conf.original index dfcd9c3cb..7ac7bdb59 100644 --- a/etc/log.conf.original +++ b/etc/log.conf.original @@ -4,7 +4,7 @@ # system is capable of. To unleash the full power read the config file manual # http://log4perl.sourceforge.net/releases/Log-Log4perl/docs/html/Log/Log4perl/Config.html -log4perl.logger = ERROR, mainlog +log4perl.logger = WARN, mainlog log4perl.appender.mainlog = Log::Log4perl::Appender::File log4perl.appender.mainlog.filename = /var/log/webgui.log log4perl.appender.mainlog.layout = PatternLayout diff --git a/lib/WebGUI/Asset.pm b/lib/WebGUI/Asset.pm index a594a1a0f..c0076d2a4 100644 --- a/lib/WebGUI/Asset.pm +++ b/lib/WebGUI/Asset.pm @@ -14,6 +14,8 @@ package WebGUI::Asset; =cut +use Carp qw( croak confess ); + use WebGUI::AssetBranch; use WebGUI::AssetClipboard; use WebGUI::AssetExportHtml; @@ -1337,12 +1339,12 @@ Loads an asset module if it's not already in memory. This is a class method. Ret sub loadModule { my ($class, $session, $className) = @_; - (my $module = $className . '.pm') =~ s{::|'}{/}g; + (my $module = $className . '.pm') =~ s{::|'}{/}g; if (eval { require $module; 1 }) { return $className; } - $session->errorHandler->error("Couldn't compile asset package: ".$className.". Root cause: ".$@); - return; + $session->errorHandler->error("Couldn't compile asset package: ".$className.". Root cause: ".$@); + return; } #------------------------------------------------------------------- @@ -1753,6 +1755,7 @@ A specific revision date for the asset to retrieve. If not specified, the most r sub newByDynamicClass { my $class = shift; my $session = shift; + confess "newByDynamicClass requires WebGUI::Session" unless $session; my $assetId = shift; my $revisionDate = shift; return unless defined $assetId; @@ -1896,6 +1899,7 @@ sub processPropertiesFromFormPost { $self->session->db->beginTransaction; $self->update(\%data); $self->session->db->commit; + return; } @@ -1984,6 +1988,16 @@ sub publish { $cache->deleteChunk(["asset",$id]); } $self->{_properties}{state} = "published"; + + # Also publish any shortcuts to this asset that are in the trash + my $shortcuts + = WebGUI::Asset::Shortcut->getShortcutsForAssetId($self->session, $self->getId, { + returnObjects => 1, + statesToInclude => ['trash','trash-limbo'], + }); + for my $shortcut ( @$shortcuts ) { + $shortcut->publish; + } } @@ -2073,6 +2087,9 @@ Updates the properties of an existing revision. If you want to create a new revi Hash reference of properties and values to set. +NOTE: C is a special property that uses the WebGUI::Keyword API +to set the keywords for this asset. + =cut sub update { diff --git a/lib/WebGUI/Asset/File.pm b/lib/WebGUI/Asset/File.pm index 6c0537ae3..0419655fb 100644 --- a/lib/WebGUI/Asset/File.pm +++ b/lib/WebGUI/Asset/File.pm @@ -16,6 +16,7 @@ package WebGUI::Asset::File; use strict; use base 'WebGUI::Asset'; +use Carp; use WebGUI::Cache; use WebGUI::Storage; use WebGUI::SQL; @@ -54,17 +55,17 @@ Override the default method in order to deal with attachments. =cut sub addRevision { - my $self = shift; - my $properties = shift; - - if ($self->get("storageId") ne "") { - my $newStorage = WebGUI::Storage->get($self->session,$self->get("storageId"))->copy; - $properties->{storageId} = $newStorage->getId; - } - - my $newSelf = $self->SUPER::addRevision($properties); - - return $newSelf; + my $self = shift; + my $properties = shift; + + if ($self->get("storageId") ne "") { + my $newStorage = $self->getStorageClass->get($self->session,$self->get("storageId"))->copy; + $properties->{storageId} = $newStorage->getId; + } + + my $newSelf = $self->SUPER::addRevision($properties, @_); + + return $newSelf; } #------------------------------------------------------------------- @@ -172,23 +173,50 @@ Returns the TabForm object that will be used in generating the edit page for thi =cut sub getEditForm { - my $self = shift; - my $tabform = $self->SUPER::getEditForm(); - my $i18n = WebGUI::International->new($self->session, 'Asset_File'); - if ($self->get("filename") ne "") { - $tabform->getTab("properties")->readOnly( - -label=>$i18n->get('current file'), - -hoverHelp=>$i18n->get('current file description', 'Asset_File'), - -value=>'

'.$self->get( '.$self->get("filename").'

' - ); + my $self = shift; + my $tabform = $self->SUPER::getEditForm(); + my $i18n = WebGUI::International->new($self->session, 'Asset_File'); - } - $tabform->getTab("properties")->file( - -name => 'newFile', - -label => $i18n->get('new file'), - -hoverHelp => $i18n->get('new file description'), - ); - return $tabform; + $tabform->getTab("properties")->raw( + ''.$i18n->get('new file').'' + . $self->getEditFormUploadControl + . '' + ); + + return $tabform; +} + +#---------------------------------------------------------------------------- + +=head2 getEditFormUploadControl + +Returns the HTML to render the upload box and link to delete the existing +file, if necessary. + +=cut + +sub getEditFormUploadControl { + my $self = shift; + my $session = $self->session; + my $i18n = WebGUI::International->new($session, 'Asset_File'); + my $html = ''; + + if ($self->get("filename") ne "") { + $html .= WebGUI::Form::readOnly( $session, { + label => $i18n->get('current file'), + hoverHelp => $i18n->get('current file description', 'Asset_File'), + value => '

'.$self->get( '.$self->get("filename").'

' + }); + } + + # Control to upload a new file + $html .= WebGUI::Form::file( $session, { + name => 'newFile', + label => $i18n->get('new file'), + hoverHelp => $i18n->get('new file description'), + }); + + return $html; } @@ -199,10 +227,10 @@ sub getFileUrl { return $self->getStorageLocation->getUrl($self->get("filename")); } - #------------------------------------------------------------------- sub getFileIconUrl { my $self = shift; + return unless $self->get("filename"); ## Why do I have to do this when creating new Files? return $self->getStorageLocation->getFileIconUrl($self->get("filename")); } @@ -221,20 +249,32 @@ sub getIcon { } +#---------------------------------------------------------------------------- + +=head2 getStorageClass + +Get the full classname of the WebGUI::Storage we should use for this asset. + +=cut + +sub getStorageClass { + return 'WebGUI::Storage'; +} + #------------------------------------------------------------------- =head2 getStorageFromPost -We have to wrap this operation because WebGUI::Asset::File::Image calls SUPER processPropertiesFormFormPost, -which gives it the wrong type of Storage object. +Get the storage location created by the form post. =cut sub getStorageFromPost { - my $self = shift; + my $self = shift; my $storageId = shift; my $fileStorageId = WebGUI::Form::File->new($self->session, {name => 'newFile', value=>$storageId })->getValueFromPost; - return WebGUI::Storage->get($self->session, $fileStorageId); + $self->session->errorHandler->info( "File Storage Id: $fileStorageId" ); + return $self->getStorageClass->get($self->session, $fileStorageId); } @@ -283,41 +323,21 @@ sub prepareView { #------------------------------------------------------------------- sub processPropertiesFromFormPost { - my $self = shift; - my $session = $self->session; - $self->SUPER::processPropertiesFromFormPost; - - #Get the storage location out of memory. If you call getStorageLocation you risk creating another one. - my $storageLocation = $self->{_storageLocation}; - my $storageId = undef; - $storageId = $storageLocation->getId if(defined $storageLocation); - - #Now remove the storage location to prevent wierd caching stuff. - delete $self->{_storageLocation}; - - #Clear the storage location if a file was uploaded. - if($session->form->get("newFile_file") ne "") { - $storageLocation->clear(); + my $self = shift; + my $session = $self->session; + + my $errors = $self->SUPER::processPropertiesFromFormPost; + return $errors if $errors; + + if (my $storageId = $session->form->get('newFile','File')) { + $session->errorHandler->info("Got a new file for asset " . $self->getId); + my $storage = $self->getStorageClass->get( $session, $storageId); + my $filePath = $storage->getPath( $storage->getFiles->[0] ); + $self->setFile( $filePath ); + $storage->delete; } - #Pass in the storage Id to prevent another one from being created. - my $storage = $self->getStorageFromPost($storageId); - - if (defined $storage) { - my $filename = $storage->getFiles()->[0]; - - if (defined $filename) { - my %data; - $data{filename} = $filename; - $data{storageId} = $storage->getId; - $data{title} = $filename unless ($session->form->process("title")); - $data{menuTitle} = $filename unless ($session->form->process("menuTitle")); - $data{url} = $self->getParent->get('url').'/'.$filename unless ($session->form->process("url")); - $self->setStorageLocation($storage); - $self->update(\%data); - } - } - $self->applyConstraints; + return; } @@ -327,7 +347,7 @@ sub purge { my $self = shift; my $sth = $self->session->db->read("select storageId from FileAsset where assetId=".$self->session->db->quote($self->getId)); while (my ($storageId) = $sth->array) { - WebGUI::Storage->get($self->session,$storageId)->delete; + $self->getStorageClass->get($self->session,$storageId)->delete; } $sth->finish; return $self->SUPER::purge; @@ -355,34 +375,68 @@ sub purgeRevision { return $self->SUPER::purgeRevision; } +#---------------------------------------------------------------------------- + +=head2 setFile ( filename ) + +Set the file being handled by this storage location with a file from the +system. + +=cut + +sub setFile { + my $self = shift; + my $filename = shift; + my $storage = $self->getStorageLocation; + + # Clear the old file if any + $storage->clear; + + $storage->addFileFromFilesystem($filename) + || croak "Couldn't setFile: " . join(", ",@{ $storage->getErrors }); + # NOTE: We should not croak here, the WebGUI::Storage should croak for us. + + $self->updatePropertiesFromStorage; + $self->applyConstraints; +} + #------------------------------------------------------------------- + +=head2 setSize ( fileSize ) + +Set the size of this asset by including all the files in its storage +location. C is an integer of additional bytes to include in +the asset size. + +=cut + sub setSize { - my $self = shift; - my $fileSize = shift || 0; - my $storage = $self->getStorageLocation; - if (defined $storage) { - foreach my $file (@{$storage->getFiles}) { - $fileSize += $storage->getFileSize($file); - } - } - return $self->SUPER::setSize($fileSize); + my $self = shift; + my $fileSize = shift || 0; + my $storage = $self->getStorageLocation; + if (defined $storage) { + foreach my $file (@{$storage->getFiles}) { + $fileSize += $storage->getFileSize($file); + } + } + return $self->SUPER::setSize($fileSize); } #------------------------------------------------------------------- sub setStorageLocation { - my $self = shift; + my $self = shift; my $storage = shift; - if (defined $storage) { + if (defined $storage) { $self->{_storageLocation} = $storage; - } - elsif ($self->get("storageId") eq "") { - $self->{_storageLocation} = WebGUI::Storage->create($self->session); - $self->update({storageId=>$self->{_storageLocation}->getId}); - } + } + elsif ($self->get("storageId") eq "") { + $self->{_storageLocation} = $self->getStorageClass->create($self->session); + $self->update({storageId=>$self->{_storageLocation}->getId}); + } else { - $self->{_storageLocation} = WebGUI::Storage->get($self->session,$self->get("storageId")); - } + $self->{_storageLocation} = $self->getStorageClass->get($self->session,$self->get("storageId")); + } } #------------------------------------------------------------------- @@ -411,6 +465,27 @@ sub update { } } +#---------------------------------------------------------------------------- + +=head2 updatePropertiesFromStorage ( ) + +Updates the asset properties from the file tracked by this asset. Should be +called every time the file is changed to ensure the correct filename is +in the asset properties. + +=cut + +sub updatePropertiesFromStorage { + my $self = shift; + my $storage = $self->getStorageLocation; + my $filename = $storage->getFiles->[0]; + $self->session->errorHandler->info("Updating file asset filename to $filename"); + $self->update({ + filename => $filename, + }); + return; +} + #------------------------------------------------------------------- sub view { my $self = shift; diff --git a/lib/WebGUI/Asset/File/Image.pm b/lib/WebGUI/Asset/File/Image.pm index 2db369014..ff7baea00 100644 --- a/lib/WebGUI/Asset/File/Image.pm +++ b/lib/WebGUI/Asset/File/Image.pm @@ -15,16 +15,12 @@ package WebGUI::Asset::File::Image; =cut use strict; -use WebGUI::Asset::File; +use base 'WebGUI::Asset::File'; use WebGUI::Storage::Image; use WebGUI::HTMLForm; use WebGUI::Utility; use WebGUI::Form::Image; - -our @ISA = qw(WebGUI::Asset::File); - - =head1 NAME Package WebGUI::Asset::File::Image @@ -98,27 +94,27 @@ A hash reference passed in from a subclass definition. =cut sub definition { - my $class = shift; - my $session = shift; - my $definition = shift; - my $i18n = WebGUI::International->new($session,"Asset_Image"); - push(@{$definition}, { - assetName=>$i18n->get('assetName'), - tableName=>'ImageAsset', - className=>'WebGUI::Asset::File::Image', - icon=>'image.gif', - properties=>{ - thumbnailSize=>{ - fieldType=>'integer', - defaultValue=>$session->setting->get("thumbnailSize") - }, - parameters=>{ - fieldType=>'textarea', - defaultValue=>'style="border-style:none;"' - } - } - }); - return $class->SUPER::definition($session,$definition); + my $class = shift; + my $session = shift; + my $definition = shift; + my $i18n = WebGUI::International->new($session,"Asset_Image"); + push @{$definition}, { + assetName => $i18n->get('assetName'), + tableName => 'ImageAsset', + className => 'WebGUI::Asset::File::Image', + icon => 'image.gif', + properties => { + thumbnailSize => { + fieldType => 'integer', + defaultValue => $session->setting->get("thumbnailSize"), + }, + parameters => { + fieldType => 'textarea', + defaultValue => 'style="border-style:none;"', + }, + }, + }; + return $class->SUPER::definition($session,$definition); } @@ -184,37 +180,16 @@ sub getEditForm { return $tabform; } +#---------------------------------------------------------------------------- +=head2 getStorageClass -#------------------------------------------------------------------- - -=head2 getStorageFromPost - -Sub class this method from WebGUI::Asset::File so the storage object is the correct type. +Returns the class name of the WebGUI::Storage we should use for this asset. =cut -sub getStorageFromPost { - my $self = shift; - my $storageId = shift; - my $fileStorageId = WebGUI::Form::Image->new($self->session, {name => 'newFile', value=>$storageId })->getValueFromPost; - return WebGUI::Storage::Image->get($self->session, $fileStorageId); -} - - -#------------------------------------------------------------------- - -sub getStorageLocation { - my $self = shift; - unless (exists $self->{_storageLocation}) { - if ($self->get("storageId") eq "") { - $self->{_storageLocation} = WebGUI::Storage::Image->create($self->session); - $self->update({storageId=>$self->{_storageLocation}->getId}); - } else { - $self->{_storageLocation} = WebGUI::Storage::Image->get($self->session,$self->get("storageId")); - } - } - return $self->{_storageLocation}; +sub getStorageClass { + return 'WebGUI::Storage::Image'; } #------------------------------------------------------------------- @@ -253,31 +228,6 @@ sub prepareView { $self->{_viewTemplate} = $template; } - -#------------------------------------------------------------------- -sub processPropertiesFromFormPost { - my $self = shift; - $self->SUPER::processPropertiesFromFormPost; - $self->applyConstraints; -} - -#------------------------------------------------------------------- - -sub setStorageLocation { - my $self = shift; - my $storage = shift; - if (defined $storage) { - $self->{_storageLocation} = $storage; - } - elsif ($self->get("storageId") eq "") { - $self->{_storageLocation} = WebGUI::Storage::Image->create($self->session); - $self->update({storageId=>$self->{_storageLocation}->getId}); - } - else { - $self->{_storageLocation} = WebGUI::Storage::Image->get($self->session,$self->get("storageId")); - } -} - #------------------------------------------------------------------- sub view { my $self = shift; @@ -297,6 +247,20 @@ sub view { return $out; } +#---------------------------------------------------------------------------- + +=head2 setFile ( filename ) + +Extend the superclass setFile to automatically generate thumbnails. + +=cut + +sub setFile { + my $self = shift; + $self->SUPER::setFile(@_); + $self->generateThumbnail; +} + #------------------------------------------------------------------- sub www_edit { my $self = shift; diff --git a/lib/WebGUI/Asset/File/Image/Photo.pm b/lib/WebGUI/Asset/File/Image/Photo.pm new file mode 100644 index 000000000..568282979 --- /dev/null +++ b/lib/WebGUI/Asset/File/Image/Photo.pm @@ -0,0 +1,1020 @@ +package WebGUI::Asset::File::Image::Photo; + +=head1 LEGAL + + ------------------------------------------------------------------- + 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 + ------------------------------------------------------------------- + +=cut + +use strict; +use base 'WebGUI::Asset::File::Image'; + +use Carp qw( croak ); +use Image::ExifTool qw( :Public ); +use JSON; +use Tie::IxHash; + +use WebGUI::DateTime; +use WebGUI::Friends; +use WebGUI::Utility; + + +=head1 NAME + +WebGUI::Asset::File::Image::Photo + +=head1 DESCRIPTION + + +=head1 SYNOPSIS + +use WebGUI::Asset::File::Image::Photo + +=head1 METHODS + +These methods are available from this class: + +=cut + +#------------------------------------------------------------------- + +=head2 definition ( session, definition ) + +Define the properties of the Photo asset. + +=cut + +sub definition { + my $class = shift; + my $session = shift; + my $definition = shift; + my $i18n = __PACKAGE__->i18n($session); + + tie my %properties, 'Tie::IxHash', ( + 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 => 'Image.gif', + tableName => 'Photo', + className => 'WebGUI::Asset::File::Image::Photo', + i18n => 'Asset_Photo', + properties => \%properties, + }; + + return $class->SUPER::definition($session, $definition); +} + +#---------------------------------------------------------------------------- + +=head2 appendTemplateVarsForCommentForm ( var ) + +Add the template variables necessary for the comment form to the given hash +reference. Returns the hash reference for convenience. + +=cut + +sub appendTemplateVarsForCommentForm { + my $self = shift; + my $var = shift; + my $session = $self->session; + + $var->{ commentForm_start } + = WebGUI::Form::formHeader( $session ) + . WebGUI::Form::hidden( $session, { name => "func", value => "addCommentSave" } ) + ; + $var->{ commentForm_end } + = WebGUI::Form::formFooter( $session ); + + $var->{ commentForm_bodyText } + = WebGUI::Form::HTMLArea( $session, { + name => "bodyText", + richEditId => $self->getGallery->get("richEditIdComment"), + }); + + $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 +and C. + +This is a sort of catch-all method for applying things to the file after it's +uploaded. This method simply calls other methods to do its work. + +C is a hash reference of options and is currently not used. + +=cut + +sub applyConstraints { + my $self = shift; + my $gallery = $self->getGallery; + + # Update the asset's size and make a thumbnail + $self->SUPER::applyConstraints({ + maxImageSize => $self->getGallery->get("imageViewSize"), + thumbnailSize => $self->getGallery->get("imageThumbnailSize"), + }); + + $self->makeResolutions(); + $self->updateExifDataFromFile(); +} + +#---------------------------------------------------------------------------- + +=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) { + return 0 + unless WebGUI::Friends->new($self->session, $self->get("ownerUserId"))->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 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 +resolution doesn't exist. + +=cut + +sub getDownloadFileUrl { + my $self = shift; + my $resolution = shift; + + croak "Photo->getDownloadFileUrl: resolution must be defined" + unless $resolution; + croak "Photo->getDownloadFileUrl: resolution doesn't exist for this Photo" + unless grep /$resolution/, @{ $self->getResolutions }; + + return $self->getStorageLocation->getFileUrl( $resolution . ".jpg" ); +} + +#---------------------------------------------------------------------------- + +=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. +Does not include the web view image or the thumbnail image. + +=cut + +sub getResolutions { + my $self = shift; + my $storage = $self->getStorageLocation; + + # Return a list not including the web view image. + return grep { $_ ne $self->get("filename") } @{ $storage->getFiles }; +} + +#---------------------------------------------------------------------------- + +=head2 getTemplateVars ( ) + +Get a hash reference of template variables shared by all views of this asset. + +=cut + +sub getTemplateVars { + my $self = shift; + my $session = $self->session; + my $var = $self->get; + my $owner = WebGUI::User->new( $session, $self->get("ownerUserId") ); + + $var->{ canComment } = $self->canComment; + $var->{ canEdit } = $self->canEdit; + $var->{ numberOfComments } = scalar @{ $self->getCommentIds }; + $var->{ ownerUsername } = $owner->username; + $var->{ url } = $self->getUrl; + $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'); + + $var->{ fileUrl } = $self->getFileUrl; + $var->{ thumbnailUrl } = $self->getThumbnailUrl; + + ### Download resolutions + for my $resolution ( $self->getResolutions ) { + push @{ $var->{ resolutions_loop } }, { + url_download => $self->getStorageLocation->getPathFrag($resolution) + }; + } + + ### Format exif vars + my $exif = jsonToObj( delete $var->{exifData} ); + $exif = ImageInfo( $self->getStorageLocation->getPath( $self->get("filename") ) ); + for my $tag ( keys %$exif ) { + # Hash of exif_tag => value + $var->{ "exif_" . $tag } = $exif->{$tag}; + + # Loop of tag => "...", value => "..." + push @{ $var->{exifLoop} }, { tag => $tag, value => $exif->{$tag} }; + } + + return $var; +} + +#---------------------------------------------------------------------------- + +=head2 i18n ( [ session ] ) + +Get a WebGUI::International object for this class. + +Can be called as a class method, in which case a WebGUI::Session object +must be passed in. + +NOTE: This method can NOT be inherited, due to a current limitation +in the i18n system. You must ALWAYS call this with C<__PACKAGE__> + +=cut + +sub i18n { + my $self = shift; + my $session = shift; + + return WebGUI::International->new($session, "Asset_Photo"); +} + +#---------------------------------------------------------------------------- + +=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 +defined, will get the resolutions to make from the Gallery this Photo is +contained in. + +=cut + +sub makeResolutions { + my $self = shift; + my $resolutions = shift; + my $error; + + croak "Photo->makeResolutions: resolutions must be an array reference" + if $resolutions && ref $resolutions ne "ARRAY"; + + # Get default if necessary + $resolutions ||= $self->getGallery->getImageResolutions; + + my $storage = $self->getStorageLocation; + $self->session->errorHandler->info(" Making resolutions for '" . $self->get("filename") . q{'}); + + for my $res ( @$resolutions ) { + # carp if resolution is bad + my $newFilename = $res . ".jpg"; + $storage->copyFile( $self->get("filename"), $newFilename ); + $storage->resize( $newFilename, $res ); + } +} + +#---------------------------------------------------------------------------- + +=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 processPropertiesFromFormPost ( ) + + +=cut + +sub processPropertiesFromFormPost { + my $self = shift; + my $errors = $self->SUPER::processPropertiesFromFormPost || []; + + # Return if errors + return $errors if @$errors; + + # Passes all checks + $self->requestAutoCommit; +} + +#---------------------------------------------------------------------------- + +=head2 processStyle ( html ) + +Returns the HTML from the Gallery's style. + +=cut + +sub processStyle { + my $self = shift; + return $self->getGallery->processStyle( @_ ); +} + +#---------------------------------------------------------------------------- + +=head2 setComment ( commentId, properties ) + +Set a comment. If C is C<"new">, create a new comment. C +is a hash reference of comment information. + +=cut + +sub setComment { + my $self = shift; + my $commentId = shift; + my $properties = shift; + + croak "Photo->setComment: commentId must be defined" + unless $commentId; + croak "Photo->setComment: properties must be a hash reference" + unless $properties && ref $properties eq "HASH"; + + $self->session->db->setRow( + "Photo_comment", "commentId", + { %$properties, commentId => $commentId } + ); +} + +#---------------------------------------------------------------------------- + +=head2 updateExifDataFromFile ( ) + +Gets the EXIF data from the uploaded image and store it in the database. + +=cut + +sub updateExifDataFromFile { + my $self = shift; + my $storage = $self->getStorageLocation; + + return; + my $info = ImageInfo( $storage->getPath( $self->get('filename') ) ); + use Data::Dumper; $self->session->errorHandler->info( Dumper $info ); + $self->update({ + exifData => objToJson( $info ), + }); +} + +#---------------------------------------------------------------------------- + +=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->appendTemplateVarsForCommentForm( $var ); + + my $p = $self->getCommentPaginator; + for my $comment ( @{ $p->getPageData } ) { + 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_addCommentSave ( ) + +Save a new comment to the Photo. + +=cut + +sub www_addCommentSave { + my $self = shift; + my $session = $self->session; + + return $session->privilege->insufficient unless $self->canComment; + + my $form = $self->session->form; + + my $properties = { + assetId => $self->getId, + creationDate => WebGUI::DateTime->new( $session, time )->toDatabase, + userId => $session->user->userId, + visitorIp => ( $session->user->userId eq "1" ? $session->env("REMOTE_ADDR") : undef ), + bodyText => $form->get("bodyText"), + }; + + $self->setComment( "new", $properties ); + + return $self->www_view; +} + +#---------------------------------------------------------------------------- + +=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_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_demote + +Override the default demote page to send the user back to the GalleryAlbum +edit screen. + +=cut + +sub www_demote { + my $self = shift; + + return $self->session->privilege->insufficient unless $self->canEdit; + + $self->demote; + + return $self->session->asset( $self->getParent )->www_edit; +} + +#---------------------------------------------------------------------------- + +=head2 www_download + +Download the Photo with the specified resolution. If no resolution specified, +download the original file. + +=cut + +sub www_download { + my $self = shift; + + return $self->session->privilege->insufficient unless $self->canView; + + my $storage = $self->getStorageLocation; + + $self->session->http->setMimeType( "image/jpeg" ); + $self->session->http->setLastModified( $self->getContentLastModified ); + + my $resolution = $self->session->form->get("resolution"); + if ($resolution) { + return $storage->getFileContentsAsScalar( $resolution . ".jpg" ); + } + else { + return $storage->getFileContentsAsScalar( $self->get("filename") ); + } +} + +#---------------------------------------------------------------------------- + +=head2 www_edit ( ) + +Web facing method which is the default edit page + +This page is only available to those who can edit this Photo. + +=cut + +sub www_edit { + my $self = shift; + my $session = $self->session; + my $form = $self->session->form; + + return $self->session->privilege->insufficient unless $self->canEdit; + return $self->session->privilege->locked unless $self->canEditIfLocked; + + # Prepare the template variables + my $var = { + url_addArchive => $self->getParent->getUrl('func=addArchive'), + }; + + # Generate the form + if ($form->get("func") eq "add") { + $var->{ form_start } + = WebGUI::Form::formHeader( $session, { + action => $self->getParent->getUrl('func=editSave;assetId=new;class='.__PACKAGE__), + }); + } + else { + $var->{ form_start } + = WebGUI::Form::formHeader( $session, { + action => $self->getUrl('func=editSave'), + }); + } + $var->{ form_start } + .= WebGUI::Form::hidden( $session, { + name => "proceed", + value => "showConfirmation", + }); + + $var->{ form_end } = WebGUI::Form::formFooter( $session ); + + $var->{ form_submit } + = WebGUI::Form::submit( $session, { + name => "submit", + value => "Save", + }); + + $var->{ form_title } + = WebGUI::Form::Text( $session, { + name => "title", + value => ( $form->get("title") || $self->get("title") ), + }); + + $var->{ form_synopsis } + = WebGUI::Form::HTMLArea( $session, { + name => "synopsis", + value => ( $form->get("synopsis") || $self->get("synopsis") ), + richEditId => $self->getGallery->get("assetIdRichEditFile"), + }); + + $var->{ form_photo } = $self->getEditFormUploadControl; + + $var->{ form_keywords } + = WebGUI::Form::Text( $session, { + name => "keywords", + value => ( $form->get("keywords") || $self->get("keywords") ), + }); + + $var->{ form_location } + = WebGUI::Form::Text( $session, { + name => "location", + value => ( $form->get("location") || $self->get("location") ), + }); + + $var->{ form_friendsOnly } + = WebGUI::Form::yesNo( $session, { + name => "friendsOnly", + value => ( $form->get("friendsOnly") || $self->get("friendsOnly") ), + defaultValue => undef, + }); + + + return $self->processStyle( + $self->processTemplate( $var, $self->getGallery->getTemplateIdEditFile ) + ); +} + +#---------------------------------------------------------------------------- + +=head2 www_editSave ( ) + +Save the edit form. Overridden to display a confirm message to the user. + +=cut + +sub www_editSave { + my $self = shift; + $self->SUPER::www_editSave; + + my $i18n = __PACKAGE__->i18n( $self->session ); + + sprintf $i18n->get("save 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_promote + +Override the default promote page to send the user back to the GalleryAlbum +edit screen. + +=cut + +sub www_promote { + my $self = shift; + + return $self->session->privilege->insufficient unless $self->canEdit; + + $self->promote; + + return $self->session->asset( $self->getParent )->www_edit; +} + +#---------------------------------------------------------------------------- + +=head2 www_showConfirmation ( ) + +Shows the confirmation message after adding / editing a gallery album. +Provides links to view the photo and add more photos. + +=cut + +sub www_showConfirmation { + my $self = shift; + my $i18n = __PACKAGE__->i18n( $self->session ); + + return $self->processStyle( + sprintf( $i18n->get('save message'), + $self->getUrl, + $self->getParent->getUrl('func=add;className='.__PACKAGE__), + ) + ); +} + +#---------------------------------------------------------------------------- + +=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; + + $self->session->http->setLastModified($self->getContentLastModified); + $self->session->http->sendHeader; + $self->prepareView; + my $style = $self->processStyle("~~~"); + my ($head, $foot) = split("~~~",$style); + $self->session->output->print($head, 1); + $self->session->output->print($self->view); + $self->session->output->print($foot, 1); + return "chunked"; +} + +1; diff --git a/lib/WebGUI/Asset/Shortcut.pm b/lib/WebGUI/Asset/Shortcut.pm index debdaebe4..5bc9714e7 100644 --- a/lib/WebGUI/Asset/Shortcut.pm +++ b/lib/WebGUI/Asset/Shortcut.pm @@ -11,6 +11,7 @@ package WebGUI::Asset::Shortcut; #------------------------------------------------------------------- use strict; +use Carp; use Tie::IxHash; use WebGUI::Asset; use WebGUI::International; @@ -605,6 +606,26 @@ sub getPrefFieldsToImport { return split("\n",$self->getValue("prefFieldsToImport")); } +#---------------------------------------------------------------------------- + +=head2 getTemplateVars + +Gets the template vars for this shortcut. + +=cut + +sub getTemplateVars { + my $self = shift; + + my $shortcut = $self->getShortcut; + if ( $shortcut->can('getTemplateVars') ) { + return $shortcut->getTemplateVars; + } + else { + return $shortcut->get; + } +} + #------------------------------------------------------------------- sub isDashlet { my $self = shift; @@ -648,6 +669,34 @@ sub processPropertiesFromFormPost { $self->uncacheOverrides; } +#---------------------------------------------------------------------------- + +=head2 setOverride ( overrides ) + +Set this shortcut's overrides. C is a hash reference of overrides +to set. + +=cut + +sub setOverride { + my $self = shift; + my $override = shift; + + croak "Shortcut->setOverride - first argument must be hash reference" + unless $override && ref $override eq "HASH"; + + for my $key ( %$override ) { + $self->session->db->write( + "DELETE FROM Shortcut_overrides WHERE assetId=? AND fieldName=?", + [$self->getId, $key], + ); + $self->session->db->write( + "INSERT INTO Shortcut_overrides VALUES (?,?,?)", + [$self->getId, $key, $override->{$key}], + ); + } +} + #------------------------------------------------------------------- sub purge { my $self = shift; @@ -880,23 +929,23 @@ sub www_editOverride { #------------------------------------------------------------------- sub www_saveOverride { - my $self = shift; - return $self->session->privilege->insufficient() unless $self->canEdit; - my $fieldName = $self->session->form->process("overrideFieldName"); - my %overrides = $self->getOverrides; - my $output = ''; - my %props; - foreach my $def (@{$self->getShortcutOriginal->definition($self->session)}) { - %props = (%props,%{$def->{properties}}); - } - my $fieldType = $props{$fieldName}{fieldType}; - my $value = $self->session->form->process($fieldName,$fieldType); - $value = $self->session->form->process("newOverrideValueText") || $value; - $self->session->db->write("delete from Shortcut_overrides where assetId=".$self->session->db->quote($self->getId)." and fieldName=".$self->session->db->quote($fieldName)); - $self->session->db->write("insert into Shortcut_overrides values (".$self->session->db->quote($self->getId).",".$self->session->db->quote($fieldName).",".$self->session->db->quote($value).")"); - $self->uncacheOverrides; - $self->getShortcutOriginal->purgeCache(); - return $self->www_manageOverrides; + my $self = shift; + return $self->session->privilege->insufficient() unless $self->canEdit; + my $fieldName = $self->session->form->process("overrideFieldName"); + my %overrides = $self->getOverrides; + my $output = ''; + my %props; + foreach my $def (@{$self->getShortcutOriginal->definition($self->session)}) { + %props = (%props,%{$def->{properties}}); + } + my $fieldType = $props{$fieldName}{fieldType}; + my $value = $self->session->form->process($fieldName,$fieldType); + $value = $self->session->form->process("newOverrideValueText") || $value; + $self->session->db->write("delete from Shortcut_overrides where assetId=".$self->session->db->quote($self->getId)." and fieldName=".$self->session->db->quote($fieldName)); + $self->session->db->write("insert into Shortcut_overrides values (".$self->session->db->quote($self->getId).",".$self->session->db->quote($fieldName).",".$self->session->db->quote($value).")"); + $self->uncacheOverrides; + $self->getShortcutOriginal->purgeCache(); + return $self->www_manageOverrides; } #------------------------------------------------------------------- @@ -928,5 +977,43 @@ sub www_view { return $output; } +#---------------------------------------------------------------------------- + +=head1 STATIC METHODS + +These methods are called using CLASS->method + +#---------------------------------------------------------------------------- + +=head2 getShortcutsForAssetId ( session, assetId [, properties] ) + +Get an arrayref of assetIds of all the shortcuts for the passed-in assetId. + +"properties" is a hash reference of properties to give to getLineage. +Probably the only useful key will be "returnObjects". + +=cut + +sub getShortcutsForAssetId { + my $class = shift; + my $session = shift; + my $assetId = shift; + my $properties = shift || {}; + + croak "First argument to getShortcutsForAssetId must be WebGUI::Session" + unless $session && $session->isa("WebGUI::Session"); + croak "Second argument to getShortcutsForAssetId must be assetId" + unless $assetId; + croak "Third argument to getShortcutsForAssetId must be hash reference" + if $properties && !ref $properties eq "HASH"; + + my $db = $session->db; + + $properties->{ joinClass } = 'WebGUI::Asset::Shortcut'; + $properties->{ whereClause } = 'Shortcut.shortcutToAssetId = ' . $db->quote($assetId); + + return WebGUI::Asset->getRoot($session)->getLineage(['descendants'], $properties); +} + 1; diff --git a/lib/WebGUI/Asset/Wobject/Gallery.pm b/lib/WebGUI/Asset/Wobject/Gallery.pm new file mode 100644 index 000000000..3bbbdb3cc --- /dev/null +++ b/lib/WebGUI/Asset/Wobject/Gallery.pm @@ -0,0 +1,1024 @@ +package WebGUI::Asset::Wobject::Gallery; + +$VERSION = "1.0.0"; + +#------------------------------------------------------------------- +# WebGUI is Copyright 2001-2006 Plain Black Corporation. +#------------------------------------------------------------------- +# Please read the legal notices (docs/legal.txt) and the license +# (docs/license.txt) that came with this distribution before using +# this software. +#------------------------------------------------------------------- +# http://www.plainblack.com info@plainblack.com +#------------------------------------------------------------------- + +use strict; +use Tie::IxHash; +use WebGUI::International; +use WebGUI::Utility; +use base 'WebGUI::Asset::Wobject'; + +=head1 NAME + +=head1 DESCRIPTION + +=head1 SYNOPSIS + +=head1 DIAGNOSTICS + +=head1 METHODS + +#------------------------------------------------------------------- + +=head2 definition ( ) + +=cut + +sub definition { + my $class = shift; + my $session = shift; + my $definition = shift; + my $i18n = WebGUI::International->new($session, 'Asset_Gallery'); + + tie my %imageResolutionOptions, 'Tie::IxHash', ( + '640' => '640', + '800' => '800', + '1024' => '1024', + '1260' => '1260', + '1440' => '1440', + '1600' => '1600', + '2880' => '2880', + ); + + tie my %viewDefaultOptions, 'Tie::IxHash', ( + list => $i18n->get("viewDefault option list"), + album => $i18n->get("viewDefault option album"), + ); + + tie my %viewListOrderByOptions, 'Tie::IxHash', ( + creationDate => $i18n->get("viewListOrderBy option creationDate"), + lineage => $i18n->get("viewListOrderBy option lineage"), + revisionDate => $i18n->get("viewListOrderBy option revisionDate"), + title => $i18n->get("viewListOrderBy option title"), + ); + + tie my %viewListOrderDirectionOptions, 'Tie::IxHash', ( + ASC => $i18n->get("viewListOrderDirection option asc"), + DESC => $i18n->get("viewListOrderDirection option desc"), + ); + + tie my %properties, 'Tie::IxHash', ( + groupIdAddComment => { + tab => "security", + fieldType => "group", + defaultValue => 2, # Registered Users + label => $i18n->get("groupIdAddComment label"), + hoverHelp => $i18n->get("groupIdAddComment description"), + }, + groupIdAddFile => { + tab => "security", + fieldType => "group", + defaultValue => 2, # Registered Users + 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", + defaultValue => join("\n", '800', '1024', '1200', '1600', '2880'), + options => \%imageResolutionOptions, + label => $i18n->get("imageResolutions label"), + hoverHelp => $i18n->get("imageResolutions description"), + }, + imageViewSize => { + tab => "properties", + fieldType => "integer", + defaultValue => 700, + label => $i18n->get("imageViewSize label"), + hoverHelp => $i18n->get("imageViewSize description"), + }, + imageThumbnailSize => { + tab => "properties", + fieldType => "integer", + defaultValue => 300, + label => $i18n->get("imageThumbnailSize label"), + hoverHelp => $i18n->get("imageThumbnailSize description"), + }, + maxSpacePerUser => { + tab => "properties", + fieldType => "integer", + defaultValue => 0, + label => $i18n->get("maxSpacePerUser label"), + hoverHelp => $i18n->get("maxSpacePerUser description"), + }, + richEditIdComment => { + tab => "properties", + fieldType => "selectRichEditor", + defaultValue => "PBrichedit000000000002", # Forum Rich Editor + label => $i18n->get("richEditIdFileComment label"), + hoverHelp => $i18n->get("richEditIdFileComment description"), + }, + templateIdAddArchive => { + tab => "display", + fieldType => "template", + defaultValue => "", + namespace => "GalleryAlbum/AddArchive", + label => $i18n->get("templateIdAddArchive label"), + hoverHelp => $i18n->get("templateIdAddArchive description"), + }, + templateIdDeleteAlbum => { + tab => "display", + fieldType => "template", + defaultValue => "", + namespace => "GalleryAlbum/Delete", + label => $i18n->get("templateIdDeleteAlbum label"), + hoverHelp => $i18n->get("templateIdDeleteAlbum description"), + }, + templateIdDeleteFile => { + tab => "display", + fieldType => "template", + defaultValue => "", + namespace => "GalleryFile/Delete", + label => $i18n->get("templateIdDeleteFile label"), + hoverHelp => $i18n->get("templateIdDeleteFile description"), + }, + templateIdEditAlbum => { + tab => "display", + fieldType => "template", + defaultValue => "", + namespace => "GalleryAlbum/Edit", + label => $i18n->get("templateIdEditAlbum label"), + hoverHelp => $i18n->get("templateIdEditAlbum description"), + }, + templateIdEditFile => { + tab => "display", + fieldType => "template", + defaultValue => "", + namespace => "GalleryFile/Edit", + label => $i18n->get("templateIdEditFile label"), + hoverHelp => $i18n->get("templateIdEditFile description"), + }, + templateIdListAlbums => { + tab => "display", + fieldType => "template", + defaultValue => "", + namespace => "Gallery/ListAlbums", + label => $i18n->get("templateIdListAlbums label"), + hoverHelp => $i18n->get("templateIdListAlbums description"), + }, + templateIdListAlbumsRss => { + tab => "display", + fieldType => "template", + defaultValue => "", + namespace => "Gallery/ListAlbumsRss", + label => $i18n->get("templateIdListAlbumsRss label"), + hoverHelp => $i18n->get("templateIdListAlbumsRss description"), + }, + templateIdListFilesForUser => { + tab => "display", + fieldType => "template", + defaultValue => "", + namespace => "Gallery/ListFilesForUser", + label => $i18n->get("templateIdListFilesForUser label"), + hoverHelp => $i18n->get("templateIdListFilesForUser description"), + }, + templateIdListFilesForUserRss => { + tab => "display", + fieldType => "template", + defaultValue => "", + namespace => "Gallery/ListFilesForUserRss", + label => $i18n->get("templateIdListFilesForUserRss label"), + hoverHelp => $i18n->get("templateIdListFilesForUserRss description"), + }, + templateIdMakeShortcut => { + tab => "display", + fieldType => "template", + defaultValue => "", + namespace => "GalleryFile/MakeShortcut", + label => $i18n->get("templateIdMakeShortcut label"), + hoverHelp => $i18n->get("templateIdMakeShortcut description"), + }, + templateIdSearch => { + tab => "display", + fieldType => "template", + defaultValue => "", + namespace => "Gallery/Search", + label => $i18n->get("templateIdSearch label"), + hoverHelp => $i18n->get("templateIdSearch description"), + }, + templateIdViewSlideshow => { + tab => "display", + fieldType => "template", + defaultValue => "", + namespace => "GalleryAlbum/ViewSlideshow", + label => $i18n->get("templateIdViewSlideshow label"), + hoverHelp => $i18n->get("templateIdViewSlideshow description"), + }, + templateIdViewThumbnails => { + tab => "display", + fieldType => "template", + defaultValue => "", + namespace => "GalleryAlbum/ViewThumbnails", + label => $i18n->get("templateIdViewThumbnails label"), + hoverHelp => $i18n->get("templateIdViewThumbnails description"), + }, + templateIdViewAlbum => { + tab => "display", + fieldType => "template", + defaultValue => "", + namespace => "GalleryAlbum/View", + label => $i18n->get("templateIdViewAlbum label"), + hoverHelp => $i18n->get("templateIdViewAlbum description"), + }, + templateIdViewAlbumRss => { + tab => "display", + fieldType => "template", + defaultValue => "", + namespace => "GalleryAlbum/ViewRss", + label => $i18n->get("templateIdViewAlbumRss label"), + hoverHelp => $i18n->get("templateIdViewAlbumRss description"), + }, + templateIdViewFile => { + tab => "display", + fieldType => "template", + defaultValue => "", + namespace => "GalleryFile/View", + label => $i18n->get("templateIdViewFile label"), + hoverHelp => $i18n->get("templateIdViewFile description"), + }, + viewDefault => { + tab => "display", + fieldType => "selectBox", + defaultValue => "list", + options => \%viewDefaultOptions, + label => $i18n->get("viewDefault label"), + hoverHelp => $i18n->get("viewDefault description"), + }, + viewAlbumAssetId => { + tab => "display", + fieldType => "asset", + class => "WebGUI::Asset::Wobject::GalleryAlbum", + label => $i18n->get("viewAlbumAssetId label"), + hoverHelp => $i18n->get("viewAlbumAssetId description"), + }, + viewListOrderBy => { + tab => "display", + fieldType => "selectBox", + defaultValue => "lineage", # "Sequence Number" + options => \%viewListOrderByOptions, + label => $i18n->get("viewListOrderBy label"), + hoverHelp => $i18n->get("viewListOrderBy description"), + }, + viewListOrderDirection => { + tab => "display", + fieldType => "selectBox", + defaultValue => "ASC", + options => \%viewListOrderDirectionOptions, + label => $i18n->get("viewListOrderDirection label"), + hoverHelp => $i18n->get("viewListOrderDirection description"), + }, + workflowIdCommit => { + tab => "security", + fieldType => "workflow", + defaultValue => "pbworkflow000000000003", # Commit without approval + type => 'WebGUI::VersionTag', + label => $i18n->get("workflowIdCommit label"), + hoverHelp => $i18n->get("workflowIdCommit description"), + }, + ); + + push @{$definition}, { + assetName => $i18n->get('assetName'), + icon => 'newWobject.gif', + autoGenerateForms => 1, + tableName => 'Gallery', + className => __PACKAGE__, + properties => \%properties, + }; + + return $class->SUPER::definition($session, $definition); +} + +#---------------------------------------------------------------------------- + +=head2 appendTemplateVarsSearchForm ( var ) + +Appends the template vars for the search form to the hash reference C. +Returns the hash reference for convenience. + +=cut + +sub appendTemplateVarsSearchForm { + my $self = shift; + my $var = shift; + my $session = $self->session; + my $form = $self->session->form; + my $i18n = WebGUI::International->new($session, 'Asset_Gallery'); + + $var->{ searchForm_start } + = WebGUI::Form::formHeader( $session, { + action => $self->getUrl('func=search'), + method => "GET", + }); + + $var->{ searchForm_end } + = WebGUI::Form::formFooter( $session ); + + $var->{ searchForm_basicSearch } + = WebGUI::Form::text( $session, { + name => "basicSearch", + value => $form->get("basicSearch"), + }); + + $var->{ searchForm_title } + = WebGUI::Form::text( $session, { + name => "title", + value => $form->get("title"), + }); + + $var->{ searchForm_description } + = WebGUI::Form::text( $session, { + name => "description", + value => $form->get("description"), + }); + + $var->{ searchForm_keywords } + = WebGUI::Form::text( $session, { + name => "keywords", + value => $form->get("keywords"), + }); + + # Search classes + tie my %searchClassOptions, 'Tie::IxHash', ( + 'WebGUI::Asset::File::Image::Photo' => $i18n->get("search class photo"), + 'WebGUI::Asset::Wobject::GalleryAlbum' => $i18n->get("search class galleryalbum"), + '' => $i18n->get("search class any"), + ); + $var->{ searchForm_className } + = WebGUI::Form::radioList( $session, { + name => "className", + value => $form->get("className"), + options => \%searchClassOptions, + }); + + # Search creationDate + my $oneYearAgo = WebGUI::DateTime->new( $session, time )->add( years => -1 )->epoch; + $var->{ searchForm_creationDate_after } + = WebGUI::Form::dateTime( $session, { + name => "creationDate_after", + value => $form->get("creationDate_after","dateTime") || $oneYearAgo, + }); + $var->{ searchForm_creationDate_before } + = WebGUI::Form::dateTime( $session, { + name => "creationDate_before", + value => $form->get("creationDate_before","dateTime"), + }); + + # Buttons + $var->{ searchForm_submit } + = WebGUI::Form::submit( $session, { + name => "submit", + value => $i18n->get("search submit"), + }); + + return $var; +} + +#---------------------------------------------------------------------------- + +=head2 canAddFile ( [userId] ) + +Returns true if the user can add files to this Gallery. C is the +userId to check. If no userId is passed, will check the current user. + +Users can add files to this gallery if they are part of the C + +=cut + +sub canAddFile { + my $self = shift; + my $userId = shift; + + my $user = $userId + ? WebGUI::User->new( $self->session, $userId ) + : $self->session->user + ; + + return $user->isInGroup( $self->get("groupIdAddFile") ); +} + +#---------------------------------------------------------------------------- + +=head2 canComment ( [userId] ) + +Returns true if the user can comment on this Gallery. C is the userId +to check. If no userId is passed, will check the current user. + +Users can comment on this gallery if they are part of the +C group. + +=cut + +sub canComment { + my $self = shift; + my $userId = shift; + + my $user = $userId + ? WebGUI::User->new( $self->session, $userId ) + : $self->session->user + ; + + return $user->isInGroup( $self->get("groupIdAddComment") ); +} + +#---------------------------------------------------------------------------- + +=head2 canEdit ( [userId] ) + +Returns true if the user can edit this Gallery. C is the userId to +check. If no userId is passed, will check the current user. + +Users can edit this gallery if they are part of the C group. + +=cut + +sub canEdit { + my $self = shift; + my $userId = shift; + + my $user = $userId + ? WebGUI::User->new( $self->session, $userId ) + : $self->session->user + ; + + return $user->isInGroup( $self->get("groupIdEdit") ); +} + +#---------------------------------------------------------------------------- + +=head2 canView ( [userId] ) + +Returns true if the user can view this Gallery. C is the userId to +check. If no userId is passed, will check the current user. + +Users can view this gallery if they are part of the C group. + +=cut + +sub canView { + my $self = shift; + my $userId = shift; + + my $user = $userId + ? WebGUI::User->new( $self->session, $userId ) + : $self->session->user + ; + + return $user->isInGroup( $self->get("groupIdView") ); +} + +#---------------------------------------------------------------------------- + +=head2 getAlbumIds ( ) + +Gets an array reference of all the album IDs under this Gallery. + +=cut + +sub getAlbumIds { + my $self = shift; + + my $assets + = $self->getLineage(['descendants'], { + includeOnlyClasses => ['WebGUI::Asset::Wobject::GalleryAlbum'], + }); + + return $assets; +} + +#---------------------------------------------------------------------------- + +=head2 getAlbumPaginator ( options ) + +Gets a WebGUI::Paginator for all the albums in this Gallery. C is a +hash reference with the following keys. + + perpage => The number of results to show per page. Default: 20 + +=cut + +sub getAlbumPaginator { + my $self = shift; + my $options = shift; + + my $perpage = $options->{ perpage } || 20; + + my $p + = WebGUI::Paginator->new( $self->session, $self->getUrl, $perpage ); + $p->setDataByArrayRef( $self->getAlbumIds ); + + return $p; +} + +#---------------------------------------------------------------------------- + +=head2 getAssetClassForFile ( filepath ) + +Gets the WebGUI Asset class for the file at the given C. Returns +undef if the file cannot be saved under this Gallery. + +=cut + +sub getAssetClassForFile { + my $self = shift; + my $filepath = shift; + + # Checks for Photo assets + if ( $filepath =~ /\.(jpe?g|gif|png)$/i ) { + return "WebGUI::Asset::File::Image::Photo"; + } + + # No class found + return undef; +} + +#---------------------------------------------------------------------------- + +=head2 getImageResolutions ( ) + +Gets an array reference of the image resolutions to create for image-type +assets in this gallery. + +=cut + +sub getImageResolutions { + my $self = shift; + return [ split /\n/, $self->get("imageResolutions") ]; +} + +#---------------------------------------------------------------------------- + +=head2 getSearchPaginator ( rules ) + +Gets a WebGUI::Paginator for a search. C is a hash reference of +options with the following keys: + + keywords => Keywords to search on + +Other keys are valid, see C for details. + +=cut + +sub getSearchPaginator { + my $self = shift; + my $rules = shift; + + $rules->{ lineage } = [ $self->get("lineage") ]; + + my $search = WebGUI::Search->new( $self->session ); + $search->search( $rules ); + my $paginator = $search->getPaginatorResultSet( $rules->{url} ); + + return $paginator; +} + +#---------------------------------------------------------------------------- + +=head2 getTemplateIdEditFile ( ) + +Returns the ID for the template to edit a file. + +NOTE: This may need to change in the future to take into account different +classes of files inside of a Gallery. + +=cut + +sub getTemplateIdEditFile { + my $self = shift; + return $self->get("templateIdEditFile"); +} + +#---------------------------------------------------------------------------- + +=head2 getTemplateVars ( ) + +Gets a hash reference of vars common to all templates. + +=cut + +sub getTemplateVars { + my $self = shift; + my $var = $self->get; + + # Add the search form variables + $self->appendTemplateVarsSearchForm( $var ); + + $var->{ url } = $self->getUrl; + $var->{ url_addAlbum } = $self->getUrl('func=add;class=WebGUI::Asset::Wobject::GalleryAlbum'); + $var->{ url_listAlbums } = $self->getUrl('func=listAlbums'); + $var->{ url_listAlbumsRss } = $self->getUrl('func=listAlbumsRss'); + $var->{ url_listFilesForCurrentUser } = $self->getUrl('func=listFilesForUser'); + $var->{ url_search } = $self->getUrl('func=search'); + + $var->{ canEdit } = $self->canEdit; + $var->{ canAddFile } = $self->canAddFile; + + return $var; +} + +#---------------------------------------------------------------------------- + +=head2 getUserAlbumIds ( [userId] ) + +Gets an array reference of assetIds for the GalleryAlbums in this Gallery +owned by the specified C. If userId is not defined, will use the +current user. + +=cut + +sub getUserAlbumIds { + my $self = shift; + my $userId = shift || $self->session->user->userId; + + my $db = $self->session->db; + + my $assetIds + = $self->getLineage( ['descendants'], { + includeOnlyClasses => [ 'WebGUI::Asset::Wobject::GalleryAlbum' ], + whereClause => "ownerUserId = " . $db->quote($userId), + }); + + return $assetIds; +} + +#---------------------------------------------------------------------------- + +=head2 getUserFileIds ( [userId] ) + +Gets an array reference of assetIds for the files in this Gallery owned by +the specified C. If userId is not defined, will use the current user. + +=cut + +sub getUserFileIds { + my $self = shift; + my $userId = shift || $self->session->user->userId; + + my $db = $self->session->db; + + # Note: We use excludeClasses to avoid getting GalleryAlbum assets + my $assetIds + = $self->getLineage( ['descendants'], { + excludeClasses => [ 'WebGUI::Asset::Wobject::GalleryAlbum' ], + whereClause => "ownerUserId = " . $db->quote($userId), + }); + + return $assetIds; +} + +#---------------------------------------------------------------------------- + +=head2 getUserFilePaginator ( options ) + +Gets a WebGUI::Paginator for the files owned by a specific C. +C is a hash reference of options with the following keys: + + userId => The user who owns the asset. Defaults to the current user. + url => The URL to give to the paginator + +=cut + +sub getUserFilePaginator { + my $self = shift; + my $options = shift; + my $userId = delete $options->{userId}; + my $url = delete $options->{url}; + + my $p = WebGUI::Paginator->new( $self->session, $url ); + $p->setDataByArrayRef( $self->getUserFileIds( $userId ) ); + + return $p; +} + +#---------------------------------------------------------------------------- + +=head2 prepareView ( ) + +See WebGUI::Asset::prepareView() for details. + +=cut + +sub prepareView { + my $self = shift; + $self->SUPER::prepareView(); + + if ( $self->get("viewDefault") eq "album" ) { + my $asset + = WebGUI::Asset->newByDynamicClass( $self->session, $self->get("viewAlbumAssetId") ); + $asset->prepareView; + $self->{_viewAsset} = $asset; + } + else { + $self->prepareViewListAlbums; + } +} + +#---------------------------------------------------------------------------- + +=head2 prepareViewListAlbums ( ) + +Prepare the template for listing multiple albums. + +=cut + +sub prepareViewListAlbums { + my $self = shift; + my $template + = WebGUI::Asset::Template->new($self->session, $self->get("templateIdListAlbums")); + $template->prepare; + $self->{_viewTemplate} = $template; +} + +#---------------------------------------------------------------------------- + +=head2 view ( ) + +Show the default view based on the Gallery settings. + +=cut + +sub view { + my $self = shift; + my $session = $self->session; + my $var = $self->get; + + if ( $self->get("viewDefault") eq "album" ) { + return $self->{_viewAsset}->view; + } + else { + return $self->view_listAlbums; + } +} + +#---------------------------------------------------------------------------- + +=head2 view_listAlbums ( ) + +Show a paginated list of the albums in this gallery. This method does the +actual work. + +=cut + +sub view_listAlbums { + my $self = shift; + my $session = $self->session; + my $var = $self->getTemplateVars; + my $form = $self->session->form; + + my $p + = $self->getAlbumPaginator( { + perpage => $form->get('perpage'), + } ); + $p->appendTemplateVars( $var ); + + for my $assetId ( @{ $p->getPageData } ) { + my $asset = WebGUI::Asset->newByDynamicClass( $session, $assetId ); + push @{ $var->{albums} }, $asset->getTemplateVars; + } + + return $self->processTemplate( $var, undef, $self->{_viewTemplate} ); +} + +#---------------------------------------------------------------------------- + +=head2 www_listAlbums ( ) + +Show a paginated list of the albums in this gallery. + +=cut + +sub www_listAlbums { + my $self = shift; + + # Perform the prepareView ourselves + $self->prepareViewListAlbums; + + return $self->processStyle( + $self->view_listAlbums + ); +} + +#---------------------------------------------------------------------------- + +=head2 www_listAlbumsRss ( ) + +Show an RSS feed for the albums in this gallery. + +=cut + +sub www_listAlbumsRss { + my $self = shift; + my $session = $self->session; + my $var = $self->getTemplateVars; + + for my $assetId ( @{ $self->getAlbumIds } ) { + my $asset = WebGUI::Asset->newByDynamicClass( $session, $assetId ); + my $assetVar = $asset->getTemplateVars; + + # Fix URLs + for my $key ( qw( url ) ) { + $assetVar->{ $key } = $self->session->url->getSiteURL . $assetVar->{ $key }; + } + + # Additional vars for RSS + $assetVar->{ rssDate } + = $session->datetime->epochToMail( $assetVar->{ creationDate } ); + + push @{ $var->{albums} }, $assetVar; + } + + $self->session->http->setMimeType('text/xml'); + return $self->processTemplate( $var, $self->get("templateIdListAlbumsRss") ); +} + +#---------------------------------------------------------------------------- + +=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 $form = $self->session->form; + my $db = $self->session->db; + + my $var = $self->getTemplateVars; + # NOTE: Search form is added as part of getTemplateVars() + + # Get search results, if necessary. + if ($form->get("submit")) { + # Keywords to search on + my $keywords = join " ", $form->get('basicSearch'), + $form->get('keywords'), + $form->get('title'), + $form->get('description') + ; + + # Build a where clause from the advanced options + # Lineage search can capture gallery + my $where = q{assetIndex.assetId <> '} . $self->getId . q{'}; + if ( $form->get("title") ) { + $where .= q{ AND assetData.title LIKE } + . $db->quote( '%' . $form->get("title") . '%' ) + ; + } + if ( $form->get("description") ) { + $where .= q{ AND assetData.synopsis LIKE } + . $db->quote( '%' . $form->get("description") . '%' ) + ; + } + if ( $form->get("className") ) { + $where .= q{ AND asset.className IN ('} + . $db->quoteAndJoin( [$form->get('className','checkList')] ) + . q{)} + ; + } + + # Build a URL for the pagination + my $url + = $self->getUrl( + 'func=search;submit=1;' + . 'basicSearch=' . $form->get('basicSearch') . ';' + . 'keywords=' . $form->get('keywords') . ';' + . 'title=' . $form->get('title') . ';' + . 'description=' . $form->get('description') . ';' + . 'className=' . $form->get('className') . ';' + . 'creationDate_after=' . $form->get('creationDate_after') . ';' + . 'creationDate_before=' . $form->get('creationDate_before') . ';' + ); + + my $p + = $self->getSearchPaginator( { + url => $url, + keywords => $keywords, + where => $where, + joinClass => ['WebGUI::Asset::Wobject::GalleryAlbum', 'WebGUI::Asset::File::Image::Photo'], + } ); + + $var->{ keywords } = $keywords; + + $p->appendTemplateVars( $var ); + for my $result ( @{ $p->getPageData } ) { + my $asset = WebGUI::Asset->newByDynamicClass( $self->session, $result->{assetId} ); + push @{ $var->{search_results} }, $asset->getTemplateVars; + } + } + + return $self->processStyle( + $self->processTemplate( $var, $self->get("templateIdSearch") ) + ); +} + +#---------------------------------------------------------------------------- + +=head2 www_listFilesForUser ( ) + +Show all the GalleryAlbums and files owned by a given userId. If no userId is +given, will use the current user. + +=cut + +sub www_listFilesForUser { + my $self = shift; + my $session = $self->session; + my $var = $self->getTemplateVars; + my $userId = $self->session->form->get("userId") || $self->session->user->userId; + my $user = WebGUI::User->new( $session, $userId ); + + $var->{ url_rss } = $self->getUrl('func=listFilesForUserRss;userId=' . $userId); + $var->{ userId } = $userId; + $var->{ username } = $user->username; + + # Get all the albums + my $albumIds = $self->getUserAlbumIds( $userId ); + for my $albumId ( @$albumIds ) { + my $asset = WebGUI::Asset->newByDynamicClass( $session, $albumId ); + push @{ $var->{user_albums} }, $asset->getTemplateVars; + } + + # Get a page of files + my $p + = $self->getUserFilePaginator({ + userId => $userId, + url => $self->getUrl("func=listFilesForUser") + }); + $p->appendTemplateVars( $var ); + + for my $fileId ( @{ $p->getPageData } ) { + my $asset = WebGUI::Asset->newByDynamicClass( $session, $fileId ); + push @{ $var->{user_files} }, $asset->getTemplateVars; + } + + return $self->processStyle( + $self->processTemplate( $var, $self->get("templateIdListFilesForUser") ) + ); +} + +#---------------------------------------------------------------------------- + +=head2 www_listFilesForUserRss ( ) + +=cut + +sub www_listFilesForUserRss { + my $self = shift; + my $session = $self->session; + my $var = $self->getTemplateVars; + my $userId = $self->session->form("userId") || $self->session->user->userId; + + # Fix URLs for template vars + for my $key ( qw( url ) ) { + $var->{ $key } = $self->session->url->getSiteURL . $var->{ $key }; + } + + # Get all the albums + my $albumIds = $self->getUserAlbumIds( $userId ); + for my $albumId ( @$albumIds ) { + my $asset = WebGUI::Asset->newByDynamicClass( $session, $albumId ); + my $assetVar = $asset->getTemplateVars; + + for my $key ( qw( url ) ) { + $assetVar->{ $key } = $self->session->url->getSiteURL . $assetVar->{ $key }; + } + + push @{ $var->{user_albums} }, $assetVar; + } + + # Get all the files + my $fileIds = $self->getUserFileIds( $userId ); + for my $fileId ( @$fileIds ) { + my $asset = WebGUI::Asset->newByDynamicClass( $session, $fileId ); + my $assetVar = $asset->getTemplateVars; + + for my $key ( qw( url ) ) { + $assetVar->{ $key } = $self->session->url->getSiteURL . $assetVar->{ $key }; + } + + push @{ $var->{user_files} }, $assetVar; + } + + $self->session->http->setMimeType('text/xml'); + return $self->processTemplate( $var, $self->get("templateIdListFilesForUserRss") ); +} + +1; diff --git a/lib/WebGUI/Asset/Wobject/GalleryAlbum.pm b/lib/WebGUI/Asset/Wobject/GalleryAlbum.pm new file mode 100644 index 000000000..2f62447e3 --- /dev/null +++ b/lib/WebGUI/Asset/Wobject/GalleryAlbum.pm @@ -0,0 +1,882 @@ +package WebGUI::Asset::Wobject::GalleryAlbum; + +$VERSION = "1.0.0"; + +#------------------------------------------------------------------- +# WebGUI is Copyright 2001-2006 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 base 'WebGUI::Asset::Wobject'; +use Carp qw( croak ); +use File::Find; +use File::Spec; +use File::Temp qw{ tempdir }; +use Tie::IxHash; +use WebGUI::International; +use WebGUI::Utility; + +use Archive::Any; + +=head1 NAME + +=head1 DESCRIPTION + +=head1 SYNOPSIS + +=head1 DIAGNOSTICS + +=head1 METHODS + +#------------------------------------------------------------------- + +=head2 definition ( ) + +Define wobject properties for new GalleryAlbum wobjects. + +=cut + +sub definition { + my $class = shift; + my $session = shift; + my $definition = shift; + my $i18n = __PACKAGE__->i18n($session); + + tie my %properties, 'Tie::IxHash', ( + allowComments => { + fieldType => "yesNo", + defaultValue => 1, + }, + othersCanAdd => { + fieldType => "yesNo", + defaultValue => 0, + }, + assetIdThumbnail => { + fieldType => "asset", + defaultValue => undef, + }, + ); + + push @{$definition}, { + assetName => $i18n->get('assetName'), + autoGenerateForms => 0, + icon => 'newWobject.gif', + tableName => 'GalleryAlbum', + className => __PACKAGE__, + properties => \%properties, + }; + + return $class->SUPER::definition($session, $definition); +} + +#---------------------------------------------------------------------------- + +=head2 addArchive ( filename, properties ) + +Add an archive of Files to this Album. C is the full path of the +archive. C is a hash reference of properties to assign to the +photos in the archive. + +Will croak if cannot read the archive or if the archive will extract itself to +a directory outside of the storage location. + +Will only handle file types handled by the parent Gallery. + +=cut + +sub addArchive { + my $self = shift; + my $filename = shift; + my $properties = shift; + my $gallery = $self->getParent; + + my $archive = Archive::Any->new( $filename ); + + croak "Archive will extract to directory outside of storage location!" + if $archive->is_naughty; + + my $tempdirName = tempdir( "WebGUI-Gallery-XXXXXXXX", TMPDIR => 1, CLEANUP => 1); + $archive->extract( $tempdirName ); + + # Get all the files in the archive + my @files; + my $wanted = sub { push @files, $File::Find::name }; + find( { + wanted => $wanted, + }, $tempdirName ); + + 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!" ); + $properties->{ className } = $class; + $properties->{ menuTitle } = $filename; + $properties->{ title } = $filename; + $properties->{ url } = $self->getUrl . "/" . $filename; + + my $asset = $self->addChild( $properties, undef, undef, { skipAutoCommitWorkflows => 1 } ); + $asset->setFile( $filePath ); + } + + my $versionTag = WebGUI::VersionTag->getWorking( $self->session ); + $versionTag->set({ + "workflowId" => $self->getParent->get("workflowIdCommit"), + }); + $versionTag->requestCommit; + + return; +} + +#---------------------------------------------------------------------------- + +=head2 appendTemplateVarsFileLoop ( vars, assetIds ) + +Append template vars for a file loop for the specified assetIds. C is +a hash reference to add the file loop to. C is an array reference +of assetIds for the loop. + +Returns the hash reference for convenience. + +=cut + +sub appendTemplateVarsFileLoop { + my $self = shift; + my $var = shift; + my $assetIds = shift; + my $session = $self->session; + + for my $assetId (@$assetIds) { + push @{$var->{file_loop}}, + WebGUI::Asset->newByDynamicClass($session, $assetId)->getTemplateVars; + } + + return $var; +} + +#---------------------------------------------------------------------------- + +=head2 canAddFile ( [userId] ) + +Returns true if the user can add a file to this album. C is a WebGUI +user ID. If no userId is passed, will check the current user. + +Users can add files to this album if they are the owner, or if +C is true and the Gallery allows them to add files. + +=cut + +sub canAddFile { + my $self = shift; + my $userId = shift || $self->session->user->userId; + my $gallery = $self->getParent; + + return 1 if $userId eq $self->get("ownerUserId"); + return 1 if $self->get("othersCanAdd") && $gallery->canAddFile( $userId ); +} + +#---------------------------------------------------------------------------- + +=head2 canComment ( [userId] ) + +Returns true if the user is allowed to comment on files in this Album. +C is a WebGUI user ID. If no userId is passed, will check the current +user. + +Users can comment on files if C is true and the parent Gallery +allows comments. + +=cut + +sub canComment { + my $self = shift; + my $userId = shift || $self->session->user->userId; + my $gallery = $self->getParent; + + return 0 if !$self->get("allowComments"); + + return $gallery->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 GalleryAlbum if they are the owner, or if they can edit +the Gallery parent. + +Also handles adding of child assets by calling C. + +=cut + +sub canEdit { + my $self = shift; + my $userId = shift || $self->session->user->userId; + my $gallery = $self->getParent; + my $form = $self->session->form; + + # Handle adding a photo + if ( $form->get("func") eq "add" ) { + return $self->canAddFile; + } + elsif ( $form->get("func") eq "editSave" && $form->get("className") eq __PACKAGE__ ) { + return $self->canAddFile; + } + else { + return 1 if $userId eq $self->get("ownerUserId"); + + return $gallery->canEdit($userId); + } +} + +#---------------------------------------------------------------------------- + +=head2 canView ( [userId] ) + +Returns true if the user can view this asset. C is a WebGUI user ID. +If no userId is given, checks the current user. + +Users can view this album if they can view the containing Gallery. + +NOTE: It may be possible to view a GalleryAlbum that has no public files. In +such cases, the GalleryAlbum will appear empty to unprivileged users. This is +not a bug. + +=cut + +sub canView { + my $self = shift; + my $userId = shift || $self->session->user->userId; + return $self->getParent->canView($userId); +} + +#---------------------------------------------------------------------------- + +=head2 i18n ( session ) + +Get a WebGUI::International object for this class. + +Can be called as a class method, in which case a WebGUI::Session object +must be passed in. + +NOTE: This method can NOT be inherited, due to a current limitation +in the i18n system. You must ALWAYS call this with C<__PACKAGE__> + +=cut + +sub i18n { + my $self = shift; + my $session = shift; + + return WebGUI::International->new($session, "Asset_GalleryAlbum"); +} + +#---------------------------------------------------------------------------- + +=head2 getAutoCommitWorkflowId ( ) + +Returns the workflowId of the Gallery's approval workflow. + +=cut + +sub getAutoCommitWorkflowId { + my $self = shift; + return $self->getParent->get("workflowIdCommit"); +} + +#---------------------------------------------------------------------------- + +=head2 getFileIds ( ) + +Gets an array reference of asset IDs for all the files in this album. + +=cut + +sub getFileIds { + my $self = shift; + my $gallery = $self->getParent; + + return $self->getLineage( ['descendants'], { } ); +} + +#---------------------------------------------------------------------------- + +=head2 getFilePaginator ( paginatorUrl ) + +Gets a WebGUI::Paginator for the files in this album. C is the +url to the current page that will be given to the paginator. + +=cut + +sub getFilePaginator { + my $self = shift; + my $url = shift || $self->getUrl; + + my $p = WebGUI::Paginator->new( $self->session, $url ); + $p->setDataByArrayRef( $self->getFileIds ); + + return $p; +} + +#---------------------------------------------------------------------------- + +=head2 getTemplateVars ( ) + +Gets template vars common to all views. + +=cut + +sub getTemplateVars { + my $self = shift; + my $session = $self->session; + my $gallery = $self->getParent; + my $var = $self->get; + my $owner = WebGUI::User->new( $session, $self->get("ownerUserId") ); + + # Permissions + $var->{ canAddFile } = $self->canAddFile; + $var->{ canEdit } = $self->canEdit; + + # Add some common template vars from Gallery + $gallery->appendTemplateVarsSearchForm( $var ); + $var->{ url_listAlbums } = $gallery->getUrl('func=listAlbums'); + $var->{ url_listAlbumsRss } = $gallery->getUrl('func=listAlbumsRss'); + $var->{ url_listFilesForCurrentUser } = $gallery->getUrl('func=listFilesForUser'); + $var->{ url_search } = $gallery->getUrl('func=search'); + + # Friendly URLs + $var->{ url } = $self->getUrl; + $var->{ url_addArchive } = $self->getUrl('func=addArchive'); + $var->{ url_addPhoto } = $self->getUrl("func=add;class=WebGUI::Asset::File::Image::Photo"); + $var->{ url_addNoClass } = $self->getUrl("func=add"); + $var->{ url_delete } = $self->getUrl("func=delete"); + $var->{ url_edit } = $self->getUrl("func=edit"); + $var->{ url_listFilesForOwner } = $gallery->getUrl("func=listFilesForUser;userId=".$var->{ownerUserId}); + $var->{ url_viewRss } = $self->getUrl("func=viewRss"); + $var->{ url_slideshow } = $self->getUrl("func=slideshow"); + $var->{ url_thumbnails } = $self->getUrl("func=thumbnails"); + + $var->{ fileCount } = $self->getChildCount; + $var->{ ownerUsername } = $owner->username; + $var->{ thumbnailUrl } = $self->getThumbnailUrl; + + return $var; +} + +#---------------------------------------------------------------------------- + +=head2 getThumbnailUrl ( ) + +Gets the URL for the thumbnail for this asset. If no asset is set, gets the +first child. + +NOTE: If the asset does not have a getThumbnailUrl method, this method will +return undef. + +=cut + +sub getThumbnailUrl { + my $self = shift; + my $asset = undef; + + if ( $self->get("assetIdThumbnail") ) { + $asset = WebGUI::Asset->newByDynamicClass( $self->session, $self->get("assetIdThumbnail") ); + } + elsif ( $self->getFirstChild ) { + $asset = $self->getFirstChild; + } + else { + return undef; + } + + if ( $asset->can("getThumbnailUrl") ) { + return $asset->getThumbnailUrl; + } + else { + return undef; + } +} + +#---------------------------------------------------------------------------- + +=head2 othersCanAdd ( ) + +Returns true if people other than the owner can add files to this album. + +=cut + +sub othersCanAdd { + my $self = shift; + return $self->get("othersCanAdd"); +} + +#---------------------------------------------------------------------------- + +=head2 prepareView ( ) + +See WebGUI::Asset::prepareView() for details. + +=cut + +sub prepareView { + my $self = shift; + $self->SUPER::prepareView(); + + my $templateId = $self->getParent->get("templateIdViewAlbum"); + + my $template + = WebGUI::Asset::Template->new($self->session, $templateId); + $template->prepare; + + $self->{_viewTemplate} = $template; +} + +#---------------------------------------------------------------------------- + +=head2 processStyle ( ) + +Gets the parent Gallery's style template + +=cut + +sub processStyle { + my $self = shift; + return $self->getParent->processStyle(@_); +} + +#---------------------------------------------------------------------------- + +=head2 processPropertiesFromFormPost ( ) + +Process the form to save the asset. Request approval from the Gallery's +approval workflow. + +=cut + +sub processPropertiesFromFormPost { + my $self = shift; + my $errors = $self->SUPER::processPropertiesFromFormPost || []; + + # Return if error + return $errors if @$errors; + + # Passes all checks + $self->requestAutoCommit; +} + +#---------------------------------------------------------------------------- + +=head2 view ( ) + +method called by the www_view method. Returns a processed template +to be displayed within the page style. + +=cut + +sub view { + my $self = shift; + my $session = $self->session; + my $var = $self->getTemplateVars; + + my $p = $self->getFilePaginator; + $p->appendTemplateVars( $var ); + $self->appendTemplateVarsFileLoop( $var, $p->getPageData ); + + return $self->processTemplate($var, undef, $self->{_viewTemplate}); +} + +#---------------------------------------------------------------------------- + +=head2 view_slideshow ( ) + +method called by the www_slideshow method. Returns a processed template to be +displayed within the page style. + +Show a slideshow of the GalleryAlbum's files. + +=cut + +sub view_slideshow { + my $self = shift; + my $session = $self->session; + my $var = $self->getTemplateVars; + + $self->appendTemplateVarsFileLoop( $var, $self->getFileIds ); + + return $self->processTemplate($var, $self->getParent->get("templateIdViewSlideshow")); +} + +#---------------------------------------------------------------------------- + +=head2 view_thumbnails ( ) + +method called by the www_thumbnails method. Returns a processed template to be +displayed within the page style. + +Shows all the thumbnails for this GalleryAlbum. In addition, shows details +about a specific thumbnail. + +=cut + +sub view_thumbnails { + my $self = shift; + my $session = $self->session; + my $var = $self->getTemplateVars; + + my $fileId = $session->form->get("fileId"); + + $self->appendTemplateVarsFileLoop( $var, $self->getFileIds ); + + # Process the file loop to add an additional URL + for my $file ( @{ $var->{file_loop} } ) { + $file->{ url_albumViewThumbnails } + = $self->getUrl('func=thumbnails;fileId=' . $file->{assetId}); + } + + # Add direct vars for the requested file + my $asset; + if ($fileId) { + $asset = WebGUI::Asset->newByDynamicClass( $session, $fileId ); + } + # If no fileId given or fileId does not exist + if (!$asset) { + $asset = $self->getFirstChild; + } + my %assetVars = %{ $asset->getTemplateVars }; + for my $key ( keys %assetVars ) { + $var->{ 'file_' . $key } = $assetVars{ $key }; + } + + return $self->processTemplate($var, $self->getParent->get("templateIdViewThumbnails")); +} + +#---------------------------------------------------------------------------- + +=head2 www_addArchive ( ) + +Show the form to add an archive of files to this gallery. + +=cut + +sub www_addArchive { + my $self = shift; + + return $self->session->privilege->insufficient unless $self->canAddFile; + + my $session = $self->session; + my $form = $self->session->form; + my $var = $self->getTemplateVars; + + $var->{ form_start } + = WebGUI::Form::formHeader( $session, { + action => $self->getUrl('func=addArchiveSave'), + }); + $var->{ form_end } + = WebGUI::Form::formFooter( $session ); + + $var->{ form_submit } + = WebGUI::Form::submit( $session, { + name => "submit", + value => "Submit", + }); + + $var->{ form_archive } + = WebGUI::Form::File( $session, { + name => "archive", + maxAttachments => 1, + value => ( $form->get("archive") ), + }); + + $var->{ form_keywords } + = WebGUI::Form::text( $session, { + name => "keywords", + value => ( $form->get("keywords") ), + }); + + $var->{ form_friendsOnly } + = WebGUI::Form::yesNo( $session, { + name => "friendsOnly", + value => ( $form->get("friendsOnly") ), + }); + + return $self->processStyle( + $self->processTemplate($var, $self->getParent->get("templateIdAddArchive")) + ); +} + +#----------------------------------------------------------------------------- + +=head2 www_addArchiveSave ( ) + +Process the form for adding an archive. + +=cut + +sub www_addArchiveSave { + my $self = shift; + + return $self->session->privilege->insufficient unless $self->canAddFile; + + my $session = $self->session; + my $form = $self->session->form; + my $i18n = __PACKAGE__->i18n( $session ); + my $properties = { + keywords => $form->get("keywords"), + friendsOnly => $form->get("friendsOnly"), + }; + + my $storageId = $form->get("archive", "File"); + my $storage = WebGUI::Storage->get( $session, $storageId ); + my $filename = $storage->getPath( $storage->getFiles->[0] ); + + $self->addArchive( $filename, $properties ); + + $storage->delete; + + return $self->processStyle( + sprintf $i18n->get('addArchive message'), $self->getUrl, + ); +} + +#----------------------------------------------------------------------------- + +=head2 www_delete ( ) + +Show the form to confirm deleting this album and all files inside of it. + +=cut + +sub www_delete { + my $self = shift; + + return $self->session->privilege->insufficient unless $self->canEdit; + + my $var = $self->getTemplateVars; + $var->{ url_yes } = $self->getUrl("func=deleteConfirm"); + + return $self->processStyle( + $self->processTemplate( $var, $self->getParent->get("templateIdDeleteAlbum") ) + ); +} + +#----------------------------------------------------------------------------- + +=head2 www_deleteConfirm ( ) + +Confirm deleting this album and all files inside of it. + +=cut + +sub www_deleteConfirm { + my $self = shift; + + return $self->session->privilege->insufficient unless $self->canEdit; + + my $gallery = $self->getParent; + + $self->purge; + + return $gallery->www_view; +} + +#---------------------------------------------------------------------------- + +=head2 www_edit ( ) + +Show the form to add / edit a GalleryAlbum asset. + +=cut + +sub www_edit { + my $self = shift; + my $session = $self->session; + my $form = $self->session->form; + my $var = $self->getTemplateVars; + my $i18n = __PACKAGE__->i18n($session); + + # Generate the form + if ($form->get("func") eq "add") { + $var->{ form_start } + = WebGUI::Form::formHeader( $session, { + action => $self->getParent->getUrl('func=editSave;assetId=new;class='.__PACKAGE__), + }); + } + else { + $var->{ form_start } + = WebGUI::Form::formHeader( $session, { + action => $self->getUrl('func=editSave'), + }); + } + $var->{ form_start } + .= WebGUI::Form::hidden( $session, { + name => "proceed", + value => "showConfirmation", + }); + + $var->{ form_end } + = WebGUI::Form::formFooter( $session ); + + $var->{ form_cancel } + = WebGUI::Form::button( $session, { + name => "cancel", + value => $i18n->get("cancel"), + extras => 'onclick="history.go(-1)"', + }); + + $var->{ form_submit } + = WebGUI::Form::submit( $session, { + name => "save", + value => $i18n->get("save"), + }); + + $var->{ form_title } + = WebGUI::Form::text( $session, { + name => "title", + value => $form->get("title") || $self->get("title"), + }); + + $var->{ form_description } + = WebGUI::Form::HTMLArea( $session, { + name => "description", + value => $form->get("description") || $self->get("description"), + }); + + # Generate the file loop + my $thumbnailUrl = $self->getThumbnailUrl; + $self->appendTemplateVarsFileLoop( $var, $self->getFileIds ); + for my $file ( @{ $var->{file_loop} } ) { + if ( $thumbnailUrl eq $file->{thumbnailUrl} ) { + $file->{ isAlbumThumbnail } = 1; + } + } + + return $self->processStyle( + $self->processTemplate( $var, $self->getParent->get("templateIdEditAlbum") ) + ); +} + +#----------------------------------------------------------------------------- + +=head2 www_editSave ( ) + +Save the asset edit form. Overridden to give a nice message when a photo or +album is added + +=cut + +sub www_editSave { + my $self = shift; + my $form = $self->session->form; + my $i18n = __PACKAGE__->i18n($self->session); + $self->SUPER::www_editSave; + + if ( $form->get("assetId") eq "new" ) { + return sprintf $i18n->get("addFile message"), $self->getUrl, + } + else { + return sprintf $i18n->get("save message"), $self->getUrl, + } +} + +#---------------------------------------------------------------------------- + +=head2 www_showConfirmation ( ) + +Shows the confirmation message after adding / editing a gallery album. +Provides links to view the album. + +=cut + +sub www_showConfirmation { + my $self = shift; + my $i18n = __PACKAGE__->i18n( $self->session ); + + my $output = sprintf $i18n->get('save message'), $self->getUrl; + + return $self->processStyle( + $output + ); +} + +#----------------------------------------------------------------------------- + +=head2 www_slideshow ( ) + +Show a slideshow-type view of this album. The slideshow itself is powered by +a javascript application in the template. + +=cut + +sub www_slideshow { + my $self = shift; + + return $self->session->privilege->insufficient unless $self->canView; + + return $self->processStyle( $self->view_slideshow ); +} + +#---------------------------------------------------------------------------- + +=head2 www_thumbnails ( ) + +Show the thumbnails for the album. + +=cut + +sub www_thumbnails { + my $self = shift; + + return $self->session->privilege->insufficient unless $self->canView; + + return $self->processStyle( $self->view_thumbnails ); +} + +#---------------------------------------------------------------------------- + +=head2 www_viewRss ( ) + +Display an RSS feed for this album. + +=cut + +sub www_viewRss { + my $self = shift; + + return $self->session->privilege->insufficient unless $self->canView; + + my $var = $self->getTemplateVars; + $self->appendTemplateVarsFileLoop( $var, $self->getFileIds ); + + # Fix URLs to be full URLs + for my $key ( qw( url url_viewRss ) ) { + $var->{ $key } = $self->session->url->getSiteURL . $var->{ $key }; + } + + # Process the file loop to add additional params + for my $file ( @{ $var->{file_loop} } ) { + # Fix URLs to be full URLs + for my $key ( qw( url ) ) { + $file->{ $key } = $self->session->url->getSiteURL . $file->{$key}; + } + + $file->{ rssDate } + = $self->session->datetime->epochToMail( $file->{creationDate} ); + } + + $self->session->http->setMimeType('text/xml'); + return $self->processTemplate( $var, $self->getParent->get('templateIdViewAlbumRss') ); +} + +1; diff --git a/lib/WebGUI/AssetLineage.pm b/lib/WebGUI/AssetLineage.pm index 06a7bd940..cbda377b5 100644 --- a/lib/WebGUI/AssetLineage.pm +++ b/lib/WebGUI/AssetLineage.pm @@ -15,6 +15,7 @@ package WebGUI::Asset; =cut use strict; +use Carp qw( croak ); =head1 NAME @@ -82,9 +83,9 @@ sub addChild { $self->session->db->commit; $properties->{assetId} = $id; $properties->{parentId} = $self->getId; - my $temp = WebGUI::Asset->newByPropertyHashRef($self->session,$properties); + my $temp = WebGUI::Asset->newByPropertyHashRef($self->session,$properties) || croak "Couldn't create a new $properties->{className} asset!"; $temp->{_parent} = $self; - my $newAsset = $temp->addRevision($properties,$now, {skipAutoCommitWorkflows=>$options->{skipAutoCommitWorkflows}}); + my $newAsset = $temp->addRevision($properties, $now, $options); $self->updateHistory("added child ".$id); $self->session->http->setStatus(201,"Asset Creation Successful"); return $newAsset; diff --git a/lib/WebGUI/AssetPackage.pm b/lib/WebGUI/AssetPackage.pm index 000188706..5ae3c2930 100644 --- a/lib/WebGUI/AssetPackage.pm +++ b/lib/WebGUI/AssetPackage.pm @@ -125,12 +125,16 @@ A hash reference containing the exported data. =cut sub importAssetData { - my $self = shift; - my $data = shift; - my $error = $self->session->errorHandler; - my $id = $data->{properties}{assetId}; - my $class = $data->{properties}{className}; - my $version = $data->{properties}{revisionDate}; + my $self = shift; + my $data = shift; + my $error = $self->session->errorHandler; + my $id = $data->{properties}{assetId}; + my $class = $data->{properties}{className}; + my $version = $data->{properties}{revisionDate}; + + # Load the class + WebGUI::Asset->loadModule( $self->session, $class ); + my $asset; my $assetExists = WebGUI::Asset->assetExists($self->session, $id, $class, $version); if ($assetExists) { # update an existing revision @@ -194,40 +198,40 @@ A reference to a WebGUI::Storage object that contains a webgui package file. =cut sub importPackage { - my $self = shift; - my $storage = shift; - my $decompressed = $storage->untar($storage->getFiles->[0]); - my %assets = (); - my $error = $self->session->errorHandler; - $error->info("Importing package."); - foreach my $file (sort(@{$decompressed->getFiles})) { - next unless ($decompressed->getFileExtension($file) eq "json"); - $error->info("Found data file $file"); - my $data = eval{ + my $self = shift; + my $storage = shift; + my $decompressed = $storage->untar($storage->getFiles->[0]); + my %assets = (); + my $error = $self->session->errorHandler; + $error->info("Importing package."); + foreach my $file (sort(@{$decompressed->getFiles})) { + next unless ($decompressed->getFileExtension($file) eq "json"); + $error->info("Found data file $file"); + my $data = eval{ local $JSON::UnMapping = 1; JSON::jsonToObj($decompressed->getFileContentsAsScalar($file)) }; - if ($@ || $data->{properties}{assetId} eq "" || $data->{properties}{className} eq "" || $data->{properties}{revisionDate} eq "") { - $error->error("package corruption: ".$@) if ($@); - return "corrupt"; - } - $error->info("Data file $file is valid and represents asset ".$data->{properties}{assetId}); - foreach my $storageId (@{$data->{storage}}) { - my $assetStorage = WebGUI::Storage->get($self->session, $storageId); - $decompressed->untar($storageId.".storage", $assetStorage); - } - my $asset = $assets{$data->{properties}{parentId}} || $self; - my $newAsset = $asset->importAssetData($data); + if ($@ || $data->{properties}{assetId} eq "" || $data->{properties}{className} eq "" || $data->{properties}{revisionDate} eq "") { + $error->error("package corruption: ".$@) if ($@); + return "corrupt"; + } + $error->info("Data file $file is valid and represents asset ".$data->{properties}{assetId}); + foreach my $storageId (@{$data->{storage}}) { + my $assetStorage = WebGUI::Storage->get($self->session, $storageId); + $decompressed->untar($storageId.".storage", $assetStorage); + } + my $asset = $assets{$data->{properties}{parentId}} || $self; + my $newAsset = $asset->importAssetData($data); $newAsset->importAssetCollateralData($data); - $assets{$newAsset->getId} = $newAsset; - } - if ($self->session->setting->get("autoRequestCommit")) { + $assets{$newAsset->getId} = $newAsset; + } + if ($self->session->setting->get("autoRequestCommit")) { if ($self->session->setting->get("skipCommitComments")) { WebGUI::VersionTag->getWorking($self->session)->requestCommit; } else { - $self->session->http->setRedirect($self->getUrl("op=commitVersionTag;tagId=".WebGUI::VersionTag->getWorking($self->session)->getId)); + $self->session->http->setRedirect($self->getUrl("op=commitVersionTag;tagId=".WebGUI::VersionTag->getWorking($self->session)->getId)); } - } + } return; } diff --git a/lib/WebGUI/AssetTrash.pm b/lib/WebGUI/AssetTrash.pm index cae344f8d..92f395f2e 100644 --- a/lib/WebGUI/AssetTrash.pm +++ b/lib/WebGUI/AssetTrash.pm @@ -15,6 +15,7 @@ package WebGUI::Asset; =cut use strict; +use WebGUI::Asset::Shortcut; =head1 NAME @@ -146,6 +147,16 @@ sub purge { } } + # Delete shortcuts to this asset + # Also publish any shortcuts to this asset that are in the trash + my $shortcuts + = WebGUI::Asset::Shortcut->getShortcutsForAssetId($self->session, $self->getId, { + returnObjects => 1, + }); + for my $shortcut ( @$shortcuts ) { + $shortcut->purge; + } + # gotta delete stuff we've exported unless ($options->{skipExported}) { $self->_invokeWorkflowOnExportedFiles($self->session->setting->get('purgeWorkflow'), 1); @@ -190,29 +201,40 @@ sub purge { =head2 trash ( ) -Removes asset from lineage, places it in trash state. The "gap" in the lineage is changed in state to trash-limbo. +Removes asset from lineage, places it in trash state. The "gap" in the +lineage is changed in state to trash-limbo. =cut sub trash { - my $self = shift; - return if ($self->getId eq $self->session->setting->get("defaultPage") || $self->getId eq $self->session->setting->get("notFoundPage")); - foreach my $asset ($self, @{$self->getLineage(['descendants'], {returnObjects => 1})}) { - $asset->_invokeWorkflowOnExportedFiles($self->session->setting->get('trashWorkflow'), 1); - } + my $self = shift; + return if ($self->getId eq $self->session->setting->get("defaultPage") || $self->getId eq $self->session->setting->get("notFoundPage")); + foreach my $asset ($self, @{$self->getLineage(['descendants'], {returnObjects => 1})}) { + $asset->_invokeWorkflowOnExportedFiles($self->session->setting->get('trashWorkflow'), 1); + } - my $db = $self->session->db; - $db->beginTransaction; - my $sth = $db->read("select assetId from asset where lineage like ?",[$self->get("lineage").'%']); - while (my ($id) = $sth->array) { - $db->write("delete from assetIndex where assetId=?",[$id]); - } - $db->write("update asset set state='trash-limbo' where lineage like ?",[$self->get("lineage").'%']); - $db->write("update asset set state='trash', stateChangedBy=?, stateChanged=? where assetId=?",[$self->session->user->userId, $self->session->datetime->time(), $self->getId]); - $db->commit; - $self->{_properties}{state} = "trash"; - $self->updateHistory("trashed"); - $self->purgeCache; + # Trash any shortcuts to this asset + my $shortcuts + = WebGUI::Asset::Shortcut->getShortcutsForAssetId($self->session, $self->getId, { returnObjects => 1}); + for my $shortcut ( @$shortcuts ) { + $shortcut->trash; + } + + # Raw database work is more efficient than $asset->update + my $db = $self->session->db; + $db->beginTransaction; + my $sth = $db->read("select assetId from asset where lineage like ?",[$self->get("lineage").'%']); + while (my ($id) = $sth->array) { + $db->write("delete from assetIndex where assetId=?",[$id]); + } + $db->write("update asset set state='trash-limbo' where lineage like ?",[$self->get("lineage").'%']); + $db->write("update asset set state='trash', stateChangedBy=?, stateChanged=? where assetId=?",[$self->session->user->userId, $self->session->datetime->time(), $self->getId]); + $db->commit; + + # Update ourselves since we didn't use update() + $self->{_properties}{state} = "trash"; + $self->updateHistory("trashed"); + $self->purgeCache; } require WebGUI::Workflow::Activity::DeleteExportedFiles; diff --git a/lib/WebGUI/AssetVersioning.pm b/lib/WebGUI/AssetVersioning.pm index 7eeff27d6..1c4797847 100644 --- a/lib/WebGUI/AssetVersioning.pm +++ b/lib/WebGUI/AssetVersioning.pm @@ -87,12 +87,12 @@ Posts) will know not to send them under certain conditions. sub addRevision { my $self = shift; my $properties = shift; - my $now = shift || $self->session->datetime->time(); - my $options = shift; - + my $now = shift || $self->session->datetime->time(); + my $options = shift; + my $autoCommitId = $self->getAutoCommitWorkflowId() unless ($options->{skipAutoCommitWorkflows}); - - my $workingTag + + my $workingTag = ($autoCommitId) ? WebGUI::VersionTag->create($self->session, {groupToUse=>'12', workflowId=>$autoCommitId}) : WebGUI::VersionTag->getWorking($self->session) @@ -112,8 +112,7 @@ sub addRevision { $self->session->user->userId, $workingTag->getId, $self->getId, - ] - ); + ]); foreach my $definition (@{$self->definition($self->session)}) { unless ($definition->{tableName} eq "assetData") { diff --git a/lib/WebGUI/Form.pm b/lib/WebGUI/Form.pm index d319da505..2b05168a5 100644 --- a/lib/WebGUI/Form.pm +++ b/lib/WebGUI/Form.pm @@ -15,6 +15,8 @@ package WebGUI::Form; =cut use strict; +use Carp qw( croak ); +use Scalar::Util qw( blessed ); use Tie::IxHash; use WebGUI::Asset; use WebGUI::Asset::RichEdit; @@ -92,7 +94,7 @@ sub formFooter { #------------------------------------------------------------------- -=head2 formHeader ( session, hashRef ) +=head2 formHeader ( session, options ) Returns a form header. @@ -100,7 +102,7 @@ Returns a form header. A reference to the current session. -=head3 hashRef +=head3 options A hash reference that contains one or more of the following parameters. @@ -108,6 +110,9 @@ A hash reference that contains one or more of the following parameters. The form action. Defaults to the current page. +NOTE: If the C contains a query string (?param=value), C +will translate the parameters into hidden form elements automatically. + =head4 method The form method. Defaults to "post". @@ -118,28 +123,36 @@ The form enctype. Defaults to "multipart/form-data". =head4 extras -If you want to add anything special to the form header like javascript actions or stylesheet info, then use this. +If you want to add anything special to the form header like javascript +actions or stylesheet info, then use this. =cut sub formHeader { - my $session = shift; - my $params = shift; - my $action = $params->{action} || $session->url->page(); - my $hidden; - if ($action =~ /\?/) { - my ($path,$query) = split(/\?/,$action); - $action = $path; - my @params = split(/\;/,$query); - foreach my $param (@params) { - $param =~ s/amp;(.*)/$1/; - my ($name,$value) = split(/\=/,$param); - $hidden .= hidden($session,{name=>$name,value=>$value}); - } - } - my $method = $params->{method} || "post"; - my $enctype = $params->{enctype} || "multipart/form-data"; - return '
{extras}.'>
'.$hidden; + my $session = shift; + my $params = shift || {}; + + croak "First parameter must be WebGUI::Session object" + unless blessed $session && $session->isa( "WebGUI::Session" ); + croak "Second parameter must be hash reference" + if ref $params ne "HASH"; + + my $action = exists $params->{ action } ? $params->{ action } : $session->url->page(); + my $method = exists $params->{ method } ? $params->{ method } : "post"; + my $enctype = exists $params->{ enctype } ? $params->{ enctype } : "multipart/form-data"; + + # Fix a query string in the action URL + my $hidden; + if ($action =~ /\?/) { + ($action, my $query) = split /\?/, $action, 2; + my @params = split /[&;]/, $query; + foreach my $param ( @params ) { + my ($name, $value) = split /=/, $param; + $hidden .= hidden( $session, { name => $name, value => $value } ); + } + } + + return '{extras}.'>
'.$hidden; } diff --git a/lib/WebGUI/Form/SelectRichEditor.pm b/lib/WebGUI/Form/SelectRichEditor.pm new file mode 100644 index 000000000..1093dda31 --- /dev/null +++ b/lib/WebGUI/Form/SelectRichEditor.pm @@ -0,0 +1,101 @@ +package WebGUI::Form::SelectRichEditor; + +=head1 LEGAL + + ------------------------------------------------------------------- + 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 + ------------------------------------------------------------------- + +=cut + +use strict; +use base 'WebGUI::Form::SelectBox'; +use WebGUI::International; + +=head1 NAME + +WebGUI::Form::SelectRichEditor + +=head1 DESCRIPTION + +Creates a select box to choose a Rich Text Editor asset. + +=head1 SEE ALSO + +This is a subclass of WebGUI::Form::SelectBox. + +=head1 METHODS + +The following methods are specifically available from this class. Check the superclass for additional methods. + +=cut + +#---------------------------------------------------------------------------- + +=head2 definition ( [ additionalTerms ] ) + +See the super class for additional details. + +=head3 additionalTerms + +The following additional parameters have been added via this sub class. + +=head4 defaultValue + +Defaults to the Post Rich Editor, the least-featured Rich Text Editor and the +one most likely to be selected by users of this form control. + +=cut + +sub definition { + my $class = shift; + my $session = shift; + my $definition = shift || []; + my $i18n = WebGUI::International->new($session); + push @{$definition}, { + formName => { + defaultValue => $i18n->get("475"), + }, + defaultValue => { + defaultValue => '', + }, + }; + return $class->SUPER::definition($session, $definition); +} + +#---------------------------------------------------------------------------- + +=head2 new + +Create a new WebGUI::Form::SelectRichEditor object and populate it with all +the available Rich Text Editor assets. + +=cut + +sub new { + my $class = shift; + my $self = $class->SUPER::new(@_); + + # Get all the RTEs available to this site + my $options + = $self->session->db->buildHashRef( q{ + SELECT DISTINCT(assetData.assetId), assetData.title + FROM asset, assetData + WHERE asset.className='WebGUI::Asset::RichEdit' + AND asset.assetId=assetData.assetId + ORDER BY assetData.title + }); + + $self->set( "options", $options ); + + return $self; +} + +1; + diff --git a/lib/WebGUI/Friends.pm b/lib/WebGUI/Friends.pm index 309d574e2..f86de822e 100644 --- a/lib/WebGUI/Friends.pm +++ b/lib/WebGUI/Friends.pm @@ -311,7 +311,7 @@ sub sendMessage { userId => $userId, sentBy => $myId, status => 'unread', - }); + }); } } diff --git a/lib/WebGUI/Help/Asset_Gallery.pm b/lib/WebGUI/Help/Asset_Gallery.pm new file mode 100644 index 000000000..d449681f2 --- /dev/null +++ b/lib/WebGUI/Help/Asset_Gallery.pm @@ -0,0 +1,229 @@ +package WebGUI::Help::Asset_Gallery; + +our $HELP = { + 'help searchForm' => { + title => 'help searchForm title', + body => 'help searchForm body', + variables => [ + { + name => 'searchForm_start', + description => 'helpvar searchForm_start', + }, + { + name => 'searchForm_end', + description => 'helpvar searchForm_end', + }, + { + name => 'searchForm_basicSearch', + description => 'helpvar searchForm_basicSearch', + }, + { + name => 'searchForm_title', + description => 'helpvar searchForm_title', + }, + { + name => 'searchForm_description', + description => 'helpvar searchForm_description', + }, + { + name => 'searchForm_keywords', + description => 'helpvar searchForm_keywords', + }, + { + name => 'searchForm_className', + description => 'helpvar searchForm_className', + }, + { + name => 'searchForm_creationDate_after', + description => 'helpvar searchForm_creationDate_after', + }, + { + name => 'searchForm_creationDate_before', + description => 'helpvar searchForm_creationDate_before', + }, + { + name => 'searchForm_submit', + description => 'helpvar searchForm_submit', + }, + ], + }, + + 'help common' => { + title => 'help common title', + body => 'help common body', + isa => [ + { + tag => 'help searchForm', + namespace => 'Asset_Gallery', + }, + ], + variables => [ + { + name => 'url_addAlbum', + description => 'helpvar url_addAlbum', + }, + { + name => 'url_listAlbums', + description => 'helpvar url_listAlbums', + }, + { + name => 'url_listAlbumsRss', + description => 'helpvar url_listAlbumsRss', + }, + { + name => 'url_listFilesForCurrentUser', + description => 'helpvar url_listFilesForCurrentUser', + }, + { + name => 'url_search', + description => 'helpvar url_search', + }, + { + name => 'canEdit', + description => 'helpvar canEdit', + }, + { + name => 'canAddFile', + description => 'helpvar canAddFile', + }, + ], + }, + + 'help listAlbums' => { + title => 'help listAlbums title', + body => 'help listAlbums body', + isa => [ + { + tag => 'help common', + namespace => 'Asset_Gallery', + }, + ], + variables => [ + { + name => 'albums', + description => 'helpvar albums', + }, + ], + related => [ + { + tag => 'help common', + namespace => 'Asset_GalleryAlbum', + }, + ], + }, + + 'help listAlbumsRss' => { + title => 'help listAlbumsRss title', + body => 'help listAlbumsRss body', + isa => [ + { + tag => 'help common', + namespace => 'Asset_Gallery', + }, + ], + variables => [ + { + name => 'albums', + description => 'helpvar albums rss', + variables => [ + { + name => 'rssDate', + description => 'helpvar rssDate', + }, + ], + }, + ], + related => [ + { + tag => 'help common', + namespace => 'Asset_GalleryAlbum', + }, + ], + }, + + 'help search' => { + title => 'help search title', + body => 'help search body', + isa => [ + { + tag => 'help common', + namespace => 'Asset_Gallery', + }, + ], + variables => [ + { + name => 'search_results', + description => 'helpvar search_results', + }, + ], + # All classes that can be found by a Gallery search go in here + related => [ + { + tag => 'help common', + namespace => 'Asset_GalleryAlbum', + }, + { + tag => 'help common', + namespace => 'Asset_Photo', + }, + ], + }, + + 'help listFilesForUser' => { + title => 'help listFilesForUser title', + body => 'help listFilesForUser body', + isa => [ + { + tag => 'help common', + namespace => 'Asset_Gallery', + }, + ], + variables => [ + { + name => 'user_albums', + description => 'helpvar user_albums', + }, + { + name => 'user_files', + description => 'helpvar user_files', + }, + { + name => 'userId', + description => 'helpvar userId', + }, + { + name => 'url_rss', + description => 'helpvar url_rss', + }, + { + name => 'username', + description => 'helpvar username', + }, + ], + # All classes that can be found by a Gallery search go in here + related => [ + { + tag => 'help common', + namespace => 'Asset_GalleryAlbum', + }, + { + tag => 'help common', + namespace => 'Asset_Photo', + }, + ], + }, + + 'help listFilesForUserRss' => { + title => 'help listFilesForUserRss title', + body => 'help listFilesForUserRss body', + isa => [ + { + tag => 'help listFilesForUser', + namespace => 'Asset_Gallery', + }, + ], + }, + +}; + +1; diff --git a/lib/WebGUI/Help/Asset_GalleryAlbum.pm b/lib/WebGUI/Help/Asset_GalleryAlbum.pm new file mode 100644 index 000000000..25cc4598a --- /dev/null +++ b/lib/WebGUI/Help/Asset_GalleryAlbum.pm @@ -0,0 +1,309 @@ +package WebGUI::Help::Asset_GalleryAlbum; + +our $HELP = { + + 'help common' => { + title => 'help common title', + body => 'help common body', + isa => [ + { + tag => 'help searchForm', + namespace => 'Asset_Gallery', + }, + ], + variables => [ + { + name => 'canAddFile', + description => 'helpvar canAddFile', + }, + { + name => 'canEdit', + description => 'helpvar canEdit', + }, + { + name => 'url_listAlbums', + description => 'helpvar url_listAlbums', + }, + { + name => 'url_listAlbumsRss', + description => 'helpvar url_listAlbumsRss', + }, + { + name => 'url_listFilesForCurrentUser', + description => 'helpvar url_listFilesForCurrentUser', + }, + { + name => 'url_search', + description => 'helpvar url_search', + }, + { + name => 'url_addArchive', + description => 'helpvar url_addArchive', + }, + { + name => 'url_addPhoto', + description => 'helpvar url_addPhoto', + }, + { + name => 'url_addNoClass', + description => 'helpvar url_addNoClass', + }, + { + name => 'url_delete', + description => 'helpvar url_delete', + }, + { + name => 'url_edit', + description => 'helpvar url_edit', + }, + { + name => 'url_listFilesForOwner', + description => 'helpvar url_listFilesForOwner', + }, + { + name => 'url_viewRss', + description => 'helpvar url_viewRss', + }, + { + name => 'url_slideshow', + description => 'helpvar url_slideshow', + }, + { + name => 'url_thumbnails', + description => 'helpvar url_thumbnails', + }, + { + name => 'fileCount', + description => 'helpvar fileCount', + }, + { + name => 'ownerUsername', + description => 'helpvar ownerUsername', + }, + { + name => 'thumbnailUrl', + description => 'helpvar thumbnailUrl', + }, + ], + }, + + 'help fileLoop' => { + title => 'help fileLoop title', + body => 'help fileLoop body', + variables => [ + { + name => 'file_loop', + description => 'helpvar file_loop', + }, + ], + + # ADD ALL GalleryAlbum FILE CLASSES HERE!!! + related => [ + { + tag => 'help common', + namespace => 'Asset_Photo', + }, + ], + }, + + 'help view' => { + title => 'help view title', + body => 'help view body', + isa => [ + { + tag => 'help common', + namespace => 'Asset_GalleryAlbum', + }, + { + tag => 'help fileLoop', + namespace => 'Asset_GalleryAlbum', + }, + ], + }, + + 'help slideshow' => { + title => 'help slideshow title', + body => 'help slideshow body', + isa => [ + { + tag => 'help common', + namespace => 'Asset_GalleryAlbum', + }, + { + tag => 'help fileLoop', + namespace => 'Asset_GalleryAlbum', + }, + ], + }, + + 'help thumbnails' => { + title => 'help thumbnails title', + body => 'help thumbnails body', + isa => [ + { + tag => 'help common', + namespace => 'Asset_GalleryAlbum', + }, + { + tag => 'help fileLoop', + namespace => 'Asset_GalleryAlbum', + }, + ], + + variables => [ + { + name => 'file_*', + description => 'helpvar file_*', + }, + ], + + # PUT ALL GalleryAlbum FILE CLASSES HERE ALSO!!! + related => [ + { + tag => 'help common', + namespace => 'Asset_Photo', + }, + ], + }, + + 'help addArchive' => { + title => 'help addArchive title', + body => 'help addArchive body', + isa => [ + { + tag => 'help common', + namespace => 'Asset_GalleryAlbum', + }, + ], + variables => { + { + name => 'form_start', + description => 'helpvar form_start', + required => 1, + }, + { + name => 'form_end', + description => 'helpvar form_end', + required => 1, + }, + { + name => 'form_submit', + description => 'helpvar form_submit', + }, + { + name => 'form_archive', + description => 'helpvar form_archive', + required => 1, + }, + { + name => 'form_keywords', + description => 'helpvar form_keywords', + }, + { + name => 'form_friendsOnly', + description => 'helpvar form_friendsOnly', + }, + }, + }, + + 'help delete' => { + title => 'help delete title', + body => 'help delete body', + isa => [ + { + tag => 'help common', + namespace => 'Asset_GalleryAlbum', + }, + ], + variables => [ + { + name => 'url_yes', + description => 'helpvar url_yes', + }, + ], + }, + + 'help edit' => { + title => 'help edit title', + body => 'help edit body', + isa => [ + { + tag => 'help common', + namespace => 'Asset_GalleryAlbum', + }, + { + tag => 'help fileLoop', + namespace => 'Asset_GalleryAlbum', + }, + ], + variables => [ + { + name => 'form_start', + description => 'helpvar form_start', + required => 1, + }, + { + name => 'form_end', + description => 'helpvar form_end', + required => 1, + }, + { + name => 'form_cancel', + description => 'helpvar form_cancel', + }, + { + name => 'form_submit', + description => 'helpvar form_submit', + }, + { + name => 'form_title', + description => 'helpvar form_title', + }, + { + name => 'form_description', + description => 'helpvar form_description', + required => 1, + }, + { + name => 'file_loop', + description => 'helpvar file_loop edit', + variables => [ + { + name => 'isAlbumThumbnail', + description => 'helpvar isAlbumThumbnail', + }, + ], + }, + ], + }, + + 'help viewRss' => { + title => 'help viewRss title', + body => 'help viewRss body', + isa => [ + { + tag => 'help common', + namespace => 'Asset_GalleryAlbum', + }, + { + tag => 'help fileLoop', + namespace => 'Asset_GalleryAlbum', + }, + ], + variables => [ + { + name => 'file_loop', + description => 'helpvar file_loop viewRss', + variables => [ + { + name => 'rssDate', + description => 'helpvar rssDate', + }, + ], + }, + ], + }, + +}; + +1; diff --git a/lib/WebGUI/Help/Asset_Photo.pm b/lib/WebGUI/Help/Asset_Photo.pm new file mode 100644 index 000000000..9a733ed42 --- /dev/null +++ b/lib/WebGUI/Help/Asset_Photo.pm @@ -0,0 +1,258 @@ +package WebGUI::Help::Asset_Photo; + +our $HELP = { + 'help commentForm' => { + title => 'help commentForm title', + body => 'help commentForm body', + variables => [ + { + name => 'commentForm_start', + description => 'helpvar commentForm_start', + }, + { + name => 'commentForm_end', + description => 'helpvar commentForm_end', + }, + { + name => 'commentForm_bodyText', + description => 'helpvar commentForm_bodyText', + }, + { + name => 'commentForm_submit', + description => 'helpvar commentForm_submit', + }, + ], + }, + + 'help common' => { + title => 'help common title', + body => 'help common body', + isa => [ + { + tag => 'help searchForm', + namespace => 'Asset_Gallery', + }, + { + tag => 'help commentForm', + namespace => 'Asset_Photo', + }, + ], + variables => [ + { + name => 'canComment', + description => 'helpvar canComment', + }, + { + name => 'canEdit', + description => 'helpvar canEdit', + }, + { + name => 'fileUrl', + description => 'helpvar fileUrl', + }, + { + name => 'numberOfComments', + description => 'helpvar numberOfComments', + }, + { + name => 'ownerUsername', + description => 'helpvar ownerUsername', + }, + { + name => 'thumbnailUrl', + description => 'helpvar thumbnailUrl', + }, + { + name => 'url_delete', + description => 'helpvar url_delete', + }, + { + name => 'url_demote', + description => 'helpvar url_demote', + }, + { + name => 'url_edit', + description => 'helpvar url_edit', + }, + { + name => 'url_gallery', + description => 'helpvar url_gallery', + }, + { + name => 'url_makeShortcut', + description => 'helpvar url_makeShortcut', + }, + { + name => 'url_listFilesForOwner', + description => 'helpvar url_listFilesForOwner', + }, + { + name => 'url_promote', + description => 'helpvar url_promote', + }, + { + name => 'resolutions_loop', + description => 'helpvar resolutions_loop', + variables => [ + { + name => 'url_download', + description => 'helpvar resolutions_loop url_download', + }, + ], + }, + { + name => 'exif_*', + description => 'helpvar exif_*', + }, + { + name => 'exifLoop', + description => 'helpvar exifLoop', + variables => [ + { + name => 'tag', + description => 'helpvar exifLoop tag', + }, + { + name => 'value', + description => 'helpvar exifLoop value', + }, + ], + }, + ], + }, + + 'help delete' => { + title => 'help delete title', + body => 'help delete body', + isa => [ + { + tag => 'help common', + namespace => 'Asset_Photo', + }, + ], + variables => [ + { + name => 'url_yes', + description => 'helpvar url_yes', + }, + ], + }, + + 'help edit' => { + title => 'help edit title', + body => 'htlp edit body', + variables => [ + { + name => 'url_addArchive', + description => 'helpvar url_addArchive', + }, + { + name => 'form_start', + description => 'helpvar form_start', + required => 1, + }, + { + name => 'form_end', + description => 'helpvar form_end', + required => 1, + }, + { + name => 'form_submit', + description => 'helpvar form_submit', + }, + { + name => 'form_title', + description => 'helpvar form_title', + }, + { + name => 'form_synopsis', + description => 'helpvar form_synopsis', + }, + { + name => 'form_photo', + description => 'helpvar form_photo', + }, + { + name => 'form_keywords', + description => 'helpvar form_keywords', + }, + { + name => 'form_location', + description => 'helpvar form_location', + }, + { + name => 'form_friendsOnly', + description => 'helpvar form_friendsOnly', + }, + ], + }, + + 'help makeShortcut' => { + title => 'help makeShortcut title', + body => 'htlp makeShortcut body', + variables => [ + { + name => 'form_start', + description => 'helpvar form_start', + required => 1, + }, + { + name => 'form_end', + description => 'helpvar form_end', + required => 1, + }, + { + name => 'form_parentId', + description => 'helpvar form_parentId', + required => 1, + }, + ], + } + 'help view' => { + title => 'help view title', + body => 'help view body', + isa => [ + { + tag => 'help common', + namespace => 'Asset_Photo', + }, + ], + variables => [ + { + name => 'commentLoop', + description => 'helpvar commentLoop', + variables => [ + { + name => 'userId', + description => 'helpvar commentLoop userId', + }, + { + name => 'visitorIp', + description => 'helpvar commentLoop visitorIp', + }, + { + name => 'creationDate', + description => 'helpvar commentLoop creationDate', + }, + { + name => 'bodyText', + description => 'helpvar commentLoop bodyText', + }, + { + name => 'username', + description => 'helpvar commentLoop username', + }, + ], + }, + { + name => 'commentLoop_pageBar', + description => 'helpvar commentLoop_pageBar', + }, + ], + }, + + +}; + +1; + diff --git a/lib/WebGUI/Keyword.pm b/lib/WebGUI/Keyword.pm index 3ae518bc7..a5fbc9adc 100644 --- a/lib/WebGUI/Keyword.pm +++ b/lib/WebGUI/Keyword.pm @@ -27,6 +27,9 @@ Package WebGUI::Keyword This package provides an API to create and modify keywords used by the asset sysetm. +Assets can use the C property to set keywords automatically. See +WebGUI::Asset::update() for more details. + =head1 SYNOPSIS use WebGUI::Keyword; diff --git a/lib/WebGUI/Search.pm b/lib/WebGUI/Search.pm index b19ec0316..138b9b80b 100644 --- a/lib/WebGUI/Search.pm +++ b/lib/WebGUI/Search.pm @@ -15,6 +15,7 @@ package WebGUI::Search; =cut use strict; +use Carp qw( croak ); use WebGUI::Asset; =head1 NAME @@ -329,6 +330,9 @@ This rule allows for an array reference of table join clauses. join => 'join assetData on assetId = assetData.assetId' +NOTE: This rule is deprecated and will be removed in a future release. Use +joinClass instead. + =head4 columns This rule allows for additional columns to be returned by getResultSet(). @@ -344,6 +348,11 @@ placeholders and parameters. sub search { my $self = shift; my $rules = shift; + + # Send the rules through some sanity checks + croak "'lineage' rule must be array reference" + if ( $rules->{lineage} && ref $rules->{lineage} ne "ARRAY" ); + my @params = (); my $query = ""; my @clauses = (); @@ -410,10 +419,31 @@ sub search { if ($rules->{where}) { push(@clauses, $rules->{where}); } - if ($rules->{join}) { # This join happens in _getQuery - $rules->{join} = [$rules->{join}] - unless (ref $rules->{join} eq "ARRAY"); - $self->{_join} = $rules->{join}; + # deal with custom joined tables if we must + if (exists $rules->{joinClass}) { + my $join = [ "left join assetData on assetIndex.assetId=assetData.assetId" ]; + for my $className ( @{ $rules->{ joinClass } } ) { + my $cmd = "use " . $className; + eval { $cmd }; + $self->session->errorHandler->fatal("Couldn't compile asset package: ".$className.". Root cause: ".$@) if ($@); + 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; + } + } + # Get only the latest revision + push @clauses, "assetData.revisionDate = (SELECT MAX(revisionDate) FROM assetData ad WHERE ad.assetId = assetData.assetId)"; + # Join happens in _getQuery + $self->{_join} = $join; + } + elsif ($rules->{join}) { # This join happens in _getQuery + $rules->{join} = [$rules->{join}] + unless (ref $rules->{join} eq "ARRAY"); + $self->{_join} = $rules->{join}; } if ($rules->{columns}) { $rules->{columns} = [$rules->{columns}] diff --git a/lib/WebGUI/Storage.pm b/lib/WebGUI/Storage.pm index 034917f47..2e4c0e06f 100644 --- a/lib/WebGUI/Storage.pm +++ b/lib/WebGUI/Storage.pm @@ -15,6 +15,8 @@ package WebGUI::Storage; =cut use Archive::Tar; +use Carp qw( croak ); +use Cwd; use File::Copy qw(cp); use FileHandle; use File::Find; @@ -24,7 +26,6 @@ use Storable qw(nstore retrieve); use strict; use warnings; use WebGUI::Utility; -use Carp; =head1 NAME @@ -64,6 +65,7 @@ This package provides a mechanism for storing and retrieving files that are not $newstore = $store->untar($filename); + $store->copyFile($filename, $newFilename); $store->delete; $store->deleteFile($filename); $store->rename($filename, $newFilename); @@ -202,6 +204,7 @@ sub addFileFromFormPost { my $filename; my $attachmentCount = 1; foreach my $upload ($self->session->request->upload($formVariableName)) { + $self->session->errorHandler->info("Trying to get " . $upload->filename); return $filename if $attachmentCount > $attachmentLimit; my $tempFilename = $upload->filename(); next unless $tempFilename; @@ -225,6 +228,7 @@ sub addFileFromFormPost { print $file $buffer; } close($file); + $self->session->errorHandler->info("Got ".$upload->filename); } else { $self->_addError("Couldn't open file ".$self->getPath($filename)." for writing due to error: ".$!); return; @@ -343,6 +347,31 @@ sub copy { #------------------------------------------------------------------- +=head2 copyFile ( filename, newFilename ) + +Copy a file in this storage location. C is the file to copy. +C is the new file to create. + +=cut + +sub copyFile { + my $self = shift; + my $filename = shift; + my $newFilename = shift; + + croak "Can't find '$filename' in storage location " . $self->getId + unless -e $self->getPath($filename); + croak "Second argument must be a filename" + unless $newFilename; + + cp( $self->getPath($filename), $self->getPath($newFilename) ) + || croak "Couldn't copy '$filename' to '$newFilename': $!"; + + return; +} + +#------------------------------------------------------------------- + =head2 create ( session ) Creates a new storage location on the file system. @@ -354,9 +383,9 @@ A reference to the current session; =cut sub create { - my $class = shift; - my $session = shift; - my $id = $session->id->generate(); + my $class = shift; + my $session = shift; + my $id = $session->id->generate(); #Determine whether or not to use case insensitive files my $config = $session->config; @@ -365,14 +394,16 @@ sub create { #$session->errorHandler->warn($caseInsensitive.": $id\n".Carp::longmess()."\n"); #For case insensitive operating systems, convert guid to hex - if($caseInsensitive) { + if ($caseInsensitive) { my $hexId = $session->id->toHex($id); $db->write("insert into storageTranslation (guidValue,hexValue) values (?,?)",[$id,$hexId]); } my $self = $class->get($session,$id); - $self->_makePath; - return $self; + $self->_makePath; + + $session->errorHandler->info("Created storage location $id as a $class"); + return $self; } @@ -446,6 +477,7 @@ sub delete { $db->write("delete from storageTranslation where guidValue=?",[$self->getId]); } } + $self->session->errorHandler->info("Deleted storage ".$self->getId); return; } @@ -753,6 +785,8 @@ Returns a full path to this storage location. If specified, we'll return a path to the file rather than the storage location. +NOTE: Does not check if the file exists. This is a feature. + =cut sub getPath { @@ -898,21 +932,22 @@ Pass in a storage location object to create the tar file in, instead of having a =cut sub tar { - my $self = shift; - my $filename = shift; - my $temp = shift || WebGUI::Storage->createTemp($self->session); + my $self = shift; + my $filename = shift; + my $temp = shift || WebGUI::Storage->createTemp($self->session); chdir $self->getPath or croak 'Unable to chdir to ' . $self->getPath . ": $!"; - my @files = (); - find(sub { push(@files, $File::Find::name)}, "."); - if ($Archive::Tar::VERSION eq '0.072') { - my $tar = Archive::Tar->new(); - $tar->add_files(@files); - $tar->write($temp->getPath($filename),1); - - } else { - Archive::Tar->create_archive($temp->getPath($filename),1,@files); - } - return $temp; + my @files = (); + find(sub { push(@files, $File::Find::name)}, "."); + if ($Archive::Tar::VERSION eq '0.072') { + my $tar = Archive::Tar->new(); + $tar->add_files(@files); + $tar->write($temp->getPath($filename),1); + + } + else { + Archive::Tar->create_archive($temp->getPath($filename),1,@files); + } + return $temp; } #------------------------------------------------------------------- @@ -932,12 +967,17 @@ Pass in a storage location object to extract the contents to, instead of having =cut sub untar { - my $self = shift; - my $filename = shift; - my $temp = shift || WebGUI::Storage->createTemp($self->session); + my $self = shift; + my $filename = shift; + my $temp = shift || WebGUI::Storage->createTemp($self->session); + + my $originalDir = cwd; chdir $temp->getPath; + Archive::Tar->extract_archive($self->getPath($filename),1); $self->_addError(Archive::Tar->error) if (Archive::Tar->error); + + chdir $originalDir; return $temp; } diff --git a/lib/WebGUI/Storage/Image.pm b/lib/WebGUI/Storage/Image.pm index 4e9ebedb3..008c75fae 100644 --- a/lib/WebGUI/Storage/Image.pm +++ b/lib/WebGUI/Storage/Image.pm @@ -52,11 +52,11 @@ use WebGUI::Storage::Image; These methods are available from this class: -my $boolean = $self->generateThumbnail($filename); -my $url = $self->getThumbnailUrl($filename); -my $boolean = $self->isImage($filename); -my ($captchaFile, $challenge) = $self->addFileFromCaptcha; -$self->resize($imageFile, $width, $height); + my $boolean = $self->generateThumbnail($filename); + my $url = $self->getThumbnailUrl($filename); + my $boolean = $self->isImage($filename); + my ($captchaFile, $challenge) = $self->addFileFromCaptcha; + $self->resize($imageFile, $width, $height); =cut @@ -313,7 +313,7 @@ sub getThumbnailUrl { return ''; } if (! isIn($filename, @{ $self->getFiles() })) { - $self->session->errorHandler->error("Can't make a thumbnail for a file that is not in my storage location."); + $self->session->errorHandler->error("Can't make a thumbnail for a file named '$filename' that is not in my storage location."); return ''; } return $self->getUrl("thumb-".$filename); @@ -360,41 +360,42 @@ The new height of the image in pixels. =cut sub resize { - my $self = shift; - my $filename = shift; - my $width = shift; - my $height = shift; - unless (defined $filename) { - $self->session->errorHandler->error("Can't resize when you haven't specified a file."); - return 0; - } - unless ($self->isImage($filename)) { - $self->session->errorHandler->error("Can't resize something that's not an image."); - return 0; - } - unless ($width || $height) { - $self->session->errorHandler->error("Can't resize with no resizing parameters."); - return 0; - } - my $image = $graphicsPackage->new; - my $error = $image->Read($self->getPath($filename)); - if ($error) { - $self->session->errorHandler->error("Couldn't read image for resizing: ".$error); - return 0; - } - my ($x, $y) = $image->Get('width','height'); - if ($width && !$height) { # proportional scale by width - $height = $width / $x * $y; - } elsif (!$width && $height) { # proportional scale by height - $width = $height * $x / $y; - } - $image->Scale(width=>$width, height=>$height); - $error = $image->Write($self->getPath($filename)); - if ($error) { - $self->session->errorHandler->error("Couldn't create thumbnail: ".$error); - return 0; - } - return 1; + my $self = shift; + my $filename = shift; + my $width = shift; + my $height = shift; + unless (defined $filename) { + $self->session->errorHandler->error("Can't resize when you haven't specified a file."); + return 0; + } + unless ($self->isImage($filename)) { + $self->session->errorHandler->error("Can't resize something that's not an image."); + return 0; + } + unless ($width || $height) { + $self->session->errorHandler->error("Can't resize with no resizing parameters."); + return 0; + } + my $image = $graphicsPackage->new; + my $error = $image->Read($self->getPath($filename)); + if ($error) { + $self->session->errorHandler->error("Couldn't read image for resizing: ".$error); + return 0; + } + my $geometry; + if (!$width || !$height) { + $geometry = $width || $height; + } + else { + $geometry = $width . "x" . $height; + } + $image->Resize( geometry => $geometry, filter => "lanczos" ); + $error = $image->Write($self->getPath($filename)); + if ($error) { + $self->session->errorHandler->error("Couldn't resize image: ".$error); + return 0; + } + return 1; } diff --git a/lib/WebGUI/i18n/English/Asset_Gallery.pm b/lib/WebGUI/i18n/English/Asset_Gallery.pm new file mode 100644 index 000000000..a93676cf9 --- /dev/null +++ b/lib/WebGUI/i18n/English/Asset_Gallery.pm @@ -0,0 +1,558 @@ +package WebGUI::i18n::English::Asset_Gallery; + +our $I18N = { + 'assetName' => { + message => 'Gallery', + lastUpdated => 1131394072, + }, + "groupIdAddComment label" => { + message => "Group to Add Comments", + lastUpdated => 0, + context => '', + }, + "groupIdAddComment description" => { + message => "The group that is allowed to add comments to files.", + lastUpdated => 0, + context => '', + }, + "groupIdAddFile label" => { + message => "Group to Add Files", + lastUpdated => 0, + context => '', + }, + "groupIdAddFile description" => { + message => "The group that is allowed to add files and albums to this gallery", + lastUpdated => 0, + context => '', + }, + "groupIdModerator label" => { + message => "Group to Moderate Comments", + lastUpdated => 0, + context => '', + }, + "groupIdModerator description" => { + message => "The group that is allowed to edit / delete comments in this gallery", + lastUpdated => 0, + context => '', + }, + "imageResolutions label" => { + message => "Image Resolutions", + lastUpdated => 0, + context => '', + }, + "imageResolutions description" => { + message => "The sizes of images available for download.", + lastUpdated => 0, + context => '', + }, + "imageViewSize label" => { + message => "Image View Size", + lastUpdated => 0, + context => '', + }, + "imageViewSize description" => { + message => "The size for images in the gallery. Will default to the Image Size + in the site settings.", + lastUpdated => 0, + context => '', + }, + "imageThumbnailSize label" => { + message => "Image Thumbnail Size", + lastUpdated => 0, + context => '', + }, + "imageThumbnailSize description" => { + message => "The size for thumbnails of images in the gallery. Will default to the + Thumbnail Size in the site settings.", + lastUpdated => 0, + context => '', + }, + "maxSpacePerUser label" => { + message => "Max Disk Space Per User", + lastUpdated => 0, + context => '', + }, + "maxSpacePerUser description" => { + message => "The maximum amount of disk space a user is allowed to use in this Gallery.", + lastUpdated => 0, + context => '', + }, + "richEditIdFileComment label" => { + message => "Rich Editor for Comments", + lastUpdated => 0, + context => '', + }, + "richEditIdFileComment description" => { + message => "The Rich Text Editor to use for comments", + lastUpdated => 0, + context => '', + }, + 'search class galleryalbum' => { + message => 'Album', + lastUpdated => 0, + context => 'Asset name for WebGUI::Asset::Wobject::GalleryAlbum', + }, + 'search class any' => { + message => 'Any', + lastUpdated => 0, + context => 'Label to not restrict gallery search by class', + }, + 'search class photo' => { + message => "Photo", + lastUpdated => 0, + context => 'Asset name for WebGUI::Asset::File::Image::Photo class', + }, + "search submit" => { + message => "Search", + lastUpdated => 0, + context => 'Label for button to submit search form', + }, + "templateIdAddArchive label" => { + message => "Template to Add Multiple", + lastUpdated => 0, + context => '', + }, + "templateIdAddArchive description" => { + message => "Display the form to add an archive of files to the gallery.", + lastUpdated => 0, + context => '', + }, + "templateIdDeleteAlbum label" => { + message => "Template to Delete Albums", + lastUpdated => 0, + context => '', + }, + "templateIdDeleteAlbum description" => { + message => "Display the confirmation to delete an album from the gallery.", + lastUpdated => 0, + context => '', + }, + "templateIdDeleteFile label" => { + message => "Template to Delete Files", + lastUpdated => 0, + context => '', + }, + "templateIdDeleteFile description" => { + message => "Display the confirmation to delete a file from the gallery.", + lastUpdated => 0, + context => '', + }, + "templateIdEditAlbum label" => { + message => "Template to Edit Albums", + lastUpdated => 0, + context => '', + }, + "templateIdEditAlbum description" => { + message => "The template to add / edit an album.", + lastUpdated => 0, + context => '', + }, + "templateIdEditFile label" => { + message => "Template to Edit Files", + lastUpdated => 0, + context => '', + }, + "templateIdEditFile description" => { + message => "The template to add / edit a file.", + lastUpdated => 0, + context => '', + }, + "templateIdListAlbums label" => { + message => "Template to List Albums", + lastUpdated => 0, + context => '', + }, + "templateIdListAlbums description" => { + message => "Template to show a list of albums in the gallery.", + lastUpdated => 0, + context => '', + }, + "templateIdListAlbumsRss label" => { + message => "Template to List Albums RSS", + lastUpdated => 0, + context => '', + }, + "templateIdListAlbumsRss description" => { + message => "Template to show an RSS feed of the albums in this gallery.", + lastUpdated => 0, + context => '', + }, + "templateIdMakeShortcut label" => { + message => "Template to Cross Post Files", + lastUpdated => 0, + context => '', + }, + "templateIdMakeShortcut description" => { + message => "Display the form to copy an image to another album.", + lastUpdated => 0, + context => '', + }, + "templateIdSearch label" => { + message => "Template to Search", + lastUpdated => 0, + context => '', + }, + "templateIdSearch description" => { + message => "Display the form to search the gallery. Display search results.", + lastUpdated => 0, + context => '', + }, + "templateIdViewSlideshow label" => { + message => "Template for Slideshow", + lastUpdated => 0, + context => '', + }, + "templateIdViewSlideshow description" => { + message => "Display all the images in an album as a slideshow.", + lastUpdated => 0, + context => '', + }, + "templateIdViewThumbnails label" => { + message => "Template for Thumbnails", + lastUpdated => 0, + context => '', + }, + "templateIdViewThumbnails description" => { + message => "Display all the images in an album as their thumbnails", + lastUpdated => 0, + context => '', + }, + "templateIdListFilesForUser label" => { + message => "Template to List Files for User", + lastUpdated => 0, + context => '', + }, + "templateIdListFilesForUser description" => { + message => "Display all the files and albums for a specific user.", + lastUpdated => 0, + context => '', + }, + "templateIdListFilesForUserRss label" => { + message => "Template to List Files for User RSS", + lastUpdated => 0, + context => '', + }, + "templateIdListFilesForUserRss description" => { + message => "RSS feed for all the files for a specific user.", + lastUpdated => 0, + context => '', + }, + "templateIdViewAlbum label" => { + message => "Template to View Album", + lastUpdated => 0, + context => '', + }, + "templateIdViewAlbum description" => { + message => "Default view for albums", + lastUpdated => 0, + context => '', + }, + "templateIdViewAlbumRss label" => { + message => "Template to View Album RSS", + lastUpdated => 0, + context => '', + }, + "templateIdViewAlbumRss description" => { + message => "RSS feed for a single album", + lastUpdated => 0, + context => '', + }, + "templateIdViewFile label" => { + message => "Template to View a File", + lastUpdated => 0, + context => '', + }, + "templateIdViewFile description" => { + message => "Show the details and comments for a specific file", + lastUpdated => 0, + context => '', + }, + "viewDefault label" => { + message => "Default View", + lastUpdated => 0, + context => '', + }, + "viewDefault description" => { + message => "Select the default view when a user enters the gallery.", + lastUpdated => 0, + context => '', + }, + "viewDefault option list" => { + message => "List Albums", + lastUpdated => 0, + context => '', + }, + "viewDefault option album" => { + message => "Single Album", + lastUpdated => 0, + context => '', + }, + "viewAlbumAssetId label" => { + message => "Default View Album", + lastUpdated => 0, + context => '', + }, + "viewAlbumAssetId description" => { + message => "The album to view when the default view is 'Album'", + lastUpdated => 0, + context => '', + }, + "viewListOrderBy label" => { + message => "List Albums Order By", + lastUpdated => 0, + context => '', + }, + "viewListOrderBy description" => { + message => "The field to order the album list by", + lastUpdated => 0, + context => '', + }, + "viewListOrderBy option creationDate" => { + message => "Creation Date", + lastUpdated => 0, + context => '', + }, + "viewListOrderBy option lineage" => { + message => "Sequence Number", + lastUpdated => 0, + context => 'Label to order by sequence (as in asset manager)', + }, + "viewListOrderBy option revisionDate" => { + message => "Revision Date", + lastUpdated => 0, + context => '', + }, + "viewListOrderBy option title" => { + message => "Title", + lastUpdated => 0, + context => '', + }, + "viewListOrderDirection label" => { + message => "List Albums Direction", + lastUpdated => 0, + context => '', + }, + "viewListOrderDirection description" => { + message => "The direction to order the album list", + lastUpdated => 0, + context => '', + }, + "viewListOrderDirection option asc" => { + message => "Ascending", + lastUpdated => 0, + context => 'Label for sorting in ascending order', + }, + "viewListOrderDirection option desc" => { + message => "Descending", + lastUpdated => 0, + context => 'Label for sorting in descending order', + }, + "workflowIdCommit label" => { + message => "Approval Workflow", + lastUpdated => 0, + context => '', + }, + "workflowIdCommit description" => { + message => "Workflow to approve new Files.", + lastUpdated => 0, + context => '', + }, + + 'helpvar searchForm_start' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar searchForm_end' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar searchForm_basicSearch' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar searchForm_title' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar searchForm_description' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar searchForm_keywords' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar searchForm_className' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar searchForm_creationDate_after' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar searchForm_creationDate_before' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar searchForm_submit' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar url_addAlbum' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar url_listAlbums' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar url_listAlbumsRss' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar url_listFilesForCurrentUser' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar url_search' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar canEdit' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar canAddFile' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar albums' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar albums rss' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar rssDate' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar search_results' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar user_albums' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar user_files' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar userId' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar url_rss' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar username' => { + message => '', + lastUpdated => 0, + }, + + 'help searchForm title' => { + message => '', + lastUpdated => 0, + }, + + 'help searchForm body' => { + message => '', + lastUpdated => 0, + }, + + 'help common title' => { + message => '', + lastUpdated => 0, + }, + + 'help common body' => { + message => '', + lastUpdated => 0, + }, + + 'help listAlbums title' => { + message => '', + lastUpdated => 0, + }, + + 'help listAlbums body' => { + message => '', + lastUpdated => 0, + }, + + 'help listAlbumsRss title' => { + message => '', + lastUpdated => 0, + }, + + 'help search body' => { + message => '', + lastUpdated => 0, + }, + + 'help search title' => { + message => '', + lastUpdated => 0, + }, + + 'help listFilesForUser title' => { + message => '', + lastUpdated => 0, + }, + + 'help listFilesForUser body' => { + message => '', + lastUpdated => 0, + }, + + 'help listFilesForUserRss title' => { + message => '', + lastUpdated => 0, + }, + + 'help listFilesForUserRss body' => { + message => '', + lastUpdated => 0, + }, + +}; + +1; diff --git a/lib/WebGUI/i18n/English/Asset_GalleryAlbum.pm b/lib/WebGUI/i18n/English/Asset_GalleryAlbum.pm new file mode 100644 index 000000000..7724c47fc --- /dev/null +++ b/lib/WebGUI/i18n/English/Asset_GalleryAlbum.pm @@ -0,0 +1,299 @@ +package WebGUI::i18n::English::Asset_GalleryAlbum; + +our $I18N = { + 'addArchive message' => { + message => 'Your files have been submitted for approval and commit. Return to Album', + lastUpdated => 0, + }, + 'assetName' => { + message => 'Gallery Album', + lastUpdated => 1131394072, + }, + 'cancel' => { + message => "Cancel", + lastUpdated => 0, + context => "Label for Cancel button", + }, + 'save' => { + message => "Save", + lastUpdated => 0, + context => "Label for Save button", + }, + 'save message' => { + message => 'Album settings saved. Return to Album', + lastUpdated => 0, + }, + + 'help common title' => { + message => '', + lastUpdated => 0, + }, + + 'help common body' => { + message => '', + lastUpdated => 0, + }, + + 'help fileLoop title' => { + message => '', + lastUpdated => 0, + }, + + 'help fileLoop body' => { + message => '', + lastUpdated => 0, + }, + + 'help view title' => { + message => '', + lastUpdated => 0, + }, + + 'help view body' => { + message => '', + lastUpdated => 0, + }, + + 'help slideshow title' => { + message => '', + lastUpdated => 0, + }, + + 'help slideshow body' => { + message => '', + lastUpdated => 0, + }, + + 'help thumbnails title' => { + message => '', + lastUpdated => 0, + }, + + 'help thumbnails body' => { + message => '', + lastUpdated => 0, + }, + + 'help addArchive title' => { + message => '', + lastUpdated => 0, + }, + + 'help addArchive body' => { + message => '', + lastUpdated => 0, + }, + + 'help delete title' => { + message => '', + lastUpdated => 0, + }, + + 'help delete body' => { + message => '', + lastUpdated => 0, + }, + + 'help edit title' => { + message => '', + lastUpdated => 0, + }, + + 'help viewRss title' => { + message => '', + lastUpdated => 0, + }, + + 'help viewRss body' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar canAddFile' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar canEdit' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar url_listAlbums' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar url_listAlbumsRss' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar url_listFilesForCurrentUser' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar url_search' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar url_addArchive' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar url_addPhoto' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar url_addNoClass' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar url_delete' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar url_edit' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar url_listFilesForOwner' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar url_viewRss' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar url_slideshow' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar url_thumbnails' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar fileCount' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar ownerUsername' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar thumbnailUrl' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar file_loop' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar file_*' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar form_start' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar form_end' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar form_submit' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar form_archive' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar form_keywords' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar form_friendsOnly' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar url_yes' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar form_start' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar form_end' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar form_cancel' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar form_submit' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar form_title' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar form_description' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar file_loop edit' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar isAlbumThumbnail' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar file_loop viewRss' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar rssDate' => { + message => '', + lastUpdated => 0, + }, + +}; + +1; diff --git a/lib/WebGUI/i18n/English/Asset_Photo.pm b/lib/WebGUI/i18n/English/Asset_Photo.pm new file mode 100644 index 000000000..bca896348 --- /dev/null +++ b/lib/WebGUI/i18n/English/Asset_Photo.pm @@ -0,0 +1,291 @@ +package WebGUI::i18n::English::Asset_Photo; + +our $I18N = { + 'assetName' => { + message => q{Photo}, + lastUpdated => 0, + }, + + 'delete message' => { + message => q{The photo has been deleted. Return to Album}, + lastUpdated => 0, + }, + + 'save message' => { + message => q{Your photo has been submitted for approval and commit. View Photo. Add another photo.}, + lastUpdated => 0, + }, + + 'help commentForm title' => { + message => 'Photo -- Comment Form', + lastUpdated => 0, + }, + + 'help commentForm body' => { + message => 'These template variables make up the form to allow users to post comments on Photos', + lastUpdated => 0, + }, + + 'help common title' => { + message => 'Photo -- Common', + lastUpdated => 0, + }, + + 'help common body' => { + message => 'These template variables are shared by all views of the Photo asset.', + lastUpdated => 0, + }, + + 'help edit title' => { + message => 'Photo -- Edit Form', + lastUpdated => 0, + }, + + 'help edit body' => { + message => 'These template variables make up the form to add / edit Photo assets', + lastUpdated => 0, + }, + + 'help makeShortcut title' => { + message => 'Photo -- Make Shortcut Form', + lastUpdated => 0, + }, + + 'help makeShortcut body' => { + message => 'These template variables make up the form to cross-post Photo assets', + lastUpdated => 0, + }, + + 'help view title' => { + message => 'Photo -- Normal View', + lastUpdated => 0, + }, + + 'help view body' => { + message => 'These template variables make up the normal view of Photo assets', + lastUpdated => 0, + }, + + 'helpvar commentForm_start' => { + message => 'Begin the comment form', + lastUpdated => 0, + }, + + 'helpvar commentForm_end' => { + message => 'End the comment form', + lastUpdated => 0, + }, + + 'helpvar commentForm_bodyText' => { + message => 'The body of the comment. A rich editor as configured by the parent Gallery.', + lastUpdated => 0, + }, + + 'helpvar commentForm_submit' => { + message => 'Submit the comment form', + lastUpdated => 0, + }, + + 'helpvar canComment' => { + 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', + lastUpdated => 0, + }, + + 'helpvar fileUrl' => { + message => 'The URL to the normal-sized photo', + lastUpdated => 0, + }, + + 'helpvar numberOfComments' => { + message => 'The total number of comments on this photo', + lastUpdated => 0, + }, + + 'helpvar ownerUsername' => { + message => 'The username of the user who posted this photo', + lastUpdated => 0, + }, + + 'helpvar thumbnailUrl' => { + message => 'The URL to the thumbnail of this photo', + lastUpdated => 0, + }, + + 'helpvar url_delete' => { + message => 'The URL to delete this photo.', + lastUpdated => 0, + }, + + 'helpvar url_demote' => { + 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', + lastUpdated => 0, + }, + + 'helpvar url_gallery' => { + message => 'The URL to the Gallery that contains this photo.', + lastUpdated => 0, + }, + + 'helpvar url_makeShortcut' => { + message => 'The URL to make a shortcut to this photo.', + lastUpdated => 0, + }, + + 'helpvar url_listFilesForOwner' => { + 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', + lastUpdated => 0, + }, + + 'helpvar resolutions_loop' => { + message => 'The available resolutions this photo has for download.', + lastUpdated => 0, + }, + + 'helpvar resolutions_loop url_download' => { + message => 'The URL to the resolution to download.', + lastUpdated => 0, + }, + + 'helpvar exif_*' => { + message => 'Each EXIF tag can be referenced by name.', + lastUpdated => 0, + }, + + 'helpvar exifLoop' => { + message => 'A loop of EXIF tags', + lastUpdated => 0, + }, + + 'helpvar exifLoop tag' => { + message => 'The name of the EXIF tag', + lastUpdated => 0, + }, + + 'helpvar exifLoop value' => { + message => 'The value of the EXIF tag', + lastUpdated => 0, + }, + + 'helpvar url_addArchive' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar form_start' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar form_end' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar form_submit' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar form_title' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar form_synopsis' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar form_photo' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar form_keywords' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar form_location' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar form_friendsOnly' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar form_start' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar form_end' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar form_parentId' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar commentLoop' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar commentLoop userId' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar commentLoop visitorIp' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar commentLoop creationDate' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar commentLoop bodyText' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar commentLoop username' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar commentLoop_pageBar' => { + message => '', + lastUpdated => 0, + }, + + 'helpvar url_yes' => { + message => '', + lastUpdated => 0, + }, + +}; + +1; diff --git a/sbin/testEnvironment.pl b/sbin/testEnvironment.pl index 8f60865af..d6006238d 100644 --- a/sbin/testEnvironment.pl +++ b/sbin/testEnvironment.pl @@ -122,6 +122,8 @@ checkModule("Locale::US"); checkModule("Weather::Com::Finder","0.5.1"); checkModule("Class::InsideOut","1.06"); checkModule("HTML::TagCloud","0.34"); +checkModule("Image::ExifTool","7.00"); +checkModule("Archive::Any","0.093"); ################################### @@ -200,8 +202,8 @@ print "\nTesting complete!\n\n"; #---------------------------------------- sub checkModule { my $module = shift; - my $version = shift || 0; - my $skipInstall = shift; + my $version = shift || 0; + my $skipInstall = shift; my $afterinstall = shift; unless (defined $afterinstall) { $afterinstall = 0; } printTest("Checking for module $module"); diff --git a/t/Asset/File.t b/t/Asset/File.t index c3db89e46..74e524216 100644 --- a/t/Asset/File.t +++ b/t/Asset/File.t @@ -26,6 +26,8 @@ use Test::More; # increment this value for each test you create use Test::Deep; plan tests => 9; +#TODO: This script tests certain aspects of WebGUI::Storage and it should not + my $session = WebGUI::Test->session; ##Create a storage location diff --git a/t/Asset/File/Image/Photo/00base.t b/t/Asset/File/Image/Photo/00base.t new file mode 100644 index 000000000..e1d229aab --- /dev/null +++ b/t/Asset/File/Image/Photo/00base.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 the creation and deletion of photo assets + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; + +#---------------------------------------------------------------------------- +# Init +my $session = WebGUI::Test->session; +my $node = WebGUI::Asset->getImportNode($session); +my $versionTag = WebGUI::VersionTag->getWorking($session); +$versionTag->set({name=>"Photo Test"}); +print "hi"; +my $gallery + = $node->addChild({ + className => "WebGUI::Asset::Wobject::Gallery", + }); +my $album + = $gallery->addChild({ + className => "WebGUI::Asset::Wobject::GalleryAlbum", + }); + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + +#---------------------------------------------------------------------------- +# Tests +plan tests => 5; + +#---------------------------------------------------------------------------- +# Test module compiles okay +# plan tests => 1 +use_ok("WebGUI::Asset::File::Image::Photo"); + +#---------------------------------------------------------------------------- +# Test creating a photo +my $photo + = $album->addChild({ + className => "WebGUI::Asset::File::Image::Photo", + }); + +is( + blessed $photo, "WebGUI::Asset::File::Image::Photo", + "Photo is a WebGUI::Asset::File::Image::Photo object", +); + +isa_ok( + $photo, "WebGUI::Asset::File::Image", +); + +is( + blessed $photo->getGallery, "WebGUI::Asset::Wobject::Gallery", + "Photo->getGallery gets the gallery containing this photo", +); + +#---------------------------------------------------------------------------- +# Test deleting a photo +my $properties = $photo->get; +$photo->purge; + +is( + WebGUI::Asset->newByDynamicClass($session, $properties->{assetId}), undef, + "Photo no longer able to be instanciated", +); + diff --git a/t/Asset/File/Image/Photo/ajax.t b/t/Asset/File/Image/Photo/ajax.t new file mode 100644 index 000000000..cf1ebbe72 --- /dev/null +++ b/t/Asset/File/Image/Photo/ajax.t @@ -0,0 +1,53 @@ +#------------------------------------------------------------------- +# 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 +#------------------------------------------------------------------- + +# The goal of this test is to test the AJAX methods of the Photo asset + +use FindBin; +use strict; +use lib "$FindBin::Bin/../../../../lib"; + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; +use WebGUI::Test::Maker::HTML; +use WebGUI::Asset::File::Image::Photo; + +#---------------------------------------------------------------------------- +# Init +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::PhotoGallery", + }); +my $album + = $gallery->addChild({ + className => "WebGUI::Asset::Wobject::PhotoAlbum", + }); +my $photo + = $gallery->addChild({ + className => "WebGUI::Asset::File::Image::Photo", + }); + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + +#---------------------------------------------------------------------------- +# Tests +plan tests => 0; + diff --git a/t/Asset/File/Image/Photo/comment.t b/t/Asset/File/Image/Photo/comment.t new file mode 100644 index 000000000..0430b4b86 --- /dev/null +++ b/t/Asset/File/Image/Photo/comment.t @@ -0,0 +1,250 @@ +#------------------------------------------------------------------- +# 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 the adding, deleting, editing, and +# getting comments for photos + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; +use WebGUI::Asset::File::Image::Photo; + +#---------------------------------------------------------------------------- +# Init +my $session = WebGUI::Test->session; +my $node = WebGUI::Asset->getImportNode($session); +my $versionTag = WebGUI::VersionTag->getWorking($session); +$versionTag->set({name=>"Photo Test"}); +my $gallery + = $node->addChild({ + className => "WebGUI::Asset::Wobject::PhotoGallery", + groupIdAddComment => "2", + }); +my $album + = $gallery->addChild({ + className => "WebGUI::Asset::Wobject::PhotoAlbum", + }); +my $photo + = $album->addChild({ + className => "WebGUI::Asset::File::Image::Photo", + }); + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + +#---------------------------------------------------------------------------- +# Tests +plan tests => 0; + +#---------------------------------------------------------------------------- +# Test with no comments +is( + blessed $photo->getCommentPaginator, "WebGUI::Paginator", + "Photo with no comments still provides comments paginator", +); + +is_deeply( + $photo->getCommentIds, [], + "Photo->getCommentIds returns an empty arrayref when no comments", +); + +#---------------------------------------------------------------------------- +# Test the setComment requires two arguments +ok( + !eval{ $photo->setComment(); 1 }, + "Photo->setComment fails when no arguments given", +); + +ok( + !eval{ $photo->setComment("new"); 1 }, + "Photo->setComment fails when no second argument given", +); + +ok( + !eval{ $photo->setComment("new", "lulz"); 1 }, + "Photo->setComment fails when second argument is not a hashref", +); + +ok( + !eval{ $photo->setComment("new", { lulz => "ohai" }); 1 }, + "Photo->setComment fails when hashref does not contain a bodyText key", +); + +#---------------------------------------------------------------------------- +# Test adding a comment +# - bodyText is defined +# - All else is defaults +my $commentId; +ok( + eval{ $commentId = $photo->setComment("new", { bodyText => "bodyText", }); 1 }, + "Photo->setComment succeeds", +); + +is_deeply( + $photo->getCommentIds, [$commentId], + "Photo->getCommentIds returns newly added comment's ID", +); + +my $comment; +ok( + eval{ $comment = $photo->getComment($commentId); 1}, + "Photo->getComment does not croak.", +); + +is( + ref $comment, "HASH", + "Photo->getComment returns a hash reference", +); + +is( + $comment->{assetId}, $photo->getId, + "Comment has correct assetId", +); + +is( + $comment->{userId}, $session->user->userId, + "Comment has correct userId", +); + +is( + $comment->{visitorIp}, undef, + "visitorIp is not defined if the user is not a visitor", +); + +like( + $comment->{creationDate}, /\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/, + "creationDate is defined and is a MySQL-formatted date", +); + +#---------------------------------------------------------------------------- +# Test adding a comment +# - bodyText is defined +# - userId is visitor +# - all else is defaults +ok( + eval{ $commentId = $photo->setComment("new", { userId => 1, bodyText => "bodyText", }); 1 }, + "Photo->setComment succeeds", +); + +ok( + grep { $_ eq $commentId } @{ $photo->getCommentIds }, + "Photo->getCommentIds returns newly added comment's ID", +); + +my $comment; +ok( + eval{ $comment = $photo->getComment($commentId); 1}, + "Photo->getComment does not croak.", +); + +is( + ref $comment, "HASH", + "Photo->getComment returns a hash reference", +); + +is( + $comment->{assetId}, $photo->getId, + "Comment has correct assetId", +); + +is( + $comment->{userId}, 1, + "Comment has correct userId", +); + +ok( + $comment->{visitorIp}, + "visitorIp is defined since user is visitor", +); + +like( + $comment->{creationDate}, /\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/, + "creationDate is defined and is a MySQL-formatted date", +); + +#---------------------------------------------------------------------------- +# Test deleting comment +$photo->deleteComment($commentId); +ok( + !grep { $_ eq $commentId } @{ $photo->getCommentIds }, + "Photo->getCommentIds no longer contains deleted comment", +); + +#---------------------------------------------------------------------------- +# Test deleting asset deletes comments +my $assetId = $photo->getId; +$photo->purge; +ok( + !$session->db->quickScalar("SELECT commentId FROM Photo_comment WHERE assetId=?",[$assetId]), + "Comments are purged along with asset", +); + +#---------------------------------------------------------------------------- +# Test appendTemplateVarsForCommentForm +TODO: { + local $TODO = "Test appendTemplateVarsForCommentForm"; +} + +#---------------------------------------------------------------------------- +# Test www_addCommentSave page sanity checks +my $html; +$photo + = $album->addChild({ + className => "WebGUI::Asset::File::Image::Photo", + }); + +# Permissions +$html = WebGUI::Test->getPage($photo, "www_addCommentSave", { + userId => 1, + formParams => { bodyText => "yes?" }, + }); + +like( + $html, qr/permission denied/i, + "www_addCommentSave -- Permission denied if not Gallery->canAddComment", +); + +# Required fields +$html = WebGUI::Test->getPage($photo, "www_addCommentSave", { + userId => 2, + formParams => { }, + }); + +like( + $html, WebGUI::International->get($session, "Asset_Photo", "www_addCommentSave error missing required"), + "www_addCommentSave -- Must have bodyText defined", +); + + +#---------------------------------------------------------------------------- +# Test www_addCommentSave functionality +$html = WebGUI::Test->getPage($photo, "www_addCommentSave", { + userId => 2, + formParams => { bodyText => "YES!", }, + }); + +like( + $html, WebGUI::International->get($session, "Asset_Photo", "www_addCommentSave success"), + "www_addCommentSave -- page shows success message", +); + +my $ids = $photo->getCommentIds; +is( + scalar @$ids, 1, + "www_addCommentSave -- Comment was added", +); diff --git a/t/Asset/File/Image/Photo/delete.t b/t/Asset/File/Image/Photo/delete.t new file mode 100644 index 000000000..2afc39ad6 --- /dev/null +++ b/t/Asset/File/Image/Photo/delete.t @@ -0,0 +1,53 @@ +#------------------------------------------------------------------- +# 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 +#------------------------------------------------------------------- + +# The goal of this test is to test the www_delete() and www_deleteConfirm() +# methods + +use FindBin; +use strict; +use lib "$FindBin::Bin/../../../../lib"; + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; +use WebGUI::Test::Maker::HTML; +use WebGUI::Asset::File::Image::Photo; + +#---------------------------------------------------------------------------- +# Init +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::PhotoGallery", + }); +my $album + = $gallery->addChild({ + className => "WebGUI::Asset::Wobject::PhotoAlbum", + }); +my $photo + = $gallery->addChild({ + className => "WebGUI::Asset::File::Image::Photo", + }); + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + +#---------------------------------------------------------------------------- +# Tests +plan tests => 0; diff --git a/t/Asset/File/Image/Photo/download.t b/t/Asset/File/Image/Photo/download.t new file mode 100644 index 000000000..6744b8c5a --- /dev/null +++ b/t/Asset/File/Image/Photo/download.t @@ -0,0 +1,52 @@ +#------------------------------------------------------------------- +# 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 +#------------------------------------------------------------------- + +# The goal of this test is to get sthe getDownloadFileUrl and www_download() +# methods + +use FindBin; +use strict; +use lib "$FindBin::Bin/../../../../lib"; + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; +use WebGUI::Asset::File::Image::Photo; + +#---------------------------------------------------------------------------- +# Init +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::PhotoGallery", + }); +my $album + = $gallery->addChild({ + className => "WebGUI::Asset::Wobject::PhotoAlbum", + }); +my $photo + = $gallery->addChild({ + className => "WebGUI::Asset::File::Image::Photo", + }); + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + +#---------------------------------------------------------------------------- +# Tests +plan tests => 0; diff --git a/t/Asset/File/Image/Photo/editSave.t b/t/Asset/File/Image/Photo/editSave.t new file mode 100644 index 000000000..ba8f5a6c6 --- /dev/null +++ b/t/Asset/File/Image/Photo/editSave.t @@ -0,0 +1,107 @@ +#------------------------------------------------------------------- +# 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 +#------------------------------------------------------------------- + +# The goal of this test is to test the editSave, +# processPropertiesFromFormPost, and applyConstraints methods. + +use FindBin; +use strict; +use lib "$FindBin::Bin/../../../../lib"; + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; +use WebGUI::Test::Maker::HTML; +use WebGUI::Asset::File::Image::Photo; + +#---------------------------------------------------------------------------- +# Init +my $session = WebGUI::Test->session; +my $node = WebGUI::Asset->getImportNode($session); +my $versionTag = WebGUI::VersionTag->getWorking($session); +$versionTag->set({name=>"Photo Test"}); +$session->user( { userId => 3 } ); # Admins can do everything +my $maker = WebGUI::Test::Maker::HTML->new; +my $gallery + = $node->addChild({ + className => "WebGUI::Asset::Wobject::Gallery", + }); +my $album + = $gallery->addChild({ + className => "WebGUI::Asset::Wobject::GalleryAlbum", + }); +my $photo + = $gallery->addChild({ + className => "WebGUI::Asset::File::Image::Photo", + }); + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + +#---------------------------------------------------------------------------- +# Tests +plan no_plan => 1; + +#---------------------------------------------------------------------------- +# Test permissions + +# Edit an existing photo +$maker->prepare({ + object => $photo, + method => "www_edit", + userId => "1", + test_privilege => "insufficient", +})->run; + +# Save a new photo +$maker->prepare({ + object => $photo, + method => "www_editSave", + userId => "1", + test_privilege => "insufficient", +})->run; + +#---------------------------------------------------------------------------- +# 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::Image::Photo", + }, + 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::Image::Photo", + }, + test_regex => [ + qr/awaiting approval and commit/, + ], +})->run; + +#---------------------------------------------------------------------------- diff --git a/t/Asset/File/Image/Photo/exif.t b/t/Asset/File/Image/Photo/exif.t new file mode 100644 index 000000000..6dc256ba5 --- /dev/null +++ b/t/Asset/File/Image/Photo/exif.t @@ -0,0 +1,79 @@ +#------------------------------------------------------------------- +# 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 the EXIF functionality of WebGUI's photo +# asset + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; +use JSON; +use Image::ExifTool; + +#---------------------------------------------------------------------------- +# Init +my $session = WebGUI::Test->session; +my $node = WebGUI::Asset->getImportNode($session); +my $versionTag = WebGUI::VersionTag->getWorking($session); +$versionTag->set({name=>"Photo Test"}); +my $gallery + = $node->addChild({ + className => "WebGUI::Asset::Wobject::Gallery", + }); +my $album + = $gallery->addChild({ + className => "WebGUI::Asset::Wobject::GalleryAlbum", + }); +my ( $photo ); + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + +#---------------------------------------------------------------------------- +# Tests +plan no_plan => 1; + +#---------------------------------------------------------------------------- +# Test that exif data gets parsed from the file +$photo + = $album->addChild({ + className => "WebGUI::Asset::File::Image::Photo", + }); +$photo->setFile( WebGUI::Test->getTestCollateralPath("lamp.jpg") ); +my $exifData = $photo->get("exifData"); + +ok( defined $exifData, "exifData column is defined after setFile" ); + +my $exif = jsonToObj( $exifData ); +ok( ref $exif eq "HASH", "exifData is JSON hash" ); + +#---------------------------------------------------------------------------- +# Test getTemplateVars exif data +my $var = $photo->getTemplateVars; + +is_deeply( + [ sort keys %$exif ], + [ sort map { s/exif_// } keys %$var ], + "getTemplateVars gets a hash of all exif tags", +); + +is_deeply( + [ sort keys %$exif ], + [ sort map { $_->{tag} } @{ $var->{exifLoop} } ], + "getTemplateVars gets a loop over the tags", +); diff --git a/t/Asset/File/Image/Photo/makeResolutions.t b/t/Asset/File/Image/Photo/makeResolutions.t new file mode 100644 index 000000000..7d7c4744a --- /dev/null +++ b/t/Asset/File/Image/Photo/makeResolutions.t @@ -0,0 +1,203 @@ +#------------------------------------------------------------------- +# 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 the creation of photo download +# resolutions + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; +my $graphicsClass; +BEGIN { + if (eval { require Graphics::Magick; 1 }) { + $graphicsClass = 'Graphics::Magick'; + } + elsif (eval { require Image::Magick; 1 }) { + $graphicsClass = 'Image::Magick'; + } +} +use WebGUI::Asset::File::Image::Photo; + +#---------------------------------------------------------------------------- +# Init +my $session = WebGUI::Test->session; +my $node = WebGUI::Asset->getImportNode($session); +my $versionTag = WebGUI::VersionTag->getWorking($session); +$versionTag->set({name=>"Photo Test"}); + +my ($gallery, $album, $photo); + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + +#---------------------------------------------------------------------------- +# Photo not added under a Photo Gallery asset does NOT generate any +# default resolutions +$photo + = $node->addChild({ + className => "WebGUI::Asset::File::Image::Photo", + }); +$photo->getStorageLocation->addFileFromFilesystem( WebGUI::Test->getTestCollateralPath('page_title.jpg') ); + +ok( + eval{ $photo->makeResolutions(); 1 }, + "makeResolutions succeeds when photo not under photo gallery and no resolutions to make", +); + +is_deeply( + $photo->getStorageLocation->getFiles, ['page_title.jpg'], + "makeResolutions does not make any extra resolutions when photo not under photo gallery", +); + +#---------------------------------------------------------------------------- +# makeResolutions allows API to specify resolutions to make as array reference +# argument +$photo + = $node->addChild({ + className => "WebGUI::Asset::File::Image::Photo", + }); +$photo->getStorageLocation->addFileFromFilesystem( WebGUI::Test->getTestCollateralPath('page_title.jpg') ); + +ok( + !eval{ $photo->makeResolutions('100x100','200x200'); 1 }, + "makeResolutions fails when first argument is not array reference", +); + +ok( + eval{ $photo->makeResolutions(['100x100','200x200']); 1 }, + "makeResolutions succeeds when first argument is array reference of resolutions to make", +); + +is_deeply( + [ sort({ $a cmp $b} @{ $photo->getStorageLocation->getFiles }) ], + ['100x100.jpg', '200x200.jpg', 'page_title.jpg'], + "makeResolutions makes all the required resolutions with the appropriate names.", +); + +TODO: { + local $TODO = 'Test to ensure the files are created with correct resolution and density'; +} + +#---------------------------------------------------------------------------- +# makeResolutions throws a warning on an invalid resolution but keeps going +$photo + = $node->addChild({ + className => "WebGUI::Asset::File::Image::Photo", + }); +$photo->getStorageLocation->addFileFromFilesystem( WebGUI::Test->getTestCollateralPath('page_title.jpg') ); +{ # localize our signal handler + my @warnings; + local $SIG{__WARN__} = sub { push @warnings, $_[0]; }; + + ok( + eval{ $photo->makeResolutions(['abc','200','3d400']); 1 }, + "makeResolutions succeeds when invalid resolutions are given", + ); + + is( + scalar @warnings, 2, + "makeResolutions throws a warning for each invalid resolution given", + ); + + like( + $warnings[0], qr/abc/, + "makeResolutions throws a warning for the correct invalid resolution 'abc'", + ); + + like( + $warnings[1], qr/3d400/, + "makeResolutions throws a warning for the correct invalid resolution '3d400'", + ); + + is_deeply( + [ sort({ $a cmp $b} @{ $photo->getStorageLocation->getFiles }) ], + ['200.jpg', 'page_title.jpg'], + "makeResolutions still makes valid resolutions when invalid resolutions given", + ); +} + +#---------------------------------------------------------------------------- +# makeResolutions gets default resolutions from a parent Photo Gallery asset +$gallery + = $node->addChild({ + className => "WebGUI::Asset::Wobject::PhotoGallery", + imageResolutions => "1600x1200\n1024x768\n800x600\n640x480", + }); +$album + = $gallery->addChild({ + className => "WebGUI::Asset::Wobject::PhotoAlbum", + }); +$photo + = $album->addChild({ + className => "WebGUI::Asset::File::Image::Photo", + }); +$photo->getStorageLocation->addFileFromFilesystem( WebGUI::Test->getTestCollateralPath('page_title.jpg') ); + +ok( + eval{ $photo->makeResolutions; 1 }, + "makeResolutions succeeds when photo under photo gallery and no resolution given", +); + +is_deeply( + [ sort({ $a cmp $b} @{ $photo->getStorageLocation->getFiles }) ], + [ '1024x768.jpg', '1600x1200.jpg', '640x480.jpg', '800x600.jpg', 'page_title.jpg' ], + "makeResolutions makes all the required resolutions with the appropriate names.", +); + +TODO: { + local $TODO = 'Test to ensure the files are created with correct resolution and density'; +} + +#---------------------------------------------------------------------------- +# Array of resolutions passed to makeResolutions overrides defaults from +# parent asset +$gallery + = $node->addChild({ + className => "WebGUI::Asset::Wobject::PhotoGallery", + imageResolutions => "1600x1200\n1024x768\n800x600\n640x480", + }); +$album + = $gallery->addChild({ + className => "WebGUI::Asset::Wobject::PhotoAlbum", + }); +$photo + = $album->addChild({ + className => "WebGUI::Asset::File::Image::Photo", + }); +$photo->getStorageLocation->addFileFromFilesystem( WebGUI::Test->getTestCollateralPath('page_title.jpg') ); + +ok( + !eval{ $photo->makeResolutions('100x100','200x200'); 1 }, + "makeResolutions fails when first argument is not array reference", +); + +ok( + eval{ $photo->makeResolutions(['100x100','200x200']); 1 }, + "makeResolutions succeeds when first argument is array reference of resolutions to make", +); + +is_deeply( + [ sort({ $a cmp $b} @{ $photo->getStorageLocation->getFiles }) ], + ['100x100.jpg', '200x200.jpg', 'page_title.jpg'], + "makeResolutions makes all the required resolutions with the appropriate names.", +); + +TODO: { + local $TODO = 'Test to ensure the files are created with correct resolution and density'; +} + diff --git a/t/Asset/File/Image/Photo/makeShortcut.t b/t/Asset/File/Image/Photo/makeShortcut.t new file mode 100644 index 000000000..adbb7dd75 --- /dev/null +++ b/t/Asset/File/Image/Photo/makeShortcut.t @@ -0,0 +1,123 @@ +#------------------------------------------------------------------- +# 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 +#------------------------------------------------------------------- + +# The goal of this test is to test the makeShortcut method and www_makeShortcut +# pages + +use FindBin; +use strict; +use lib "$FindBin::Bin/../../../../lib"; + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; +use WebGUI::Test::Maker::HTML; +use WebGUI::Asset::File::Image::Photo; + +#---------------------------------------------------------------------------- +# Init +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 $otherParent + = $node->addChild({ + className => "WebGUI::Asset::Wobject::Layout", + }); +my $photo + = $node->addChild({ + className => "WebGUI::Asset::File::Image::Photo", + userDefined1 => "ORIGINAL", + }); + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + +#---------------------------------------------------------------------------- +# Tests +plan tests => 0; + +#---------------------------------------------------------------------------- +# makeShortcut argument checking +ok( + !eval{ $photo->makeShortcut(); 1 }, + "Photo->makeShortcut requires at least one argument", +); + +ok( + !eval{ $photo->makeShortcut("", ""); 1}, + "Photo->makeShortcut fails if second argument is not hash reference", +); + +ok( + !eval{ $photo->makeShortcut(""); 1}, + "Photo->makeShortcut fails if given parent cannot be instanciated", +); + +#---------------------------------------------------------------------------- +# makeShortcut returns a reference to the new Shortcut asset +my $shortcut; +ok( + eval{ $shortcut = $photo->makeShortcut($otherParent->getId); 1}, + "Photo->makeShortcut succeeds when valid assetId is given", +); + +is( + blessed $shortcut, "WebGUI::Asset::Shortcut", + "Photo->makeShortcut returns a WebGUI::Shortcut asset", +); + +is( + $shortcut->getShortcutOriginal->getId, $photo->getId, + "Photo->makeShortcut makes a shortcut to the correct asset", +); + +#---------------------------------------------------------------------------- +# makeShortcut creates the appropriate overrides +my $overrides = { + userDefined1 => "OVERRIDDEN", +}; +ok( + eval{ $shortcut = $photo->makeShortcut($otherParent->getId, $overrides); 1}, + "Photo->makeShortcut succeeds when valid assetId is given", +); + +is( + blessed $shortcut, "WebGUI::Asset::Shortcut", + "Photo->makeShortcut returns a WebGUI::Shortcut asset", +); + +is( + $shortcut->getShortcutOriginal->getId, $photo->getId, + "Photo->makeShortcut makes a shortcut to the correct asset", +); + +is_deeply( + {$shortcut->getShortcutOverrides}, $overrides, + "Photo->makeShortcut makes a shortcut with the correct overrides", +); + +#---------------------------------------------------------------------------- +# www_makeShortcut is only available to those who can edit the photo +$maker->prepare({ + object => $photo, + method => "www_makeShortcut", + userId => 1, + test_privilege => "insufficient", +}); +$maker->run; + +#---------------------------------------------------------------------------- +# www_makeShortcut diff --git a/t/Asset/File/Image/Photo/permissions.t b/t/Asset/File/Image/Photo/permissions.t new file mode 100644 index 000000000..75598deee --- /dev/null +++ b/t/Asset/File/Image/Photo/permissions.t @@ -0,0 +1,135 @@ +#------------------------------------------------------------------- +# 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 +#------------------------------------------------------------------- + +# Test permissions of Photo assets + +use FindBin; +use strict; +use lib "$FindBin::Bin/../../../../lib"; + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use WebGUI::Friends; +use Test::More; + +#---------------------------------------------------------------------------- +# Init +my $session = WebGUI::Test->session; +my $node = WebGUI::Asset->getImportNode($session); +my $versionTag = WebGUI::VersionTag->getWorking($session); +$versionTag->set({name=>"Photo Test"}); +my ($photo); +$session->user({ userId => 3 }); + +my $friend = WebGUI::User->new($session, "new"); +WebGUI::Friends->new($session)->add( [ $friend->userId ] ); + +my $gallery + = $node->addChild({ + className => "WebGUI::Asset::Wobject::Gallery", + groupIdView => "7", + groupIdEdit => "3", + ownerUserId => $session->user->userId, + }); +my $album + = $gallery->addChild({ + className => "WebGUI::Asset::Wobject::GalleryAlbum", + groupIdView => "", + groupIdEdit => "", + ownerUserId => $session->user->userId, + }); + + +#---------------------------------------------------------------------------- +# Cleanup +END { + WebGUI::Friends->new($session)->delete( [ $friend->userId ] ); + $friend->delete; + $versionTag->rollback; +} + +#---------------------------------------------------------------------------- +# Tests +plan no_plan => 1; + +#---------------------------------------------------------------------------- +# Everyone can view, Admins can edit, Owned by current user +$photo + = $album->addChild({ + className => "WebGUI::Asset::File::Image::Photo", + groupIdView => "7", + groupIdEdit => "3", + ownerUserId => $session->user->userId, + }); + +ok( $photo->canView(1), "Visitor can view" ); +ok( !$photo->canEdit(1), "Visitor cannot edit" ); +ok( $photo->canView(2), "Registered users can view" ); +ok( !$photo->canEdit(2), "Registered users cannot edit" ); +ok( $photo->canView, "Current user can view" ); +ok( $photo->canEdit, "Current user can edit" ); + +#---------------------------------------------------------------------------- +# Admins can view, Admins can edit, Owned by Admin, current user is Visitor +my $oldUser = $session->user; +$session->user( { user => WebGUI::User->new($session, "1") } ); +$photo + = $album->addChild({ + className => "WebGUI::Asset::File::Image::Photo", + groupIdView => "3", + groupIdEdit => "3", + ownerUserId => "3", + }); + +ok( !$photo->canView, "Visitors cannot view" ); +ok( !$photo->canEdit, "Visitors cannot edit" ); +ok( !$photo->canView(2), "Registered Users cannot view" ); +ok( !$photo->canEdit(2), "Registered Users cannot edit" ); +ok( $photo->canView(3), "Admins can view" ); +ok( $photo->canEdit(3), "Admins can edit" ); +$session->user( { user => $oldUser } ); + +#---------------------------------------------------------------------------- +# Photo without specific view/edit inherits from gallery properties +$photo + = $album->addChild({ + className => "WebGUI::Asset::File::Image::Photo", + groupIdView => "", + groupIdEdit => "", + ownerUserId => $session->user->userId, + }); + +ok( $photo->canView(1), "Visitors can view" ); +ok( !$photo->canEdit(1), "Visitors cannot edit" ); +ok( $photo->canView(2), "Registered Users can view" ); +ok( !$photo->canEdit(2), "Registered Users cannot edit" ); +ok( $photo->canView, "Owner can view" ); +ok( $photo->canEdit, "Owner can edit" ); +ok( $photo->canView(3), "Admin can view" ); +ok( $photo->canEdit(3), "Admin can edit" ); + +#---------------------------------------------------------------------------- +# Friends are allowed to view friendsOnly photos +$photo + = $album->addChild({ + className => "WebGUI::Asset::File::Image::Photo", + groupIdEdit => "", + ownerUserId => $session->user->userId, + }); + +ok( !$photo->canView(1), "Visitors cannot view" ); +ok( !$photo->canEdit(1), "Visitors cannot edit" ); +ok( !$photo->canView(2), "Registered Users cannot view" ); +ok( !$photo->canEdit(2), "Registered Users cannot edit" ); +ok( $photo->canView, "Owner can view" ); +ok( $photo->canEdit, "Owner can edit" ); +ok( $photo->canView(3), "Admin can view" ); +ok( $photo->canEdit(3), "Admin can edit" ); diff --git a/t/Asset/File/Image/Photo/setFile.t b/t/Asset/File/Image/Photo/setFile.t new file mode 100644 index 000000000..9a0895ca5 --- /dev/null +++ b/t/Asset/File/Image/Photo/setFile.t @@ -0,0 +1,68 @@ +#------------------------------------------------------------------- +# 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 the creation and deletion of photo assets + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; +use WebGUI::Asset::File::Image::Photo; + +#---------------------------------------------------------------------------- +# Init +my $session = WebGUI::Test->session; +my $node = WebGUI::Asset->getImportNode($session); +my $versionTag = WebGUI::VersionTag->getWorking($session); +$versionTag->set({name=>"Photo Test"}); +my $gallery + = $node->addChild({ + className => "WebGUI::Asset::Wobject::PhotoGallery", + imageResolutions => "1024x768", + }); +my $album + = $gallery->addChild({ + className => "WebGUI::Asset::Wobject::PhotoAlbum", + }); +my $photo + = $album->addChild({ + className => "WebGUI::Asset::File::Image::Photo", + }); + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + +#---------------------------------------------------------------------------- +# Tests +plan tests => 2; + +#---------------------------------------------------------------------------- +# setFile also makes download versions +$photo->setFile( WebGUI::Test->getTestCollateralPath('page_title.jpg') ); +my $storage = $photo->getStorageLocation; + +is_deeply( + $storage->getFiles, ['page_title.jpg'], + "Storage location contains only the file we added", +); + +ok( + -e $storage->getPath($gallery->get('imageResolutions') . '.jpg'), + "Generated resolution file exists on the filesystem", +); + + diff --git a/t/Asset/File/Image/Photo/view.t b/t/Asset/File/Image/Photo/view.t new file mode 100644 index 000000000..6a7c81588 --- /dev/null +++ b/t/Asset/File/Image/Photo/view.t @@ -0,0 +1,53 @@ +#------------------------------------------------------------------- +# 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 +#------------------------------------------------------------------- + +# The goal of this test is to test the view and getTemplateVars methods + +use FindBin; +use strict; +use lib "$FindBin::Bin/../../../../lib"; + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; +use WebGUI::Test::Maker::HTML; +use WebGUI::Asset::File::Image::Photo; + +#---------------------------------------------------------------------------- +# Init +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::PhotoGallery", + }); +my $album + = $gallery->addChild({ + className => "WebGUI::Asset::Wobject::PhotoAlbum", + }); +my $photo + = $gallery->addChild({ + className => "WebGUI::Asset::File::Image::Photo", + }); +$photo->setFile( WebGUI::Test->getCollateralPath('page_title.jpg') ); + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + +#---------------------------------------------------------------------------- +# Tests +plan tests => 0; diff --git a/t/Asset/File/Image/setfile.t b/t/Asset/File/Image/setfile.t new file mode 100644 index 000000000..ad02b7524 --- /dev/null +++ b/t/Asset/File/Image/setfile.t @@ -0,0 +1,62 @@ +#------------------------------------------------------------------- +# 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 the additional functionality of the +# overridden setFile method + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; +use WebGUI::Asset::File::Image; + +#---------------------------------------------------------------------------- +# Init +my $session = WebGUI::Test->session; +my $node = WebGUI::Asset->getImportNode($session); +my $versionTag = WebGUI::VersionTag->getWorking($session); +$versionTag->set({name=>"Image Test"}); +my $image + = $node->addChild({ + className => "WebGUI::Asset::File::Image", + }); + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + +#---------------------------------------------------------------------------- +# Tests +plan tests => 2; + +#---------------------------------------------------------------------------- +# setFile allows file path argument and adds the file +# setFile also generates thumbnail +# plan tests => 2 +$image->setFile( WebGUI::Test->getTestCollateralPath("page_title.jpg") ); +my $storage = $image->getStorageLocation; + +is_deeply( + $storage->getFiles, ['page_title.jpg'], + "Storage location contains only the file we added", +); + +# We must do a filesystem test because getFiles doesn't include 'thumb-' +ok( + -e $storage->getPath('thumb-page_title.jpg'), + "Thumbnail file exists on the filesystem", +); + diff --git a/t/Asset/File/setfile.t b/t/Asset/File/setfile.t new file mode 100644 index 000000000..e7a6f8e9a --- /dev/null +++ b/t/Asset/File/setfile.t @@ -0,0 +1,63 @@ +#------------------------------------------------------------------- +# 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 the creation and deletion of photo assets + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; +use WebGUI::Asset::File; + +#---------------------------------------------------------------------------- +# Init +my $session = WebGUI::Test->session; +my $node = WebGUI::Asset->getImportNode($session); +my $versionTag = WebGUI::VersionTag->getWorking($session); +$versionTag->set({name=>"File Test"}); +my $file + = $node->addChild({ + className => "WebGUI::Asset::File", + }); + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + +#---------------------------------------------------------------------------- +# Tests +plan tests => 2; + +#---------------------------------------------------------------------------- +# setFile allows file path argument and fails if can't find file +# plan tests => 1 +ok( + !eval { $file->setFile( WebGUI::Test->getTestCollateralPath("DOES_NOT_EXIST.NO") ); 1}, + "setFile allows file path argument and croaks if can't find file" +); + +#---------------------------------------------------------------------------- +# setFile allows file path argument and adds the file +# plan tests => 1 +$file->setFile( WebGUI::Test->getTestCollateralPath("WebGUI.pm") ); +my $storage = $file->getStorageLocation; + +is_deeply( + $storage->getFiles, ['WebGUI.pm'], + "Storage location contains only the file we added", +); + + diff --git a/t/Asset/Shortcut/000-create-delete.t b/t/Asset/Shortcut/000-create-delete.t new file mode 100644 index 000000000..d93ff4ebf --- /dev/null +++ b/t/Asset/Shortcut/000-create-delete.t @@ -0,0 +1,74 @@ +#------------------------------------------------------------------- +# 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 the creation and deletion of shortcut assets + +use WebGUI::Test; +use WebGUI::Session; +use Test::More; +use WebGUI::Asset::Snippet; + +#---------------------------------------------------------------------------- +# Init +my $session = WebGUI::Test->session; +my $node = WebGUI::Asset->getImportNode($session); +my $versionTag = WebGUI::VersionTag->getWorking($session); +$versionTag->set({name=>"Shortcut Test"}); + +# Make a snippet to shortcut +my $snippet + = $node->addChild({ + className => "WebGUI::Asset::Snippet", + }); + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + +#---------------------------------------------------------------------------- +# Tests +plan tests => 3; + +#---------------------------------------------------------------------------- +# Test module compiles okay +# plan tests => 1 +use_ok("WebGUI::Asset::Shortcut"); + +#---------------------------------------------------------------------------- +# Test creating a shortcut to snippet +# plan tests => 2 +my $shortcut + = $node->addChild({ + className => "WebGUI::Asset::Shortcut", + shortcutToAssetId => $snippet->getId, + }); + +isa_ok( + $shortcut, "WebGUI::Asset::Shortcut", +); + +isa_ok( + $shortcut, "WebGUI::Asset", +); + +#---------------------------------------------------------------------------- +# Test deleting a shortcut +# plan tests => +TODO: { + local $TODO = "Test deleting a shortcut."; +} + + diff --git a/t/Asset/Shortcut/010-linked-asset.t b/t/Asset/Shortcut/010-linked-asset.t new file mode 100644 index 000000000..a6563706f --- /dev/null +++ b/t/Asset/Shortcut/010-linked-asset.t @@ -0,0 +1,126 @@ +#------------------------------------------------------------------- +# 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 the link between the asset and its shortcut +# and that changes to the asset are propagated to the shortcut + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; +use WebGUI::Asset::Shortcut; +use WebGUI::Asset::Snippet; + +#---------------------------------------------------------------------------- +# Init +my $session = WebGUI::Test->session; +my $node = WebGUI::Asset->getImportNode($session); +my $versionTag = WebGUI::VersionTag->getWorking($session); +$versionTag->set({name=>"Shortcut Test"}); + +# Make a snippet to shortcut +my $snippet + = $node->addChild({ + className => "WebGUI::Asset::Snippet", + }); + +my $shortcut + = $node->addChild({ + className => "WebGUI::Asset::Shortcut", + shortcutToAssetId => $snippet->getId, + }); + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + + +#---------------------------------------------------------------------------- +# Tests +plan tests => 10; + +#---------------------------------------------------------------------------- +# Test shortcut's link to original asset +# plan => 3 +my $original = $shortcut->getShortcut; + +ok( + defined $original, + "Original asset is defined", +); + +is( + blessed $original, blessed $snippet, + "Original asset class is correct", +); + +is( + $original->getId, $snippet->getId, + "Original assetId is correct" +); + +#---------------------------------------------------------------------------- +# Test trashing snippet trashes shortcut also +# plan tests => 3 +$snippet->trash; +$shortcut = WebGUI::Asset->newByDynamicClass($session, $shortcut->getId); + +ok( + defined $shortcut, + "Trash Linked Asset: Shortcut is defined", +); + +like( + $shortcut->get("state"), qr/^trash/, + "Trash Linked Asset: Shortcut state is trash", +); + +ok( + grep({ $_->getId eq $shortcut->getId } @{ $snippet->getAssetsInTrash }), + "Trash Linked Asset: Shortcut is in trash", +); + +#---------------------------------------------------------------------------- +# Test restoring snippet restores shortcut also +# plan tests => 3 +$snippet->publish; +$shortcut = WebGUI::Asset->newByDynamicClass($session, $shortcut->getId); + +ok( + defined $shortcut, + "Restore Linked Asset: Shortcut is defined", +); + +ok( + !grep({ $_->getId eq $shortcut->getId } @{ $snippet->getAssetsInTrash }), + "Restore Linked Asset: Shortcut is not in trash", +); + +#---------------------------------------------------------------------------- +# Test purging snippet purges shortcut also +# plan tests => 2 +$snippet->purge; +$shortcut = WebGUI::Asset->newByDynamicClass($session, $shortcut->getId); + +ok( + !defined $shortcut, + "Purge Linked Asset: Shortcut is not defined", +); + +ok( + !grep({ $_->getId eq $shortcut->getId } @{ $snippet->getAssetsInTrash }), + "Purge Linked Asset: Shortcut is not in trash", +); diff --git a/t/Asset/Wobject/Gallery/00base.t b/t/Asset/Wobject/Gallery/00base.t new file mode 100644 index 000000000..0119dc465 --- /dev/null +++ b/t/Asset/Wobject/Gallery/00base.t @@ -0,0 +1,78 @@ +#------------------------------------------------------------------- +# 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 the creation and deletion of album assets + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; + +#---------------------------------------------------------------------------- +# Init +my $session = WebGUI::Test->session; +my $node = WebGUI::Asset->getImportNode($session); +my $versionTag = WebGUI::VersionTag->getWorking($session); +$versionTag->set({name=>"Album Test"}); +my $gallery + = $node->addChild({ + className => "WebGUI::Asset::Wobject::Gallery", + }); + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + +#---------------------------------------------------------------------------- +# Tests +plan tests => 5; + +#---------------------------------------------------------------------------- +# Test module compiles okay +# plan tests => 1 +use_ok("WebGUI::Asset::Wobject::GalleryAlbum"); + +#---------------------------------------------------------------------------- +# Test creating an album +my $album + = $gallery->addChild({ + className => "WebGUI::Asset::Wobject::GalleryAlbum", + }); + +is( + blessed $album, "WebGUI::Asset::Wobject::GalleryAlbum", + "Album is a WebGUI::Asset::Wobject::GalleryAlbum object", +); + +isa_ok( + $album, "WebGUI::Asset::Wobject", +); + +#---------------------------------------------------------------------------- +# Test deleting a album +my $properties = $album->get; +$album->purge; + +is( + $album, undef, + "Album is undefined", +); + +is( + WebGUI::Asset->newByDynamicClass($session, $properties->{assetId}), undef, + "Album no longer able to be instanciated", +); + diff --git a/t/Asset/Wobject/Gallery/delete.t b/t/Asset/Wobject/Gallery/delete.t new file mode 100644 index 000000000..f49603f93 --- /dev/null +++ b/t/Asset/Wobject/Gallery/delete.t @@ -0,0 +1,96 @@ +# $vim: syntax=perl +#------------------------------------------------------------------- +# 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 the deleting of GalleryAlbums + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; + +#---------------------------------------------------------------------------- +# 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); +$versionTag->set({name=>"Album Test"}); +my $gallery + = $node->addChild({ + className => "WebGUI::Asset::Wobject::Gallery", + groupIdAddComment => 2, # Registered Users + groupIdAddFile => 2, # Registered Users + groupIdView => 7, # Everyone + groupIdEdit => 3, # Admins + ownerUserId => 3, # Admin + }); +my $album + = $gallery->addChild({ + className => "WebGUI::Asset::Wobject::GalleryAlbum", + ownerUserId => "3", # Admin + }); + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + +#---------------------------------------------------------------------------- +# Tests +plan no_plan => 1; + +#---------------------------------------------------------------------------- +# Delete page gives error for those who can't edit the GalleryAlbum +$maker->prepare({ + object => $album, + method => "www_delete", + test_privilege => "insufficient", + userId => 1, +}, { + object => $album, + method => "www_deleteConfirm", + test_privilege => "insufficient", + userId => 1, +}); +$maker->run; + +#---------------------------------------------------------------------------- +# Delete confirm page appears for those allowed to edit the GalleryAlbum +$maker->prepare({ + object => $album, + method => "www_delete", + test_regex => [ qr/func=deleteConfirm/, ], + userId => 3, +}); +$maker->run; + +#---------------------------------------------------------------------------- +# www_deleteConfirm deletes the asset +my $assetId = $album->getId; +$maker->prepare({ + object => $album, + method => "www_deleteConfirm", + test_regex => [ qr/has been deleted/, ], + userId => 3, +}); +$maker->run; + +is( + WebGUI::Asset->newByDynamicClass( $session, $assetId ), + undef, + "GalleryAlbum cannot be instanciated after www_deleteConfirm", +); + diff --git a/t/Asset/Wobject/Gallery/listFilesForUser.t b/t/Asset/Wobject/Gallery/listFilesForUser.t new file mode 100644 index 000000000..0119dc465 --- /dev/null +++ b/t/Asset/Wobject/Gallery/listFilesForUser.t @@ -0,0 +1,78 @@ +#------------------------------------------------------------------- +# 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 the creation and deletion of album assets + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; + +#---------------------------------------------------------------------------- +# Init +my $session = WebGUI::Test->session; +my $node = WebGUI::Asset->getImportNode($session); +my $versionTag = WebGUI::VersionTag->getWorking($session); +$versionTag->set({name=>"Album Test"}); +my $gallery + = $node->addChild({ + className => "WebGUI::Asset::Wobject::Gallery", + }); + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + +#---------------------------------------------------------------------------- +# Tests +plan tests => 5; + +#---------------------------------------------------------------------------- +# Test module compiles okay +# plan tests => 1 +use_ok("WebGUI::Asset::Wobject::GalleryAlbum"); + +#---------------------------------------------------------------------------- +# Test creating an album +my $album + = $gallery->addChild({ + className => "WebGUI::Asset::Wobject::GalleryAlbum", + }); + +is( + blessed $album, "WebGUI::Asset::Wobject::GalleryAlbum", + "Album is a WebGUI::Asset::Wobject::GalleryAlbum object", +); + +isa_ok( + $album, "WebGUI::Asset::Wobject", +); + +#---------------------------------------------------------------------------- +# Test deleting a album +my $properties = $album->get; +$album->purge; + +is( + $album, undef, + "Album is undefined", +); + +is( + WebGUI::Asset->newByDynamicClass($session, $properties->{assetId}), undef, + "Album no longer able to be instanciated", +); + diff --git a/t/Asset/Wobject/Gallery/permission.t b/t/Asset/Wobject/Gallery/permission.t new file mode 100644 index 000000000..2d91342ed --- /dev/null +++ b/t/Asset/Wobject/Gallery/permission.t @@ -0,0 +1,107 @@ +#------------------------------------------------------------------- +# 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 the permissions of GalleryAlbum assets + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; +use WebGUI::Test::Maker::Permission; + +#---------------------------------------------------------------------------- +# Init +my $maker = WebGUI::Test::Maker::Permission->new; +my $session = WebGUI::Test->session; +my $node = WebGUI::Asset->getImportNode($session); + +my %user; +$user{"2"} = WebGUI::User->new( $session, "new" ); +$user{"2"}->addToGroups( ['2'] ); # Registered user + +my $versionTag = WebGUI::VersionTag->getWorking($session); +$versionTag->set({name=>"Album Test"}); +my $gallery + = $node->addChild({ + className => "WebGUI::Asset::Wobject::Gallery", + groupIdAddComment => 2, # Registered Users + groupIdAddFile => 2, # Registered Users + groupIdView => 7, # Everyone + groupIdEdit => 3, # Admins + ownerUserId => 3, # Admin + }); +my $album + = $gallery->addChild({ + className => "WebGUI::Asset::Wobject::GalleryAlbum", + ownerUserId => "3", # Admin + }); + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); + $user{"2"}->delete; +} + +#---------------------------------------------------------------------------- +# Tests +plan no_plan => 1; + +#---------------------------------------------------------------------------- +# By default, GalleryAlbum inherits its permissions from the Gallery, but +# only the owner of the GalleryAlbum is allowed to add files +$maker->prepare({ + object => $album, + method => "canView", + pass => [ 1, 3, $user{"2"}, ], +}, { + object => $album, + method => "canEdit", + pass => [ 3, ], + fail => [ 1, $user{"2"}, ], +}, { + object => $album, + method => "canAddFile", + pass => [ 3, ], + fail => [ 1, $user{"2"}, ], +}, { + object => $album, + method => "canAddComment", + pass => [ 3, $user{"2"}, ], + fail => [ 1, ], +}); +$maker->run; + +#---------------------------------------------------------------------------- +# GalleryAlbums with "allowComments" false do not allow anyone to comment +$album->update({ allowComments => 0 }); +$maker->prepare({ + object => $album, + method => "canComment", + fail => [ 1, 3, $user{"2"}, ], +}); +$maker->run; + +#---------------------------------------------------------------------------- +# GalleryAlbum with "othersCanAdd" true allows anyone who can add files to +# the Gallery to add files to this GalleryAlbum +$album->update({ othersCanAdd => 1 }); +$maker->prepare({ + object => $album, + method => "canAddFile", + pass => [ 3, $user{"2"}, ], + fail => [ 1, ], +}); +$maker->run; + diff --git a/t/Asset/Wobject/Gallery/rss.t b/t/Asset/Wobject/Gallery/rss.t new file mode 100644 index 000000000..1f96b786c --- /dev/null +++ b/t/Asset/Wobject/Gallery/rss.t @@ -0,0 +1,66 @@ +# $vim: syntax=perl +#------------------------------------------------------------------- +# 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 the rss view of GalleryAlbums + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; +use WebGUI::Test::Maker::HTML; + +#---------------------------------------------------------------------------- +# 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); +$versionTag->set({name=>"Album Test"}); +my $gallery + = $node->addChild({ + className => "WebGUI::Asset::Wobject::Gallery", + groupIdAddComment => 2, # Registered Users + groupIdAddFile => 2, # Registered Users + groupIdView => 7, # Everyone + groupIdEdit => 3, # Admins + ownerUserId => 3, # Admin + }); +my $album + = $gallery->addChild({ + className => "WebGUI::Asset::Wobject::GalleryAlbum", + ownerUserId => "3", # Admin + }); +my @photos; +for my $i ( 0 .. 5 ) { + $photos[ $i ] + = $album->addChild({ + className => "WebGUI::Asset::File::Image::Photo", + filename => "$i.jpg", + }); +} + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + +#---------------------------------------------------------------------------- +# Tests +plan no_plan => 1; + +#---------------------------------------------------------------------------- +# Test www_viewRss + diff --git a/t/Asset/Wobject/Gallery/search.t b/t/Asset/Wobject/Gallery/search.t new file mode 100644 index 000000000..0119dc465 --- /dev/null +++ b/t/Asset/Wobject/Gallery/search.t @@ -0,0 +1,78 @@ +#------------------------------------------------------------------- +# 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 the creation and deletion of album assets + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; + +#---------------------------------------------------------------------------- +# Init +my $session = WebGUI::Test->session; +my $node = WebGUI::Asset->getImportNode($session); +my $versionTag = WebGUI::VersionTag->getWorking($session); +$versionTag->set({name=>"Album Test"}); +my $gallery + = $node->addChild({ + className => "WebGUI::Asset::Wobject::Gallery", + }); + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + +#---------------------------------------------------------------------------- +# Tests +plan tests => 5; + +#---------------------------------------------------------------------------- +# Test module compiles okay +# plan tests => 1 +use_ok("WebGUI::Asset::Wobject::GalleryAlbum"); + +#---------------------------------------------------------------------------- +# Test creating an album +my $album + = $gallery->addChild({ + className => "WebGUI::Asset::Wobject::GalleryAlbum", + }); + +is( + blessed $album, "WebGUI::Asset::Wobject::GalleryAlbum", + "Album is a WebGUI::Asset::Wobject::GalleryAlbum object", +); + +isa_ok( + $album, "WebGUI::Asset::Wobject", +); + +#---------------------------------------------------------------------------- +# Test deleting a album +my $properties = $album->get; +$album->purge; + +is( + $album, undef, + "Album is undefined", +); + +is( + WebGUI::Asset->newByDynamicClass($session, $properties->{assetId}), undef, + "Album no longer able to be instanciated", +); + diff --git a/t/Asset/Wobject/Gallery/view.t b/t/Asset/Wobject/Gallery/view.t new file mode 100644 index 000000000..dedd4f74a --- /dev/null +++ b/t/Asset/Wobject/Gallery/view.t @@ -0,0 +1,130 @@ +# $vim: syntax=perl +#------------------------------------------------------------------- +# 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 the default view and associated subs + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; +use Test::Deep; +use WebGUI::Test::Maker::HTML; + +#---------------------------------------------------------------------------- +# 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); +$versionTag->set({name=>"Album Test"}); +my $gallery + = $node->addChild({ + className => "WebGUI::Asset::Wobject::Gallery", + groupIdAddComment => 2, # Registered Users + groupIdAddFile => 2, # Registered Users + groupIdView => 2, # Registered Users + groupIdEdit => 3, # Admins + ownerUserId => 3, # Admin + }); +my $album + = $gallery->addChild({ + className => "WebGUI::Asset::Wobject::GalleryAlbum", + ownerUserId => "3", # Admin + }); +my @photos; +for my $i ( 0 .. 5 ) { + $photos[ $i ] + = $album->addChild({ + className => "WebGUI::Asset::File::Image::Photo", + filename => "$i.jpg", + }); +} + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + +#---------------------------------------------------------------------------- +# Tests +plan no_plan => 1; + +#---------------------------------------------------------------------------- +# Test getFileIds and getFilePaginator +cmp_bag( $album->getFileIds, [ map { $_->getId } @photos ] ); + +my $p = $album->getFilePaginator; +isa_ok( $p, "WebGUI::Paginator" ); +cmp_deeply( $p->getPageData, subbagof( map { $_->getId } @photos ) ); + +#---------------------------------------------------------------------------- +# Test getTemplateVars + +# Is a superset of Asset->get +# NOTE: url is Asset->getUrl +cmp_deeply( $album->getTemplateVars, superhashof( { %{$album->get}, url => $album->getUrl, } ) ); + +# Contains specific keys/values +my $expected = { + "url_addPhoto" + => all( + re( qr/className=WebGUI::Asset::File::Image::Photo/ ), + re( qr/func=add/ ), + re( $album->getUrl ), + ), + "url_addNoClass" + => all( + re( $album->getUrl ), + re( qr/func=add$/ ), + ), + "url_slideshow" + => all( + re( $album->getUrl ), + re( qr/func=slideshow/ ), + ), + "url_thumbnails" + => all( + re( $album->getUrl ), + re( qr/func=thumbnails/ ), + ), + "url_viewRss" + => all( + re( $album->getUrl ), + re( qr/func=viewRss/ ), + ), +}; + +cmp_deeply( $album->getTemplateVars, superhashof( $expected ) ); + +#---------------------------------------------------------------------------- +# Test appendTemplateVarsFileLoop +$expected = { + "file_loop" => bag( map { $_->getTemplateVars } @photos ), +}; +cmp_deeply( + $album->appendTemplateVarsFileLoop({},$self->getFilePaginator->getPageData), + $expected +); + +#---------------------------------------------------------------------------- +# Test www_view() for those without permission to view +$maker->prepare({ + object => $album, + method => "www_view", + test_privilege => "insufficient", +}); +$maker->run; + diff --git a/t/Asset/Wobject/GalleryAlbum/00base.t b/t/Asset/Wobject/GalleryAlbum/00base.t new file mode 100644 index 000000000..0119dc465 --- /dev/null +++ b/t/Asset/Wobject/GalleryAlbum/00base.t @@ -0,0 +1,78 @@ +#------------------------------------------------------------------- +# 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 the creation and deletion of album assets + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; + +#---------------------------------------------------------------------------- +# Init +my $session = WebGUI::Test->session; +my $node = WebGUI::Asset->getImportNode($session); +my $versionTag = WebGUI::VersionTag->getWorking($session); +$versionTag->set({name=>"Album Test"}); +my $gallery + = $node->addChild({ + className => "WebGUI::Asset::Wobject::Gallery", + }); + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + +#---------------------------------------------------------------------------- +# Tests +plan tests => 5; + +#---------------------------------------------------------------------------- +# Test module compiles okay +# plan tests => 1 +use_ok("WebGUI::Asset::Wobject::GalleryAlbum"); + +#---------------------------------------------------------------------------- +# Test creating an album +my $album + = $gallery->addChild({ + className => "WebGUI::Asset::Wobject::GalleryAlbum", + }); + +is( + blessed $album, "WebGUI::Asset::Wobject::GalleryAlbum", + "Album is a WebGUI::Asset::Wobject::GalleryAlbum object", +); + +isa_ok( + $album, "WebGUI::Asset::Wobject", +); + +#---------------------------------------------------------------------------- +# Test deleting a album +my $properties = $album->get; +$album->purge; + +is( + $album, undef, + "Album is undefined", +); + +is( + WebGUI::Asset->newByDynamicClass($session, $properties->{assetId}), undef, + "Album no longer able to be instanciated", +); + diff --git a/t/Asset/Wobject/GalleryAlbum/addArchive.t b/t/Asset/Wobject/GalleryAlbum/addArchive.t new file mode 100644 index 000000000..29d9844b3 --- /dev/null +++ b/t/Asset/Wobject/GalleryAlbum/addArchive.t @@ -0,0 +1,69 @@ +# $vim: syntax=perl +#------------------------------------------------------------------- +# 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 the permissions of GalleryAlbum assets + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; +use WebGUI::Test::Maker::Permission; + +#---------------------------------------------------------------------------- +# Init +my $maker = WebGUI::Test::Maker::Permission->new; +my $session = WebGUI::Test->session; +my $node = WebGUI::Asset->getImportNode($session); +my $versionTag = WebGUI::VersionTag->getWorking($session); +$versionTag->set({name=>"Album Test"}); +my $gallery + = $node->addChild({ + className => "WebGUI::Asset::Wobject::Gallery", + groupIdAddComment => 2, # Registered Users + groupIdAddFile => 2, # Registered Users + groupIdView => 7, # Everyone + groupIdEdit => 3, # Admins + ownerUserId => 3, # Admin + }); +my $album + = $gallery->addChild({ + className => "WebGUI::Asset::Wobject::GalleryAlbum", + ownerUserId => "3", # Admin + }); + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + +#---------------------------------------------------------------------------- +# Tests +plan tests => 2; + +#---------------------------------------------------------------------------- +# Test the addArchive sub +# elephant_images.zip contains three jpgs: Aana1.jpg, Aana2.jpg, Aana3.jpg +$album->addArchive( WebGUI::Test->getTestCollateralPath('elephant_images.zip') ); +my $images = $album->getLineage(['descendants'], { returnObjects => 1 }); + +is( scalar @$images, 3, "addArchive() adds one asset per image" ); +cmp_deeply( + [ map { $_->get("filename") } @$images ], + bag( "Aana1.jpg", "Aana2.jpg", "Aana3.jpg" ), +); + +#---------------------------------------------------------------------------- +# Test the www_addArchive page diff --git a/t/Asset/Wobject/GalleryAlbum/delete.t b/t/Asset/Wobject/GalleryAlbum/delete.t new file mode 100644 index 000000000..f49603f93 --- /dev/null +++ b/t/Asset/Wobject/GalleryAlbum/delete.t @@ -0,0 +1,96 @@ +# $vim: syntax=perl +#------------------------------------------------------------------- +# 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 the deleting of GalleryAlbums + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; + +#---------------------------------------------------------------------------- +# 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); +$versionTag->set({name=>"Album Test"}); +my $gallery + = $node->addChild({ + className => "WebGUI::Asset::Wobject::Gallery", + groupIdAddComment => 2, # Registered Users + groupIdAddFile => 2, # Registered Users + groupIdView => 7, # Everyone + groupIdEdit => 3, # Admins + ownerUserId => 3, # Admin + }); +my $album + = $gallery->addChild({ + className => "WebGUI::Asset::Wobject::GalleryAlbum", + ownerUserId => "3", # Admin + }); + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + +#---------------------------------------------------------------------------- +# Tests +plan no_plan => 1; + +#---------------------------------------------------------------------------- +# Delete page gives error for those who can't edit the GalleryAlbum +$maker->prepare({ + object => $album, + method => "www_delete", + test_privilege => "insufficient", + userId => 1, +}, { + object => $album, + method => "www_deleteConfirm", + test_privilege => "insufficient", + userId => 1, +}); +$maker->run; + +#---------------------------------------------------------------------------- +# Delete confirm page appears for those allowed to edit the GalleryAlbum +$maker->prepare({ + object => $album, + method => "www_delete", + test_regex => [ qr/func=deleteConfirm/, ], + userId => 3, +}); +$maker->run; + +#---------------------------------------------------------------------------- +# www_deleteConfirm deletes the asset +my $assetId = $album->getId; +$maker->prepare({ + object => $album, + method => "www_deleteConfirm", + test_regex => [ qr/has been deleted/, ], + userId => 3, +}); +$maker->run; + +is( + WebGUI::Asset->newByDynamicClass( $session, $assetId ), + undef, + "GalleryAlbum cannot be instanciated after www_deleteConfirm", +); + diff --git a/t/Asset/Wobject/GalleryAlbum/permission.t b/t/Asset/Wobject/GalleryAlbum/permission.t new file mode 100644 index 000000000..2d91342ed --- /dev/null +++ b/t/Asset/Wobject/GalleryAlbum/permission.t @@ -0,0 +1,107 @@ +#------------------------------------------------------------------- +# 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 the permissions of GalleryAlbum assets + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; +use WebGUI::Test::Maker::Permission; + +#---------------------------------------------------------------------------- +# Init +my $maker = WebGUI::Test::Maker::Permission->new; +my $session = WebGUI::Test->session; +my $node = WebGUI::Asset->getImportNode($session); + +my %user; +$user{"2"} = WebGUI::User->new( $session, "new" ); +$user{"2"}->addToGroups( ['2'] ); # Registered user + +my $versionTag = WebGUI::VersionTag->getWorking($session); +$versionTag->set({name=>"Album Test"}); +my $gallery + = $node->addChild({ + className => "WebGUI::Asset::Wobject::Gallery", + groupIdAddComment => 2, # Registered Users + groupIdAddFile => 2, # Registered Users + groupIdView => 7, # Everyone + groupIdEdit => 3, # Admins + ownerUserId => 3, # Admin + }); +my $album + = $gallery->addChild({ + className => "WebGUI::Asset::Wobject::GalleryAlbum", + ownerUserId => "3", # Admin + }); + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); + $user{"2"}->delete; +} + +#---------------------------------------------------------------------------- +# Tests +plan no_plan => 1; + +#---------------------------------------------------------------------------- +# By default, GalleryAlbum inherits its permissions from the Gallery, but +# only the owner of the GalleryAlbum is allowed to add files +$maker->prepare({ + object => $album, + method => "canView", + pass => [ 1, 3, $user{"2"}, ], +}, { + object => $album, + method => "canEdit", + pass => [ 3, ], + fail => [ 1, $user{"2"}, ], +}, { + object => $album, + method => "canAddFile", + pass => [ 3, ], + fail => [ 1, $user{"2"}, ], +}, { + object => $album, + method => "canAddComment", + pass => [ 3, $user{"2"}, ], + fail => [ 1, ], +}); +$maker->run; + +#---------------------------------------------------------------------------- +# GalleryAlbums with "allowComments" false do not allow anyone to comment +$album->update({ allowComments => 0 }); +$maker->prepare({ + object => $album, + method => "canComment", + fail => [ 1, 3, $user{"2"}, ], +}); +$maker->run; + +#---------------------------------------------------------------------------- +# GalleryAlbum with "othersCanAdd" true allows anyone who can add files to +# the Gallery to add files to this GalleryAlbum +$album->update({ othersCanAdd => 1 }); +$maker->prepare({ + object => $album, + method => "canAddFile", + pass => [ 3, $user{"2"}, ], + fail => [ 1, ], +}); +$maker->run; + diff --git a/t/Asset/Wobject/GalleryAlbum/rss.t b/t/Asset/Wobject/GalleryAlbum/rss.t new file mode 100644 index 000000000..1f96b786c --- /dev/null +++ b/t/Asset/Wobject/GalleryAlbum/rss.t @@ -0,0 +1,66 @@ +# $vim: syntax=perl +#------------------------------------------------------------------- +# 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 the rss view of GalleryAlbums + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; +use WebGUI::Test::Maker::HTML; + +#---------------------------------------------------------------------------- +# 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); +$versionTag->set({name=>"Album Test"}); +my $gallery + = $node->addChild({ + className => "WebGUI::Asset::Wobject::Gallery", + groupIdAddComment => 2, # Registered Users + groupIdAddFile => 2, # Registered Users + groupIdView => 7, # Everyone + groupIdEdit => 3, # Admins + ownerUserId => 3, # Admin + }); +my $album + = $gallery->addChild({ + className => "WebGUI::Asset::Wobject::GalleryAlbum", + ownerUserId => "3", # Admin + }); +my @photos; +for my $i ( 0 .. 5 ) { + $photos[ $i ] + = $album->addChild({ + className => "WebGUI::Asset::File::Image::Photo", + filename => "$i.jpg", + }); +} + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + +#---------------------------------------------------------------------------- +# Tests +plan no_plan => 1; + +#---------------------------------------------------------------------------- +# Test www_viewRss + diff --git a/t/Asset/Wobject/GalleryAlbum/slideshow.t b/t/Asset/Wobject/GalleryAlbum/slideshow.t new file mode 100644 index 000000000..36513b46e --- /dev/null +++ b/t/Asset/Wobject/GalleryAlbum/slideshow.t @@ -0,0 +1,69 @@ +# $vim: syntax=perl +#------------------------------------------------------------------- +# 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 the slideshow view of GalleryAlbums + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; +use WebGUI::Test::Maker::HTML; + +#---------------------------------------------------------------------------- +# 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); +$versionTag->set({name=>"Album Test"}); +my $gallery + = $node->addChild({ + className => "WebGUI::Asset::Wobject::Gallery", + groupIdAddComment => 2, # Registered Users + groupIdAddFile => 2, # Registered Users + groupIdView => 7, # Everyone + groupIdEdit => 3, # Admins + ownerUserId => 3, # Admin + }); +my $album + = $gallery->addChild({ + className => "WebGUI::Asset::Wobject::GalleryAlbum", + ownerUserId => "3", # Admin + }); +my @photos; +for my $i ( 0 .. 5 ) { + $photos[ $i ] + = $album->addChild({ + className => "WebGUI::Asset::File::Image::Photo", + filename => "$i.jpg", + }); +} + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + +#---------------------------------------------------------------------------- +# Tests +plan no_plan => 1; + +#---------------------------------------------------------------------------- +# Test view_slideshow + +#---------------------------------------------------------------------------- +# Test www_slideshow + diff --git a/t/Asset/Wobject/GalleryAlbum/thumbnails.t b/t/Asset/Wobject/GalleryAlbum/thumbnails.t new file mode 100644 index 000000000..578dcd34c --- /dev/null +++ b/t/Asset/Wobject/GalleryAlbum/thumbnails.t @@ -0,0 +1,69 @@ +# $vim: syntax=perl +#------------------------------------------------------------------- +# 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 the thumbnails view of GalleryAlbums + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; +use WebGUI::Test::Maker::HTML; + +#---------------------------------------------------------------------------- +# 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); +$versionTag->set({name=>"Album Test"}); +my $gallery + = $node->addChild({ + className => "WebGUI::Asset::Wobject::Gallery", + groupIdAddComment => 2, # Registered Users + groupIdAddFile => 2, # Registered Users + groupIdView => 7, # Everyone + groupIdEdit => 3, # Admins + ownerUserId => 3, # Admin + }); +my $album + = $gallery->addChild({ + className => "WebGUI::Asset::Wobject::GalleryAlbum", + ownerUserId => "3", # Admin + }); +my @photos; +for my $i ( 0 .. 5 ) { + $photos[ $i ] + = $album->addChild({ + className => "WebGUI::Asset::File::Image::Photo", + filename => "$i.jpg", + }); +} + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + +#---------------------------------------------------------------------------- +# Tests +plan no_plan => 1; + +#---------------------------------------------------------------------------- +# Test view_thumbnails + +#---------------------------------------------------------------------------- +# Test www_thumbnails + diff --git a/t/Asset/Wobject/GalleryAlbum/view.t b/t/Asset/Wobject/GalleryAlbum/view.t new file mode 100644 index 000000000..b12a5f56e --- /dev/null +++ b/t/Asset/Wobject/GalleryAlbum/view.t @@ -0,0 +1,132 @@ +# $vim: syntax=perl +#------------------------------------------------------------------- +# 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 the default view and associated subs + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; +use Test::Deep; +use WebGUI::Test::Maker::HTML; + +#---------------------------------------------------------------------------- +# 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); +$versionTag->set({name=>"Album Test"}); +my $gallery + = $node->addChild({ + className => "WebGUI::Asset::Wobject::Gallery", + groupIdAddComment => 2, # Registered Users + groupIdAddFile => 2, # Registered Users + groupIdView => 2, # Registered Users + groupIdEdit => 3, # Admins + ownerUserId => 3, # Admin + }); +my $album + = $gallery->addChild({ + className => "WebGUI::Asset::Wobject::GalleryAlbum", + ownerUserId => "3", # Admin + }); +my @photos; +for my $i ( 0 .. 5 ) { + $photos[ $i ] + = $album->addChild({ + className => "WebGUI::Asset::File::Image::Photo", + filename => "$i.jpg", + }); +} + +#---------------------------------------------------------------------------- +# Cleanup +END { + $versionTag->rollback(); +} + +#---------------------------------------------------------------------------- +# Tests +plan no_plan => 1; + +#---------------------------------------------------------------------------- +# Test getFileIds and getFilePaginator +cmp_bag( $album->getFileIds, [ map { $_->getId } @photos ] ); + +my $p = $album->getFilePaginator; +isa_ok( $p, "WebGUI::Paginator" ); +cmp_deeply( $p->getPageData, subbagof( map { $_->getId } @photos ) ); + +#---------------------------------------------------------------------------- +# Test getTemplateVars + +# Is a superset of Asset->get +# NOTE: url is Asset->getUrl +cmp_deeply( $album->getTemplateVars, superhashof( { %{$album->get}, url => $album->getUrl, } ) ); + +# Contains specific keys/values +my $expected = { + "url_addPhoto" + => all( + re( qr/className=WebGUI::Asset::File::Image::Photo/ ), + re( qr/func=add/ ), + re( $album->getUrl ), + ), + "url_addNoClass" + => all( + re( $album->getUrl ), + re( qr/func=add$/ ), + ), + "url_slideshow" + => all( + re( $album->getUrl ), + re( qr/func=slideshow/ ), + ), + "url_thumbnails" + => all( + re( $album->getUrl ), + re( qr/func=thumbnails/ ), + ), + "url_viewRss" + => all( + re( $album->getUrl ), + re( qr/func=viewRss/ ), + ), + "ownerUsername" + => WebGUI::User->new($session, 3)->username, +}; + +cmp_deeply( $album->getTemplateVars, superhashof( $expected ) ); + +#---------------------------------------------------------------------------- +# Test appendTemplateVarsFileLoop +$expected = { + "file_loop" => bag( map { $_->getTemplateVars } @photos ), +}; +cmp_deeply( + $album->appendTemplateVarsFileLoop({},$self->getFilePaginator->getPageData), + $expected +); + +#---------------------------------------------------------------------------- +# Test www_view() for those without permission to view +$maker->prepare({ + object => $album, + method => "www_view", + test_privilege => "insufficient", +}); +$maker->run; + diff --git a/t/Form.t b/t/Form.t new file mode 100644 index 000000000..01838ddd1 --- /dev/null +++ b/t/Form.t @@ -0,0 +1,104 @@ +# vim:syntax=perl +#------------------------------------------------------------------- +# 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"; +use Test::More; +use Test::Deep; +use WebGUI::Test; + +#---------------------------------------------------------------------------- +# Init +my $session = WebGUI::Test->session; + +#---------------------------------------------------------------------------- +# Cleanup +END { + +} + +#---------------------------------------------------------------------------- +# Tests + +plan tests => 5; # Increment this number for each test you create + +#---------------------------------------------------------------------------- +# Test the formHeader method + +ok( + !eval{ WebGUI::Form::formHeader( "" ); 1 }, + "formHeader() dies if first parameter is not WebGUI Session", +); + +ok( + !eval{ WebGUI::Form::formHeader( $session, ['foo'] ); 1 }, + "formHeader() dies if second parameter is not hash reference", +); + +# Test the defaults for formHeader() +my $testDefaults = all( + re( q{]*>} ), + re( q{action=} ), + re( q{enctype="multipart/form-data"} ), + re( q{method="post"} ), +); + +cmp_deeply( + WebGUI::Form::formHeader( $session ), + $testDefaults, + "formHeader called without an options hashref", +); + +# Test options passed into formHeader() +my $testWithOptions = all( + re( q{]*>} ), + re( q{action="action"} ), + re( q{enctype="enctype"} ), + re( q{method="method"} ), +); + +cmp_deeply( + WebGUI::Form::formHeader( $session, { + action => "action", + enctype => "enctype", + method => "method", + } ), + $testWithOptions, + "formHeader called with an options hashref", +); + +# Test "action" option containing query parameters +my $testHiddenElements = all( + re( q{ "action?func=edit;a=1&b=2", + }), + $testHiddenElements, + "formHeader 'action' option containing query parameters", +); + +#---------------------------------------------------------------------------- + +TODO: { + local $TODO = "Some things on the TODO list"; + # Test the formFooter method + # Test that the autohandler works properly +} diff --git a/t/Form/SelectRichEditor.t b/t/Form/SelectRichEditor.t new file mode 100644 index 000000000..60c04e88a --- /dev/null +++ b/t/Form/SelectRichEditor.t @@ -0,0 +1,59 @@ +# $vim: syntax=perl +#------------------------------------------------------------------- +# 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 the SelectRichEditor form control + +use Scalar::Util qw( blessed ); +use WebGUI::Test; +use WebGUI::Session; +use Test::More; +use Test::Deep; + +use WebGUI::Form::SelectRichEditor; + +#---------------------------------------------------------------------------- +# Init +my $session = WebGUI::Test->session; +my $root = WebGUI::Asset->getRoot( $session ); + +#---------------------------------------------------------------------------- +# Cleanup +END { + +} + +#---------------------------------------------------------------------------- +# Tests +plan tests => 1; + +#---------------------------------------------------------------------------- +# Test that SelectRichEditor control contains all RichEdit assets. +my $richEditAssets + = $root->getLineage( ['descendants'], { + returnObjects => 1, + includeOnlyClasses => ['WebGUI::Asset::RichEdit'], + }); +my $richEditOptions + = { + map { $_->getId => $_->get("title") } @$richEditAssets + }; + +my $control + = WebGUI::Form::SelectRichEditor->new( $session, { name => "richEditId" } ); +cmp_deeply( + $control->get("options"), + $richEditOptions, + "SelectRichEditor control has options for all Rich Editors in this site", +); diff --git a/t/Storage.t b/t/Storage.t index 5196ec4e3..fd6f6129d 100644 --- a/t/Storage.t +++ b/t/Storage.t @@ -203,6 +203,16 @@ ok (-e $storage1->getPath("testfile-hash.file"), 'addFileFromHashRef creates fil my $thawedHash = $storage1->getFileContentsAsHashref('testfile-hash.file'); cmp_deeply($storageHash, $thawedHash, 'getFileContentsAsHashref: thawed hash correctly'); +#################################################### +# +# copyFile +# +#################################################### + +$storage1->copyFile("testfile-hash.file", "testfile-hash-copied.file"); +ok (-e $storage1->getPath("testfile-hash-copied.file"),'copyFile created file with new name'); +ok (-e $storage1->getPath("testfile-hash.file"), "copyFile original file still exists"); + #################################################### # # renameFile diff --git a/t/_test.skeleton b/t/_test.skeleton index 55ab13a23..bf4795eab 100644 --- a/t/_test.skeleton +++ b/t/_test.skeleton @@ -1,24 +1,40 @@ +# vim:syntax=perl #------------------------------------------------------------------- -# WebGUI is Copyright 2001-2006 Plain Black Corporation. +# 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 -#------------------------------------------------------------------- +#------------------------------------------------------------------ + +# Write a little about what this script tests. +# +# use FindBin; use strict; use lib "$FindBin::Bin/lib"; -use WebGUI::Test; +use Test::More; use WebGUI::Session; +use WebGUI::Test; -# load your modules here +#---------------------------------------------------------------------------- +# Init +my $session = WebGUI::Test->session; -use Test::More tests => 1; # increment this value for each test you create +#---------------------------------------------------------------------------- +# Cleanup +END { -my $session = WebGUI::Test->session; +} +#---------------------------------------------------------------------------- +# Tests + +plan tests => 1; # Increment this number for each test you create + +#---------------------------------------------------------------------------- # put your tests here diff --git a/t/lib/WebGUI/Test.pm b/t/lib/WebGUI/Test.pm index 724d8577a..857d83ac1 100644 --- a/t/lib/WebGUI/Test.pm +++ b/t/lib/WebGUI/Test.pm @@ -9,6 +9,7 @@ use Config qw[]; use IO::Handle qw[]; use File::Spec qw[]; use Test::MockObject::Extends; +use WebGUI::PseudoRequest; ##Hack to get ALL test output onto STDOUT. use Test::Builder; @@ -136,7 +137,8 @@ of options with keys outlined below. =cut sub getPage { - my $session = shift; # The session object + my $class = shift; + my $session = $SESSION; # The session object my $asset = shift; # The asset object my $page = shift; # The page subroutine my $optionsRef = shift; # A hashref of options diff --git a/t/lib/WebGUI/Test/Maker/HTML.pm b/t/lib/WebGUI/Test/Maker/HTML.pm index 5607ac546..404be27d0 100644 --- a/t/lib/WebGUI/Test/Maker/HTML.pm +++ b/t/lib/WebGUI/Test/Maker/HTML.pm @@ -2,6 +2,7 @@ package WebGUI::Test::Maker::HTML; use base 'WebGUI::Test::Maker'; use Scalar::Util qw( blessed ); +use Carp qw( croak ); use Test::More; @@ -72,6 +73,8 @@ Create a new WebGUI::Test::Maker::HTML object. Get a setting. Set L for a list of settings. +=cut + #---------------------------------------------------------------------------- =head2 plan @@ -187,7 +190,7 @@ sub prepare { croak("Couldn't prepare: Test $test_num, test_regex is not an array reference") if $test->{test_regex} && ref $test->{test_regex} ne "ARRAY"; croak("Couldn't prepare: Test $test_num, $test->{test_privilege} is not a valid test_privilege value (adminOnly, insufficient, noAccess, notMember, vitalComponent)") - if $test->{test_privilege} && $test->{test_privilege} =~ m/adminOnly|insufficient|noAccess|notMember|vitalComponent/; + if $test->{test_privilege} && $test->{test_privilege} !~ m/adminOnly|insufficient|noAccess|notMember|vitalComponent/; push @{$self->{_tests}}, $test; } diff --git a/t/lib/WebGUI/Test/Maker/Permission.pm b/t/lib/WebGUI/Test/Maker/Permission.pm index 926b82d24..f57a23423 100644 --- a/t/lib/WebGUI/Test/Maker/Permission.pm +++ b/t/lib/WebGUI/Test/Maker/Permission.pm @@ -55,9 +55,13 @@ Test::More Create a new WebGUI::Test::Maker::Permission object. +=cut + =head2 get -Get a setting. Set L for a list of settings. +Get a setting. See C for a list of settings. + +=cut #---------------------------------------------------------------------------- @@ -116,11 +120,13 @@ The permissions method to test =item pass -An array reference of userIds that should pass the permissions test. +An array reference of userIds or WebGUI::User objects that should pass the +permissions test. =item fail -An array reference of userIds that should fail the permissions test. +An array reference of userIds or WebGUI::User objects that should fail the +permissions test. =back @@ -146,6 +152,16 @@ sub prepare { croak("Couldn't prepare: Test $test_num, fail is not an array reference") if $test->{fail} && ref $test->{fail} ne "ARRAY"; + # Make sure pass and fail arrayrefs are userIds + for my $array ( $test->{pass, fail} ) { + for ( my $i = 0; $i < @$array; $i++ ) { + # If is a User object, replace with userId + if ( blessed $array->[$i] && $array->[$i]->isa("WebGUI::User") ) { + $array->[$i] = $array->[$i]->userId; + } + } + } + push @{$self->{_tests}}, $test; } diff --git a/t/supporting_collateral/elephant_images.zip b/t/supporting_collateral/elephant_images.zip new file mode 100644 index 000000000..a04bc6e69 Binary files /dev/null and b/t/supporting_collateral/elephant_images.zip differ diff --git a/t/supporting_collateral/lamp.jpg b/t/supporting_collateral/lamp.jpg new file mode 100644 index 000000000..3145fa816 Binary files /dev/null and b/t/supporting_collateral/lamp.jpg differ diff --git a/www/extras/yui-carousel/carousel-min.js b/www/extras/yui-carousel/carousel-min.js new file mode 100644 index 000000000..a08d8d82f --- /dev/null +++ b/www/extras/yui-carousel/carousel-min.js @@ -0,0 +1,115 @@ +/** + * Copyright (c) 2006-2007, Bill W. Scott. All rights reserved. + * This work is licensed under the Creative Commons Attribution 2.5 License. To view a copy + * of this license, visit http://creativecommons.org/licenses/by/2.5/ or send a letter to + * Creative Commons, 543 Howard Street, 5th Floor, San Francisco, California, 94105, USA. + * This work was created by Bill Scott (billwscott.com, looksgoodworkswell.com). + * The only attribution I require is to keep this notice of copyright & license + * in this original source file. + * Version 0.6.1 - 07.08.2007 + */ + + +YAHOO.namespace("extension");YAHOO.extension.Carousel=function(carouselElementID,carouselCfg){this.init(carouselElementID,carouselCfg);};YAHOO.extension.Carousel.prototype={UNBOUNDED_SIZE:1000000,init:function(carouselElementID,carouselCfg){var oThis=this;this.getCarouselItem=this.getItem;var carouselListClass="carousel-list";var carouselClipRegionClass="carousel-clip-region";var carouselNextClass="carousel-next";var carouselPrevClass="carousel-prev";this._carouselElemID=carouselElementID;this.carouselElem=YAHOO.util.Dom.get(carouselElementID);this._prevEnabled=true;this._nextEnabled=true;this.cfg=new YAHOO.util.Config(this);this.cfg.addProperty("scrollBeforeAmount",{value:0,handler:function(type,args,carouselElem){},validator:oThis.cfg.checkNumber});this.cfg.addProperty("scrollAfterAmount",{value:0,handler:function(type,args,carouselElem){},validator:oThis.cfg.checkNumber});this.cfg.addProperty("loadOnStart",{value:true,handler:function(type,args,carouselElem){},validator:oThis.cfg.checkBoolean});this.cfg.addProperty("orientation",{value:"horizontal",handler:function(type,args,carouselElem){oThis.reload();},validator:function(orientation){if(typeof orientation=="string"){return("horizontal,vertical".indexOf(orientation.toLowerCase())!=-1);}else{return false;}}});this.cfg.addProperty("size",{value:this.UNBOUNDED_SIZE,handler:function(type,args,carouselElem){oThis.reload();},validator:oThis.cfg.checkNumber});this.cfg.addProperty("numVisible",{value:3,handler:function(type,args,carouselElem){oThis.reload();},validator:oThis.cfg.checkNumber});this.cfg.addProperty("firstVisible",{value:1,handler:function(type,args,carouselElem){oThis.moveTo(args[0]);},validator:oThis.cfg.checkNumber});this.cfg.addProperty("scrollInc",{value:3,handler:function(type,args,carouselElem){},validator:oThis.cfg.checkNumber});this.cfg.addProperty("animationSpeed",{value:0.25,handler:function(type,args,carouselElem){oThis.animationSpeed=args[0];},validator:oThis.cfg.checkNumber});this.cfg.addProperty("animationMethod",{value:YAHOO.util.Easing.easeOut,handler:function(type,args,carouselElem){}});this.cfg.addProperty("animationCompleteHandler",{value:null,handler:function(type,args,carouselElem){if(oThis._animationCompleteEvt){oThis._animationCompleteEvt.unsubscribe(oThis._currAnimationCompleteHandler,oThis);} +oThis._currAnimationCompleteHandler=args[0];if(oThis._currAnimationCompleteHandler){if(!oThis._animationCompleteEvt){oThis._animationCompleteEvt=new YAHOO.util.CustomEvent("onAnimationComplete",oThis);} +oThis._animationCompleteEvt.subscribe(oThis._currAnimationCompleteHandler,oThis);}}});this.cfg.addProperty("autoPlay",{value:0,handler:function(type,args,carouselElem){var autoPlay=args[0];if(autoPlay>0) +oThis.startAutoPlay();else +oThis.stopAutoPlay();}});this.cfg.addProperty("wrap",{value:false,handler:function(type,args,carouselElem){},validator:oThis.cfg.checkBoolean});this.cfg.addProperty("navMargin",{value:0,handler:function(type,args,carouselElem){oThis.calculateSize();},validator:oThis.cfg.checkNumber});this.cfg.addProperty("revealAmount",{value:0,handler:function(type,args,carouselElem){oThis.reload();},validator:oThis.cfg.checkNumber});this.cfg.addProperty("prevElementID",{value:null,handler:function(type,args,carouselElem){if(oThis._carouselPrev){YAHOO.util.Event.removeListener(oThis._carouselPrev,"click",oThis._scrollPrev);} +oThis._prevElementID=args[0];if(oThis._prevElementID==null){oThis._carouselPrev=YAHOO.util.Dom.getElementsByClassName(carouselPrevClass,"div",oThis.carouselElem)[0];}else{oThis._carouselPrev=YAHOO.util.Dom.get(oThis._prevElementID);} +YAHOO.util.Event.addListener(oThis._carouselPrev,"click",oThis._scrollPrev,oThis);}});this.cfg.addProperty("prevElement",{value:null,handler:function(type,args,carouselElem){if(oThis._carouselPrev){YAHOO.util.Event.removeListener(oThis._carouselPrev,"click",oThis._scrollPrev);} +oThis._prevElementID=args[0];if(oThis._prevElementID==null){oThis._carouselPrev=YAHOO.util.Dom.getElementsByClassName(carouselPrevClass,"div",oThis.carouselElem)[0];}else{oThis._carouselPrev=YAHOO.util.Dom.get(oThis._prevElementID);} +YAHOO.util.Event.addListener(oThis._carouselPrev,"click",oThis._scrollPrev,oThis);}});this.cfg.addProperty("nextElementID",{value:null,handler:function(type,args,carouselElem){if(oThis._carouselNext){YAHOO.util.Event.removeListener(oThis._carouselNext,"click",oThis._scrollNext);} +oThis._nextElementID=args[0];if(oThis._nextElementID==null){oThis._carouselNext=YAHOO.util.Dom.getElementsByClassName(carouselNextClass,"div",oThis.carouselElem);}else{oThis._carouselNext=YAHOO.util.Dom.get(oThis._nextElementID);} +if(oThis._carouselNext){YAHOO.util.Event.addListener(oThis._carouselNext,"click",oThis._scrollNext,oThis);}}});this.cfg.addProperty("nextElement",{value:null,handler:function(type,args,carouselElem){if(oThis._carouselNext){YAHOO.util.Event.removeListener(oThis._carouselNext,"click",oThis._scrollNext);} +oThis._nextElementID=args[0];if(oThis._nextElementID==null){oThis._carouselNext=YAHOO.util.Dom.getElementsByClassName(carouselNextClass,"div",oThis.carouselElem);}else{oThis._carouselNext=YAHOO.util.Dom.get(oThis._nextElementID);} +if(oThis._carouselNext){YAHOO.util.Event.addListener(oThis._carouselNext,"click",oThis._scrollNext,oThis);}}});this.cfg.addProperty("loadInitHandler",{value:null,handler:function(type,args,carouselElem){if(oThis._loadInitHandlerEvt){oThis._loadInitHandlerEvt.unsubscribe(oThis._currLoadInitHandler,oThis);} +oThis._currLoadInitHandler=args[0];if(oThis._currLoadInitHandler){if(!oThis._loadInitHandlerEvt){oThis._loadInitHandlerEvt=new YAHOO.util.CustomEvent("onLoadInit",oThis);} +oThis._loadInitHandlerEvt.subscribe(oThis._currLoadInitHandler,oThis);}}});this.cfg.addProperty("loadNextHandler",{value:null,handler:function(type,args,carouselElem){if(oThis._loadNextHandlerEvt){oThis._loadNextHandlerEvt.unsubscribe(oThis._currLoadNextHandler,oThis);} +oThis._currLoadNextHandler=args[0];if(oThis._currLoadNextHandler){if(!oThis._loadNextHandlerEvt){oThis._loadNextHandlerEvt=new YAHOO.util.CustomEvent("onLoadNext",oThis);} +oThis._loadNextHandlerEvt.subscribe(oThis._currLoadNextHandler,oThis);}}});this.cfg.addProperty("loadPrevHandler",{value:null,handler:function(type,args,carouselElem){if(oThis._loadPrevHandlerEvt){oThis._loadPrevHandlerEvt.unsubscribe(oThis._currLoadPrevHandler,oThis);} +oThis._currLoadPrevHandler=args[0];if(oThis._currLoadPrevHandler){if(!oThis._loadPrevHandlerEvt){oThis._loadPrevHandlerEvt=new YAHOO.util.CustomEvent("onLoadPrev",oThis);} +oThis._loadPrevHandlerEvt.subscribe(oThis._currLoadPrevHandler,oThis);}}});this.cfg.addProperty("prevButtonStateHandler",{value:null,handler:function(type,args,carouselElem){if(oThis._currPrevButtonStateHandler){oThis._prevButtonStateHandlerEvt.unsubscribe(oThis._currPrevButtonStateHandler,oThis);} +oThis._currPrevButtonStateHandler=args[0];if(oThis._currPrevButtonStateHandler){if(!oThis._prevButtonStateHandlerEvt){oThis._prevButtonStateHandlerEvt=new YAHOO.util.CustomEvent("onPrevButtonStateChange",oThis);} +oThis._prevButtonStateHandlerEvt.subscribe(oThis._currPrevButtonStateHandler,oThis);}}});this.cfg.addProperty("nextButtonStateHandler",{value:null,handler:function(type,args,carouselElem){if(oThis._currNextButtonStateHandler){oThis._nextButtonStateHandlerEvt.unsubscribe(oThis._currNextButtonStateHandler,oThis);} +oThis._currNextButtonStateHandler=args[0];if(oThis._currNextButtonStateHandler){if(!oThis._nextButtonStateHandlerEvt){oThis._nextButtonStateHandlerEvt=new YAHOO.util.CustomEvent("onNextButtonStateChange",oThis);} +oThis._nextButtonStateHandlerEvt.subscribe(oThis._currNextButtonStateHandler,oThis);}}});if(carouselCfg){this.cfg.applyConfig(carouselCfg);} +this._origFirstVisible=this.cfg.getProperty("firstVisible");this._currLoadInitHandler=this.cfg.getProperty("loadInitHandler");this._currLoadNextHandler=this.cfg.getProperty("loadNextHandler");this._currLoadPrevHandler=this.cfg.getProperty("loadPrevHandler");this._currPrevButtonStateHandler=this.cfg.getProperty("prevButtonStateHandler");this._currNextButtonStateHandler=this.cfg.getProperty("nextButtonStateHandler");this._currAnimationCompleteHandler=this.cfg.getProperty("animationCompleteHandler");this._nextElementID=this.cfg.getProperty("nextElementID");if(!this._nextElementID) +this._nextElementID=this.cfg.getProperty("nextElement");this._prevElementID=this.cfg.getProperty("prevElementID");if(!this._prevElementID) +this._prevElementID=this.cfg.getProperty("prevElement");this._autoPlayTimer=null;this._priorLastVisible=this._priorFirstVisible=this.cfg.getProperty("firstVisible");this._lastPrebuiltIdx=0;this.carouselList=YAHOO.util.Dom.getElementsByClassName(carouselListClass,"ul",this.carouselElem)[0];if(this._nextElementID==null){this._carouselNext=YAHOO.util.Dom.getElementsByClassName(carouselNextClass,"div",this.carouselElem)[0];}else{this._carouselNext=YAHOO.util.Dom.get(this._nextElementID);} +if(this._prevElementID==null){this._carouselPrev=YAHOO.util.Dom.getElementsByClassName(carouselPrevClass,"div",this.carouselElem)[0];}else{this._carouselPrev=YAHOO.util.Dom.get(this._prevElementID);} +this._clipReg=YAHOO.util.Dom.getElementsByClassName(carouselClipRegionClass,"div",this.carouselElem)[0];if(this.isVertical()){YAHOO.util.Dom.addClass(this.carouselList,"carousel-vertical");} +this._scrollNextAnim=new YAHOO.util.Motion(this.carouselList,this.scrollNextParams,this.cfg.getProperty("animationSpeed"),this.cfg.getProperty("animationMethod"));this._scrollPrevAnim=new YAHOO.util.Motion(this.carouselList,this.scrollPrevParams,this.cfg.getProperty("animationSpeed"),this.cfg.getProperty("animationMethod"));if(this._carouselNext){YAHOO.util.Event.addListener(this._carouselNext,"click",this._scrollNext,this);} +if(this._carouselPrev){YAHOO.util.Event.addListener(this._carouselPrev,"click",this._scrollPrev,this);} +var loadInitHandler=this.cfg.getProperty("loadInitHandler");if(loadInitHandler){this._loadInitHandlerEvt=new YAHOO.util.CustomEvent("onLoadInit",this);this._loadInitHandlerEvt.subscribe(loadInitHandler,this);} +var loadNextHandler=this.cfg.getProperty("loadNextHandler");if(loadNextHandler){this._loadNextHandlerEvt=new YAHOO.util.CustomEvent("onLoadNext",this);this._loadNextHandlerEvt.subscribe(loadNextHandler,this);} +var loadPrevHandler=this.cfg.getProperty("loadPrevHandler");if(loadPrevHandler){this._loadPrevHandlerEvt=new YAHOO.util.CustomEvent("onLoadPrev",this);this._loadPrevHandlerEvt.subscribe(loadPrevHandler,this);} +var animationCompleteHandler=this.cfg.getProperty("animationCompleteHandler");if(animationCompleteHandler){this._animationCompleteEvt=new YAHOO.util.CustomEvent("onAnimationComplete",this);this._animationCompleteEvt.subscribe(animationCompleteHandler,this);} +var prevButtonStateHandler=this.cfg.getProperty("prevButtonStateHandler");if(prevButtonStateHandler){this._prevButtonStateHandlerEvt=new YAHOO.util.CustomEvent("onPrevButtonStateChange",this);this._prevButtonStateHandlerEvt.subscribe(prevButtonStateHandler,this);} +var nextButtonStateHandler=this.cfg.getProperty("nextButtonStateHandler");if(nextButtonStateHandler){this._nextButtonStateHandlerEvt=new YAHOO.util.CustomEvent("onNextButtonStateChange",this);this._nextButtonStateHandlerEvt.subscribe(nextButtonStateHandler,this);} +var visibleExtent=this._calculateVisibleExtent();YAHOO.util.Event.onAvailable(this._carouselElemID+"-item-"+ +visibleExtent.start,this._calculateSize,this);if(this.cfg.getProperty("loadOnStart")) +this._loadInitial();},clear:function(){var loadInitHandler=this.cfg.getProperty("loadInitHandler");if(loadInitHandler){this._removeChildrenFromNode(this.carouselList);this._lastPrebuiltIdx=0;} +this.stopAutoPlay();this._priorLastVisible=this._priorFirstVisible=this._origFirstVisible;this.cfg.setProperty("firstVisible",this._origFirstVisible,true);this.moveTo(this._origFirstVisible);},reload:function(numVisible){if(this._isValidObj(numVisible)){this.cfg.setProperty("numVisible",numVisible);} +this.clear();var visibleExtent=this._calculateVisibleExtent();YAHOO.util.Event.onAvailable(this._carouselElemID+"-item-"+visibleExtent.start,this._calculateSize,this);this._loadInitial();},load:function(){var visibleExtent=this._calculateVisibleExtent();YAHOO.util.Event.onAvailable(this._carouselElemID+"-item-"+visibleExtent.start,this._calculateSize,this);this._loadInitial();},addItem:function(idx,innerHTMLOrElem,itemClass){if(idx>this.cfg.getProperty("size")){return null;} +var liElem=this.getItem(idx);if(!this._isValidObj(liElem)){liElem=this._createItem(idx,innerHTMLOrElem);this.carouselList.appendChild(liElem);}else if(this._isValidObj(liElem.placeholder)){var newLiElem=this._createItem(idx,innerHTMLOrElem);this.carouselList.replaceChild(newLiElem,liElem);liElem=newLiElem;} +if(this._isValidObj(itemClass)){YAHOO.util.Dom.addClass(liElem,itemClass);} +if(this.isVertical()) +setTimeout(function(){liElem.style.display="block";},1);return liElem;},insertBefore:function(refIdx,innerHTML){if(refIdx>=this.cfg.getProperty("size")){return null;} +if(refIdx<1){refIdx=1;} +var insertionIdx=refIdx-1;if(insertionIdx>this._lastPrebuiltIdx){this._prebuildItems(this._lastPrebuiltIdx,refIdx);} +var liElem=this._insertBeforeItem(refIdx,innerHTML);this._enableDisableControls();return liElem;},insertAfter:function(refIdx,innerHTML){if(refIdx>this.cfg.getProperty("size")){refIdx=this.cfg.getProperty("size");} +var insertionIdx=refIdx+1;if(insertionIdx>this._lastPrebuiltIdx){this._prebuildItems(this._lastPrebuiltIdx,insertionIdx+1);} +var liElem=this._insertAfterItem(refIdx,innerHTML);if(insertionIdx>this.cfg.getProperty("size")){this.cfg.setProperty("size",insertionIdx,true);} +this._enableDisableControls();return liElem;},scrollNext:function(){this._scrollNext(null,this);this._autoPlayTimer=null;if(this.cfg.getProperty("autoPlay")!==0){this._autoPlayTimer=this.startAutoPlay();}},scrollPrev:function(){this._scrollPrev(null,this);},scrollTo:function(newStart){this._position(newStart,true);},moveTo:function(newStart){this._position(newStart,false);},startAutoPlay:function(interval){if(this._isValidObj(interval)){this.cfg.setProperty("autoPlay",interval,true);} +if(this._autoPlayTimer!==null){return this._autoPlayTimer;} +var oThis=this;var autoScroll=function(){oThis.scrollNext();};this._autoPlayTimer=setTimeout(autoScroll,this.cfg.getProperty("autoPlay"));return this._autoPlayTimer;},stopAutoPlay:function(){if(this._autoPlayTimer!==null){clearTimeout(this._autoPlayTimer);this._autoPlayTimer=null;}},isVertical:function(){return(this.cfg.getProperty("orientation")!="horizontal");},isItemLoaded:function(idx){var liElem=this.getItem(idx);if(this._isValidObj(liElem)&&!this._isValidObj(liElem.placeholder)){return true;} +return false;},getItem:function(idx){var elemName=this._carouselElemID+"-item-"+idx;var liElem=YAHOO.util.Dom.get(elemName);return liElem;},show:function(){YAHOO.util.Dom.setStyle(this.carouselElem,"display","block");this.calculateSize();},hide:function(){YAHOO.util.Dom.setStyle(this.carouselElem,"display","none");},calculateSize:function(){var ulKids=this.carouselList.childNodes;var li=null;for(var i=0;ithis._lastPrebuiltIdx)?idx:this._lastPrebuiltIdx;},_createItem:function(idx,innerHTMLOrElem){if(idx<1)return;var liElem=document.createElement("li");liElem.id=this._carouselElemID+"-item-"+idx;if(typeof(innerHTMLOrElem)==="string"){liElem.innerHTML=innerHTMLOrElem;}else{liElem.appendChild(innerHTMLOrElem);} +return liElem;},_insertAfterItem:function(refIdx,innerHTMLOrElem){return this._insertBeforeItem(refIdx+1,innerHTMLOrElem);},_insertBeforeItem:function(refIdx,innerHTMLOrElem){var refItem=this.getItem(refIdx);var size=this.cfg.getProperty("size");if(size!=this.UNBOUNDED_SIZE){this.cfg.setProperty("size",size+1,true);} +for(var i=this._lastPrebuiltIdx;i>=refIdx;i--){var anItem=this.getItem(i);if(this._isValidObj(anItem)){anItem.id=this._carouselElemID+"-item-"+(i+1);}} +var liElem=this._createItem(refIdx,innerHTMLOrElem);var insertedItem=this.carouselList.insertBefore(liElem,refItem);this._lastPrebuiltIdx+=1;return liElem;},insertAfterEnd:function(innerHTMLOrElem){return this.insertAfter(this.cfg.getProperty("size"),innerHTMLOrElem);},_position:function(newStart,showAnimation){var currStart=this._priorFirstVisible;if(newStart>currStart){var inc=newStart-currStart;this._scrollNextInc(inc,showAnimation);}else{var dec=currStart-newStart;this._scrollPrevInc(dec,showAnimation);}},_scrollPrev:function(e,carousel){if(e!==null){carousel.stopAutoPlay();} +if(carousel._scrollPrevAnim.isAnimated()){return false;} +carousel._scrollPrevInc(carousel.cfg.getProperty("scrollInc"),(carousel.cfg.getProperty("animationSpeed")!==0));},_scrollNext:function(e,carousel){if(e!==null){carousel.stopAutoPlay();} +if(carousel._scrollNextAnim.isAnimated()){return false;} +carousel._scrollNextInc(carousel.cfg.getProperty("scrollInc"),(carousel.cfg.getProperty("animationSpeed")!==0));},_handleAnimationComplete:function(type,args,argList){var carousel=argList[0];var direction=argList[1];carousel._animationCompleteEvt.fire(direction);},_areAllItemsLoaded:function(first,last){var itemsLoaded=true;for(var i=first;i<=last;i++){var liElem=this.getItem(i);if(!this._isValidObj(liElem)){this._prebuildLiElem(i);itemsLoaded=false;}else if(this._isValidObj(liElem.placeholder)){itemsLoaded=false;}} +return itemsLoaded;},_prebuildItems:function(first,last){for(var i=first;i<=last;i++){var liElem=this.getItem(i);if(!this._isValidObj(liElem)){this._prebuildLiElem(i);}}},_isExtraRevealed:function(){return(this.cfg.getProperty("revealAmount")>0);},_scrollNextInc:function(inc,showAnimation){var numVisible=this.cfg.getProperty("numVisible");var currStart=this._priorFirstVisible;var currEnd=this._priorLastVisible;var size=this.cfg.getProperty("size");var scrollExtent=this._calculateAllowableScrollExtent();if(this.cfg.getProperty("wrap")&&currEnd==scrollExtent.end){this.scrollTo(scrollExtent.start);return;} +var newStart=currStart+inc;var newEnd=newStart+numVisible-1;if(newEnd>scrollExtent.end){newEnd=scrollExtent.end;newStart=newEnd-numVisible+1;} +inc=newStart-currStart;this.cfg.setProperty("firstVisible",newStart,true);if(inc>0){if(this._isValidObj(this.cfg.getProperty("loadNextHandler"))){var visibleExtent=this._calculateVisibleExtent(newStart,newEnd);var cacheStart=(currEnd+1)scrollExtent.end){newEnd=scrollExtent.end;newStart=newEnd-numVisible+1;} +dec=currStart-newStart;this.cfg.setProperty("firstVisible",newStart,true);if(dec>0){if(this._isValidObj(this.cfg.getProperty("loadPrevHandler"))){var visibleExtent=this._calculateVisibleExtent(newStart,newEnd);var cacheEnd=(currStart-1)>visibleExtent.end?(currStart-1):visibleExtent.end;var alreadyCached=this._areAllItemsLoaded(visibleExtent.start,cacheEnd);this._loadPrevHandlerEvt.fire(visibleExtent.start,visibleExtent.end,alreadyCached);} +if(showAnimation){var prevParams={points:{by:[this.scrollAmountPerInc*dec,0]}};if(this.isVertical()){prevParams={points:{by:[0,this.scrollAmountPerInc*dec]}};} +this._scrollPrevAnim=new YAHOO.util.Motion(this.carouselList,prevParams,this.cfg.getProperty("animationSpeed"),this.cfg.getProperty("animationMethod"));if(this.cfg.getProperty("animationCompleteHandler")){this._scrollPrevAnim.onComplete.subscribe(this._handleAnimationComplete,[this,"prev"]);} +this._scrollPrevAnim.animate();}else{if(this.isVertical()){var currY=YAHOO.util.Dom.getY(this.carouselList);YAHOO.util.Dom.setY(this.carouselList,currY+ +this.scrollAmountPerInc*dec);}else{var currX=YAHOO.util.Dom.getX(this.carouselList);YAHOO.util.Dom.setX(this.carouselList,currX+ +this.scrollAmountPerInc*dec);}}} +this._priorFirstVisible=newStart;this._priorLastVisible=newEnd;this._enableDisableControls();return false;},_enableDisableControls:function(){var firstVisible=this.cfg.getProperty("firstVisible");var lastVisible=this.getLastVisible();var scrollExtent=this._calculateAllowableScrollExtent();if(this._prevEnabled){if(firstVisible===scrollExtent.start){this._disablePrev();}} +if(this._prevEnabled===false){if(firstVisible>scrollExtent.start){this._enablePrev();}} +if(this._nextEnabled){if(lastVisible===scrollExtent.end){this._disableNext();}} +if(this._nextEnabled===false){if(lastVisiblesize?size:end;var extent={start:start,end:end};this._firstItemRevealed=-1;this._lastItemRevealed=-1;if(this._isExtraRevealed()){if(start>1){this._firstItemRevealed=start-1;extent.start=this._firstItemRevealed;} +if(endYAHOO.util.Easing + * method. + */ + this.cfg.addProperty("animationMethod", { + value: YAHOO.util.Easing.easeOut, + handler: function(type, args, carouselElem) { + } + } ); + + /** + * animationCompleteHandler property. + * JavaScript function that is called when the Carousel finishes animation + * after a next or previous nagivation. + * Only invoked if animationSpeed > 0. + * Two parameters are passed: type (set to 'onAnimationComplete') and + * args array (args[0] = direction [either: 'next' or 'previous']). + */ + this.cfg.addProperty("animationCompleteHandler", { + value:null, + handler: function(type, args, carouselElem) { + if(oThis._animationCompleteEvt) { + oThis._animationCompleteEvt.unsubscribe(oThis._currAnimationCompleteHandler, oThis); + } + oThis._currAnimationCompleteHandler = args[0]; + if(oThis._currAnimationCompleteHandler) { + if(!oThis._animationCompleteEvt) { + oThis._animationCompleteEvt = new YAHOO.util.CustomEvent("onAnimationComplete", oThis); + } + oThis._animationCompleteEvt.subscribe(oThis._currAnimationCompleteHandler, oThis); + } + } + } ); + + /** + * autoPlay property. + * Specifies how many milliseconds to periodically auto scroll the content. + * If set to 0 (default) then autoPlay is turned off. + * If the user interacts by clicking left or right navigation, autoPlay is turned off. + * You can restart autoPlay by calling the startAutoPlay(). + * If you externally control navigation (with your own event handlers) + * then you may want to turn off the autoPlay by callingstopAutoPlay() + */ + this.cfg.addProperty("autoPlay", { + value:0, + handler: function(type, args, carouselElem) { + var autoPlay = args[0]; + if(autoPlay > 0) + oThis.startAutoPlay(); + else + oThis.stopAutoPlay(); + } + } ); + + /** + * wrap property. + * Specifies whether to wrap when at the end of scrolled content. When the end is reached, + * the carousel will scroll backwards to the item 1 (the animationSpeed parameter is used to + * determine how quickly it should animate back to the start.) + * Ignored if the size attribute is not explicitly set + * (i.e., value equals YAHOO.extension.Carousel.UNBOUNDED_SIZE) + */ + this.cfg.addProperty("wrap", { + value:false, + handler: function(type, args, carouselElem) { + }, + validator: oThis.cfg.checkBoolean + } ); + + /** + * navMargin property. + * The margin space for the navigation controls. This is only useful for horizontal carousels + * in which you have embedded navigation controls. + * The navMargin allocates space between the left and right margins + * (each navMargin wide) giving space for the navigation controls. + */ + this.cfg.addProperty("navMargin", { + value:0, + handler: function(type, args, carouselElem) { + oThis.calculateSize(); + }, + validator: oThis.cfg.checkNumber + } ); + + /** + * revealAmount property. + * The amount to reveal of what comes before and what comes after the firstVisible and + * the lastVisible items. Setting this will provide a slight preview that something + * exists before and after, providing an additional hint for the user. + * The revealAmount will reveal the specified number of pixels for any item + * before the firstVisible and an item after the lastVisible. Additionall, the + * loadNextHandler and loadPrevHandler methods will be passed a start or end that guarantees + * the revealed item will be loaded (if set to non-zero). + */ + this.cfg.addProperty("revealAmount", { + value:0, + handler: function(type, args, carouselElem) { + oThis.reload(); + }, + validator: oThis.cfg.checkNumber + } ); + + // For backward compatibility. Deprecated. + this.cfg.addProperty("prevElementID", { + value: null, + handler: function(type, args, carouselElem) { + if(oThis._carouselPrev) { + YAHOO.util.Event.removeListener(oThis._carouselPrev, "click", oThis._scrollPrev); + } + oThis._prevElementID = args[0]; + if(oThis._prevElementID == null) { + oThis._carouselPrev = YAHOO.util.Dom.getElementsByClassName(carouselPrevClass, + "div", oThis.carouselElem)[0]; + } else { + oThis._carouselPrev = YAHOO.util.Dom.get(oThis._prevElementID); + } + YAHOO.util.Event.addListener(oThis._carouselPrev, "click", oThis._scrollPrev, oThis); + } + }); + + /** + * prevElement property. + * An element or elements that will provide the previous navigation control. + * prevElement may be a single element or an array of elements. The values may be strings denoting + * the ID of the element or the object itself. + * If supplied, then events are wired to this control to fire scroll events to move the carousel to + * the previous content. + * You may want to provide your own interaction for controlling the carousel. If + * so leave this unset and provide your own event handling mechanism. + */ + this.cfg.addProperty("prevElement", { + value:null, + handler: function(type, args, carouselElem) { + if(oThis._carouselPrev) { + YAHOO.util.Event.removeListener(oThis._carouselPrev, "click", oThis._scrollPrev); + } + oThis._prevElementID = args[0]; + if(oThis._prevElementID == null) { + oThis._carouselPrev = YAHOO.util.Dom.getElementsByClassName(carouselPrevClass, + "div", oThis.carouselElem)[0]; + } else { + oThis._carouselPrev = YAHOO.util.Dom.get(oThis._prevElementID); + } + YAHOO.util.Event.addListener(oThis._carouselPrev, "click", oThis._scrollPrev, oThis); + } + } ); + + // For backward compatibility. Deprecated. + this.cfg.addProperty("nextElementID", { + value: null, + handler: function(type, args, carouselElem) { + if(oThis._carouselNext) { + YAHOO.util.Event.removeListener(oThis._carouselNext, "click", oThis._scrollNext); + } + oThis._nextElementID = args[0]; + if(oThis._nextElementID == null) { + oThis._carouselNext = YAHOO.util.Dom.getElementsByClassName(carouselNextClass, + "div", oThis.carouselElem); + } else { + oThis._carouselNext = YAHOO.util.Dom.get(oThis._nextElementID); + } + if(oThis._carouselNext) { + YAHOO.util.Event.addListener(oThis._carouselNext, "click", oThis._scrollNext, oThis); + } + } + }); + + /** + * nextElement property. + * An element or elements that will provide the next navigation control. + * nextElement may be a single element or an array of elements. The values may be strings denoting + * the ID of the element or the object itself. + * If supplied, then events are wired to this control to fire scroll events to move the carousel to + * the next content. + * You may want to provide your own interaction for controlling the carousel. If + * so leave this unset and provide your own event handling mechanism. + */ + this.cfg.addProperty("nextElement", { + value:null, + handler: function(type, args, carouselElem) { + if(oThis._carouselNext) { + YAHOO.util.Event.removeListener(oThis._carouselNext, "click", oThis._scrollNext); + } + oThis._nextElementID = args[0]; + if(oThis._nextElementID == null) { + oThis._carouselNext = YAHOO.util.Dom.getElementsByClassName(carouselNextClass, + "div", oThis.carouselElem); + } else { + oThis._carouselNext = YAHOO.util.Dom.get(oThis._nextElementID); + } + if(oThis._carouselNext) { + YAHOO.util.Event.addListener(oThis._carouselNext, "click", oThis._scrollNext, oThis); + } + } + } ); + + /** + * loadInitHandler property. + * JavaScript function that is called when the Carousel needs to load + * the initial set of visible items. Two parameters are passed: + * type (set to 'onLoadInit') and an argument array (args[0] = start index, args[1] = last index). + */ + this.cfg.addProperty("loadInitHandler", { + value:null, + handler: function(type, args, carouselElem) { + if(oThis._loadInitHandlerEvt) { + oThis._loadInitHandlerEvt.unsubscribe(oThis._currLoadInitHandler, oThis); + } + oThis._currLoadInitHandler = args[0]; + if(oThis._currLoadInitHandler) { + if(!oThis._loadInitHandlerEvt) { + oThis._loadInitHandlerEvt = new YAHOO.util.CustomEvent("onLoadInit", oThis); + } + oThis._loadInitHandlerEvt.subscribe(oThis._currLoadInitHandler, oThis); + } + } + } ); + + /** + * loadNextHandler property. + * JavaScript function that is called when the Carousel needs to load + * the next set of items (in response to the user navigating to the next set.) + * Two parameters are passed: type (set to 'onLoadNext') and + * args array (args[0] = start index, args[1] = last index). + */ + this.cfg.addProperty("loadNextHandler", { + value:null, + handler: function(type, args, carouselElem) { + if(oThis._loadNextHandlerEvt) { + oThis._loadNextHandlerEvt.unsubscribe(oThis._currLoadNextHandler, oThis); + } + oThis._currLoadNextHandler = args[0]; + if(oThis._currLoadNextHandler) { + if(!oThis._loadNextHandlerEvt) { + oThis._loadNextHandlerEvt = new YAHOO.util.CustomEvent("onLoadNext", oThis); + } + oThis._loadNextHandlerEvt.subscribe(oThis._currLoadNextHandler, oThis); + } + } + } ); + + /** + * loadPrevHandler property. + * JavaScript function that is called when the Carousel needs to load + * the previous set of items (in response to the user navigating to the previous set.) + * Two parameters are passed: type (set to 'onLoadPrev') and args array + * (args[0] = start index, args[1] = last index). + */ + this.cfg.addProperty("loadPrevHandler", { + value:null, + handler: function(type, args, carouselElem) { + if(oThis._loadPrevHandlerEvt) { + oThis._loadPrevHandlerEvt.unsubscribe(oThis._currLoadPrevHandler, oThis); + } + oThis._currLoadPrevHandler = args[0]; + if(oThis._currLoadPrevHandler) { + if(!oThis._loadPrevHandlerEvt) { + oThis._loadPrevHandlerEvt = new YAHOO.util.CustomEvent("onLoadPrev", oThis); + } + oThis._loadPrevHandlerEvt.subscribe(oThis._currLoadPrevHandler, oThis); + } + } + } ); + + /** + * prevButtonStateHandler property. + * JavaScript function that is called when the enabled state of the + * 'previous' control is changing. The responsibility of + * this method is to enable or disable the 'previous' control. + * Two parameters are passed to this method: type + * (which is set to "onPrevButtonStateChange") and args, + * an array that contains two values. + * The parameter args[0] is a flag denoting whether the 'previous' control + * is being enabled or disabled. The parameter args[1] is the element object + * derived from the prevElement parameter. + * If you do not supply a prevElement then you will need to track + * the elements that you would want to enable/disable while handling the state change. + */ + this.cfg.addProperty("prevButtonStateHandler", { + value:null, + handler: function(type, args, carouselElem) { + if(oThis._currPrevButtonStateHandler) { + oThis._prevButtonStateHandlerEvt.unsubscribe(oThis._currPrevButtonStateHandler, oThis); + } + + oThis._currPrevButtonStateHandler = args[0]; + + if(oThis._currPrevButtonStateHandler) { + if(!oThis._prevButtonStateHandlerEvt) { + oThis._prevButtonStateHandlerEvt = new YAHOO.util.CustomEvent("onPrevButtonStateChange", oThis); + } + oThis._prevButtonStateHandlerEvt.subscribe(oThis._currPrevButtonStateHandler, oThis); + } + } + } ); + + /** + * nextButtonStateHandler property. + * JavaScript function that is called when the enabled state of the + * 'next' control is changing. The responsibility of + * this method is to enable or disable the 'next' control. + * Two parameters are passed to this method: type + * (which is set to "onNextButtonStateChange") and args, + * an array that contains two values. + * The parameter args[0] is a flag denoting whether the 'next' control + * is being enabled or disabled. The parameter args[1] is the element object + * derived from the nextElement parameter. + * If you do not supply a nextElement then you will need to track + * the elements that you would want to enable/disable while handling the state change. + */ + this.cfg.addProperty("nextButtonStateHandler", { + value:null, + handler: function(type, args, carouselElem) { + if(oThis._currNextButtonStateHandler) { + oThis._nextButtonStateHandlerEvt.unsubscribe(oThis._currNextButtonStateHandler, oThis); + } + oThis._currNextButtonStateHandler = args[0]; + + if(oThis._currNextButtonStateHandler) { + if(!oThis._nextButtonStateHandlerEvt) { + oThis._nextButtonStateHandlerEvt = new YAHOO.util.CustomEvent("onNextButtonStateChange", oThis); + } + oThis._nextButtonStateHandlerEvt.subscribe(oThis._currNextButtonStateHandler, oThis); + } + } + } ); + + + if(carouselCfg) { + this.cfg.applyConfig(carouselCfg); + } + + this._origFirstVisible = this.cfg.getProperty("firstVisible"); + + // keep a copy of curr handler so it can be removed when a new handler is set + this._currLoadInitHandler = this.cfg.getProperty("loadInitHandler"); + this._currLoadNextHandler = this.cfg.getProperty("loadNextHandler"); + this._currLoadPrevHandler = this.cfg.getProperty("loadPrevHandler"); + this._currPrevButtonStateHandler = this.cfg.getProperty("prevButtonStateHandler"); + this._currNextButtonStateHandler = this.cfg.getProperty("nextButtonStateHandler"); + this._currAnimationCompleteHandler = this.cfg.getProperty("animationCompleteHandler"); + + this._nextElementID = this.cfg.getProperty("nextElementID"); + if(!this._nextElementID) + this._nextElementID = this.cfg.getProperty("nextElement"); + + this._prevElementID = this.cfg.getProperty("prevElementID"); + if(!this._prevElementID) + this._prevElementID = this.cfg.getProperty("prevElement"); + + this._autoPlayTimer = null; + this._priorLastVisible = this._priorFirstVisible = this.cfg.getProperty("firstVisible"); + this._lastPrebuiltIdx = 0; +// this._currSize = 0; + + // prefetch elements + this.carouselList = YAHOO.util.Dom.getElementsByClassName(carouselListClass, + "ul", this.carouselElem)[0]; + + if(this._nextElementID == null) { + this._carouselNext = YAHOO.util.Dom.getElementsByClassName(carouselNextClass, + "div", this.carouselElem)[0]; + } else { + this._carouselNext = YAHOO.util.Dom.get(this._nextElementID); + } + + if(this._prevElementID == null) { + this._carouselPrev = YAHOO.util.Dom.getElementsByClassName(carouselPrevClass, + "div", this.carouselElem)[0]; + } else { + this._carouselPrev = YAHOO.util.Dom.get(this._prevElementID); + } + + this._clipReg = YAHOO.util.Dom.getElementsByClassName(carouselClipRegionClass, + "div", this.carouselElem)[0]; + + // add a style class dynamically so that the correct styles get applied for a vertical carousel + if(this.isVertical()) { + YAHOO.util.Dom.addClass(this.carouselList, "carousel-vertical"); + } + + // initialize the animation objects for next/previous + this._scrollNextAnim = new YAHOO.util.Motion(this.carouselList, this.scrollNextParams, + this.cfg.getProperty("animationSpeed"), this.cfg.getProperty("animationMethod")); + this._scrollPrevAnim = new YAHOO.util.Motion(this.carouselList, this.scrollPrevParams, + this.cfg.getProperty("animationSpeed"), this.cfg.getProperty("animationMethod")); + + // If they supplied a nextElementID then wire an event listener for the click + if(this._carouselNext) { + YAHOO.util.Event.addListener(this._carouselNext, "click", this._scrollNext, this); + } + + // If they supplied a prevElementID then wire an event listener for the click + if(this._carouselPrev) { + YAHOO.util.Event.addListener(this._carouselPrev, "click", this._scrollPrev, this); + } + + // Wire up the various event handlers that they might have supplied + var loadInitHandler = this.cfg.getProperty("loadInitHandler"); + if(loadInitHandler) { + this._loadInitHandlerEvt = new YAHOO.util.CustomEvent("onLoadInit", this); + this._loadInitHandlerEvt.subscribe(loadInitHandler, this); + } + var loadNextHandler = this.cfg.getProperty("loadNextHandler"); + if(loadNextHandler) { + this._loadNextHandlerEvt = new YAHOO.util.CustomEvent("onLoadNext", this); + this._loadNextHandlerEvt.subscribe(loadNextHandler, this); + } + var loadPrevHandler = this.cfg.getProperty("loadPrevHandler"); + if(loadPrevHandler) { + this._loadPrevHandlerEvt = new YAHOO.util.CustomEvent("onLoadPrev", this); + this._loadPrevHandlerEvt.subscribe(loadPrevHandler, this); + } + var animationCompleteHandler = this.cfg.getProperty("animationCompleteHandler"); + if(animationCompleteHandler) { + this._animationCompleteEvt = new YAHOO.util.CustomEvent("onAnimationComplete", this); + this._animationCompleteEvt.subscribe(animationCompleteHandler, this); + } + var prevButtonStateHandler = this.cfg.getProperty("prevButtonStateHandler"); + if(prevButtonStateHandler) { + this._prevButtonStateHandlerEvt = new YAHOO.util.CustomEvent("onPrevButtonStateChange", + this); + this._prevButtonStateHandlerEvt.subscribe(prevButtonStateHandler, this); + } + var nextButtonStateHandler = this.cfg.getProperty("nextButtonStateHandler"); + if(nextButtonStateHandler) { + this._nextButtonStateHandlerEvt = new YAHOO.util.CustomEvent("onNextButtonStateChange", this); + this._nextButtonStateHandlerEvt.subscribe(nextButtonStateHandler, this); + } + + // Since loading may take some time, wire up a listener to fire when at least the first + // element actually gets loaded + var visibleExtent = this._calculateVisibleExtent(); + YAHOO.util.Event.onAvailable(this._carouselElemID + "-item-"+ + visibleExtent.start, this._calculateSize, this); + + // Call the initial loading sequence + if(this.cfg.getProperty("loadOnStart")) + this._loadInitial(); + + }, + + // /////////////////// Public API ////////////////////////////////////////// + + /** + * Clears all items from the list and resets to the carousel to its original initial state. + */ + clear: function() { + // remove all items from the carousel for dynamic content + var loadInitHandler = this.cfg.getProperty("loadInitHandler"); + if(loadInitHandler) { + this._removeChildrenFromNode(this.carouselList); + this._lastPrebuiltIdx = 0; + } + // turn off autoplay + this.stopAutoPlay(); // should we only turn this off for dynamic during reload? + + this._priorLastVisible = this._priorFirstVisible = this._origFirstVisible; + + // is this redundant since moveTo will set this? + this.cfg.setProperty("firstVisible", this._origFirstVisible, true); + this.moveTo(this._origFirstVisible); + }, + + /** + * Clears all items from the list and calls the loadInitHandler to load new items into the list. + * The carousel size is reset to the original size set during creation. + * @param {number} numVisible Optional parameter: numVisible. + * If set, the carousel will resize on the reload to show numVisible items. + */ + reload: function(numVisible) { + // this should be deprecated, not needed since can be set via property change + if(this._isValidObj(numVisible)) { + this.cfg.setProperty("numVisible", numVisible); + } + this.clear(); + + // clear resets back to start + var visibleExtent = this._calculateVisibleExtent(); + YAHOO.util.Event.onAvailable(this._carouselElemID+"-item-"+visibleExtent.start, + this._calculateSize, this); + this._loadInitial(); + + }, + + load: function() { + var visibleExtent = this._calculateVisibleExtent(); + + YAHOO.util.Event.onAvailable(this._carouselElemID + "-item-"+visibleExtent.start, + this._calculateSize, this); + this._loadInitial(); + }, + + /** + * With patch from Dan Hobbs for handling unordered loading. + * @param {number} idx which item in the list to potentially create. + * If item already exists it will not create a new item. + * @param {string} innerHTML The innerHTML string to use to create the contents of an LI element. + * @param {string} itemClass A class optionally supplied to add to the LI item created + */ + addItem: function(idx, innerHTMLOrElem, itemClass) { + + if(idx > this.cfg.getProperty("size")) { + return null; + } + + var liElem = this.getItem(idx); + + // Need to create the li + if(!this._isValidObj(liElem)) { + liElem = this._createItem(idx, innerHTMLOrElem); + this.carouselList.appendChild(liElem); + + } else if(this._isValidObj(liElem.placeholder)) { + var newLiElem = this._createItem(idx, innerHTMLOrElem); + this.carouselList.replaceChild(newLiElem, liElem); + liElem = newLiElem; + } + + // if they supplied an item class add it to the element + if(this._isValidObj(itemClass)){ + YAHOO.util.Dom.addClass(liElem, itemClass); + } + + /** + * Not real comfortable with this line of code. It exists for vertical + * carousels for IE6. For some reason LI elements are not displaying + * unless you after the fact set the display to block. (Even though + * the CSS sets vertical LIs to display:block) + */ + if(this.isVertical()) + setTimeout( function() { liElem.style.display="block"; }, 1 ); + + return liElem; + + }, + + /** + * Inserts a new LI item before the index specified. Uses the innerHTML to create the contents of the new LI item + * @param {number} refIdx which item in the list to insert this item before. + * @param {string} innerHTML The innerHTML string to use to create the contents of an LI element. + */ + insertBefore: function(refIdx, innerHTML) { + // don't allow insertion beyond the size + if(refIdx >= this.cfg.getProperty("size")) { + return null; + } + + if(refIdx < 1) { + refIdx = 1; + } + + var insertionIdx = refIdx - 1; + + if(insertionIdx > this._lastPrebuiltIdx) { + this._prebuildItems(this._lastPrebuiltIdx, refIdx); // is this right? + } + + var liElem = this._insertBeforeItem(refIdx, innerHTML); + + this._enableDisableControls(); + + return liElem; + }, + + /** + * Inserts a new LI item after the index specified. Uses the innerHTML to create the contents of the new LI item + * @param {number} refIdx which item in the list to insert this item after. + * @param {string} innerHTML The innerHTML string to use to create the contents of an LI element. + */ + insertAfter: function(refIdx, innerHTML) { + + if(refIdx > this.cfg.getProperty("size")) { + refIdx = this.cfg.getProperty("size"); + } + + var insertionIdx = refIdx + 1; + + // if we are inserting this item past where we have prebuilt items, then + // prebuild up to this point. + if(insertionIdx > this._lastPrebuiltIdx) { + this._prebuildItems(this._lastPrebuiltIdx, insertionIdx+1); + } + + var liElem = this._insertAfterItem(refIdx, innerHTML); + + if(insertionIdx > this.cfg.getProperty("size")) { + this.cfg.setProperty("size", insertionIdx, true); + } + + this._enableDisableControls(); + + return liElem; + }, + + /** + * Simulates a next button event. Causes the carousel to scroll the next set of content into view. + */ + scrollNext: function() { + this._scrollNext(null, this); + + // we know the timer has expired. + //if(this._autoPlayTimer) clearTimeout(this._autoPlayTimer); + this._autoPlayTimer = null; + if(this.cfg.getProperty("autoPlay") !== 0) { + this._autoPlayTimer = this.startAutoPlay(); + } + }, + + /** + * Simulates a prev button event. Causes the carousel to scroll the previous set of content into view. + */ + scrollPrev: function() { + this._scrollPrev(null, this); + }, + + /** + * Scrolls the content to place itemNum as the start item in the view + * (if size is specified, the last element will not scroll past the end.). + * Uses current animation speed & method. + * @param {number} newStart The item to scroll to. + */ + scrollTo: function(newStart) { + this._position(newStart, true); + }, + + /** + * Moves the content to place itemNum as the start item in the view + * (if size is specified, the last element will not scroll past the end.) + * Ignores animation speed & method; moves directly to the item. + * Note that you can also set the firstVisible property upon initialization + * to get the carousel to start at a position different than 1. + * @param {number} newStart The item to move directly to. + */ + moveTo: function(newStart) { + this._position(newStart, false); + }, + + /** + * Starts up autoplay. If autoPlay has been stopped (by calling stopAutoPlay or by user interaction), + * you can start it back up by using this method. + * @param {number} interval optional parameter that sets the interval + * for auto play the next time that autoplay fires. + */ + startAutoPlay: function(interval) { + // if interval is passed as arg, then set autoPlay to this interval. + if(this._isValidObj(interval)) { + this.cfg.setProperty("autoPlay", interval, true); + } + + // if we already are playing, then do nothing. + if(this._autoPlayTimer !== null) { + return this._autoPlayTimer; + } + + var oThis = this; + var autoScroll = function() { oThis.scrollNext(); }; + this._autoPlayTimer = setTimeout( autoScroll, this.cfg.getProperty("autoPlay") ); + + return this._autoPlayTimer; + }, + + /** + * Stops autoplay. Useful for when you want to control what events will stop the autoplay feature. + * Call startAutoPlay() to restart autoplay. + */ + stopAutoPlay: function() { + if (this._autoPlayTimer !== null) { + clearTimeout(this._autoPlayTimer); + this._autoPlayTimer = null; + } + }, + + /** + * Returns whether the carousel's orientation is set to vertical. + */ + isVertical: function() { + return (this.cfg.getProperty("orientation") != "horizontal"); + }, + + + /** + * Check to see if an element (by index) has been loaded or not. If the item is simply pre-built, but not + * loaded this will return false. If the item has not been pre-built it will also return false. + * @param {number} idx Index of the element to check load status for. + */ + isItemLoaded: function(idx) { + var liElem = this.getItem(idx); + + // if item exists and is not a placeholder, then it is already loaded. + if(this._isValidObj(liElem) && !this._isValidObj(liElem.placeholder)) { + return true; + } + + return false; + }, + + /** + * Lookup the element object for a carousel list item by index. + * @param {number} idx Index of the element to lookup. + */ + getItem: function(idx) { + var elemName = this._carouselElemID + "-item-" + idx; + var liElem = YAHOO.util.Dom.get(elemName); + return liElem; + }, + + show: function() { + YAHOO.util.Dom.setStyle(this.carouselElem, "display", "block"); + this.calculateSize(); + }, + + hide: function() { + YAHOO.util.Dom.setStyle(this.carouselElem, "display", "none"); + }, + + calculateSize: function() { + var ulKids = this.carouselList.childNodes; + var li = null; + for(var i=0; i 1 then this will adjust the scrolled location + var currY = YAHOO.util.Dom.getY(this.carouselList); + YAHOO.util.Dom.setY(this.carouselList, currY - this.scrollAmountPerInc*(firstVisible-1)); + + // --- HORIZONTAL + } else { + YAHOO.util.Dom.addClass(this.carouselList, "carousel-horizontal"); + + var upl = this._getStyleVal(this.carouselList, "paddingLeft"); + var upr = this._getStyleVal(this.carouselList, "paddingRight"); + var uml = this._getStyleVal(this.carouselList, "marginLeft") + var umr = this._getStyleVal(this.carouselList, "marginRight") + var ulPaddingWidth = upl + upr + uml + umr; + + var liMarginWidth = ml + mr; + var liPaddingMarginWidth = liMarginWidth + pr + pl; + + // try to reveal the amount taking into consideration the margin & padding. + // This guarantees that this.revealAmount of pixels will be shown on both sides + var revealAmt = (this._isExtraRevealed()) ? + (this.cfg.getProperty("revealAmount")+(liPaddingMarginWidth)/2) : 0; + + var liWidth = li.offsetWidth; + this.scrollAmountPerInc = liWidth + liMarginWidth; + + this._clipReg.style.width = + (this.scrollAmountPerInc*numVisible + revealAmt*2) + "px"; + this.carouselElem.style.width = + (this.scrollAmountPerInc*numVisible + navMargin*2 + revealAmt*2 + + ulPaddingWidth) + "px"; + + var revealLeft = (this._isExtraRevealed()) ? + (revealAmt - (Math.abs(mr-ml)+Math.abs(pr-pl))/2 - (uml+upl) + ) : + 0; + YAHOO.util.Dom.setStyle(this.carouselList, "position", "relative"); + YAHOO.util.Dom.setStyle(this.carouselList, "left", "" + revealLeft + "px"); + + // if we set the initial start > 1 then this will adjust the scrolled location + var currX = YAHOO.util.Dom.getX(this.carouselList); + YAHOO.util.Dom.setX(this.carouselList, currX - this.scrollAmountPerInc*(firstVisible-1)); + } + }, + + // Hides the cfg object + setProperty: function(property, value, silent) { + this.cfg.setProperty(property, value, silent); + }, + + getProperty: function(property) { + return this.cfg.getProperty(property); + }, + + getFirstItemRevealed: function() { + return this._firstItemRevealed; + }, + getLastItemRevealed: function() { + return this._lastItemRevealed; + }, + + // Just for convenience and to be symmetrical with getFirstVisible + getFirstVisible: function() { + return this.cfg.getProperty("firstVisible"); + }, + + getLastVisible: function() { + var firstVisible = this.cfg.getProperty("firstVisible"); + var numVisible = this.cfg.getProperty("numVisible"); + + return firstVisible + numVisible - 1; + }, + + // /////////////////// PRIVATE API ////////////////////////////////////////// + _getStyleVal : function(li, style, returnFloat) { + var styleValStr = YAHOO.util.Dom.getStyle(li, style); + + var styleVal = returnFloat ? parseFloat(styleValStr) : parseInt(styleValStr, 10); + if(style=="height" && isNaN(styleVal)) { + styleVal = li.offsetHeight; + } else if(isNaN(styleVal)) { + styleVal = 0; + } + return styleVal; + }, + + _calculateSize: function(me) { + me.calculateSize(); + me.show(); + //YAHOO.util.Dom.setStyle(me.carouselElem, "visibility", "visible"); + }, + + // From Mike Chambers: http://weblogs.macromedia.com/mesh/archives/2006/01/removing_html_e.html + _removeChildrenFromNode: function(node) + { + if(!this._isValidObj(node)) + { + return; + } + + var len = node.childNodes.length; + + while (node.hasChildNodes()) + { + node.removeChild(node.firstChild); + } + }, + + _prebuildLiElem: function(idx) { + if(idx < 1) return; + + + var liElem = document.createElement("li"); + liElem.id = this._carouselElemID + "-item-" + idx; + // this is default flag to know that we're not really loaded yet. + liElem.placeholder = true; + this.carouselList.appendChild(liElem); + + this._lastPrebuiltIdx = (idx > this._lastPrebuiltIdx) ? idx : this._lastPrebuiltIdx; + }, + + _createItem: function(idx, innerHTMLOrElem) { + if(idx < 1) return; + + + var liElem = document.createElement("li"); + liElem.id = this._carouselElemID + "-item-" + idx; + + // if String then assume innerHTML, else an elem object + if(typeof(innerHTMLOrElem) === "string") { + liElem.innerHTML = innerHTMLOrElem; + } else { + liElem.appendChild(innerHTMLOrElem); + } + + return liElem; + }, + + // idx is the location to insert after + _insertAfterItem: function(refIdx, innerHTMLOrElem) { + return this._insertBeforeItem(refIdx+1, innerHTMLOrElem); + }, + + + _insertBeforeItem: function(refIdx, innerHTMLOrElem) { + + var refItem = this.getItem(refIdx); + var size = this.cfg.getProperty("size"); + if(size != this.UNBOUNDED_SIZE) { + this.cfg.setProperty("size", size + 1, true); + } + + for(var i=this._lastPrebuiltIdx; i>=refIdx; i--) { + var anItem = this.getItem(i); + if(this._isValidObj(anItem)) { + anItem.id = this._carouselElemID + "-item-" + (i+1); + } + } + + var liElem = this._createItem(refIdx, innerHTMLOrElem); + + var insertedItem = this.carouselList.insertBefore(liElem, refItem); + this._lastPrebuiltIdx += 1; + + return liElem; + }, + + // TEST THIS... think it has to do with prebuild + insertAfterEnd: function(innerHTMLOrElem) { + return this.insertAfter(this.cfg.getProperty("size"), innerHTMLOrElem); + }, + + _position: function(newStart, showAnimation) { + // do we bypass the isAnimated check? + var currStart = this._priorFirstVisible; + if(newStart > currStart) { + var inc = newStart - currStart; + this._scrollNextInc(inc, showAnimation); + } else { + var dec = currStart - newStart; + this._scrollPrevInc(dec, showAnimation); + } + }, + + _scrollPrev: function(e, carousel) { + if(e !== null) { // event fired this so disable autoplay + carousel.stopAutoPlay(); + } + if(carousel._scrollPrevAnim.isAnimated()) { + return false; + } + carousel._scrollPrevInc(carousel.cfg.getProperty("scrollInc"), + (carousel.cfg.getProperty("animationSpeed") !== 0)); + }, + + // event handler + _scrollNext: function(e, carousel) { + if(e !== null) { // event fired this so disable autoplay + carousel.stopAutoPlay(); + } + if(carousel._scrollNextAnim.isAnimated()) { + return false; // might be better to set ourself waiting for animation completion and + // then just do this function. that will allow faster scroll responses. + } + + carousel._scrollNextInc(carousel.cfg.getProperty("scrollInc"), + (carousel.cfg.getProperty("animationSpeed") !== 0)); + }, + + + _handleAnimationComplete: function(type, args, argList) { + var carousel = argList[0]; + var direction = argList[1]; + + carousel._animationCompleteEvt.fire(direction); + + + }, + + // If EVERY item is already loaded in the range then return true + // Also prebuild whatever is not already created. + _areAllItemsLoaded: function(first, last) { + var itemsLoaded = true; + for(var i=first; i<=last; i++) { + var liElem = this.getItem(i); + + // If the li elem does not exist, then prebuild it in the correct order + // but still flag as not loaded (just prebuilt the li item. + if(!this._isValidObj(liElem)) { + this._prebuildLiElem(i); + itemsLoaded = false; + // but if the item exists and is a placeholder, then + // note that this item is not loaded (only a placeholder) + } else if(this._isValidObj(liElem.placeholder)) { + itemsLoaded = false; + } + } + return itemsLoaded; + }, + + _prebuildItems: function(first, last) { + for(var i=first; i<=last; i++) { + var liElem = this.getItem(i); + + // If the li elem does not exist, then prebuild it in the correct order + // but still flag as not loaded (just prebuilt the li item. + if(!this._isValidObj(liElem)) { + this._prebuildLiElem(i); + } + } + }, + + _isExtraRevealed: function() { + return (this.cfg.getProperty("revealAmount") > 0); + }, + + // probably no longer need carousel passed in, this should be correct now. + _scrollNextInc: function(inc, showAnimation) { + var numVisible = this.cfg.getProperty("numVisible"); + var currStart = this._priorFirstVisible; + var currEnd = this._priorLastVisible; + var size = this.cfg.getProperty("size"); + + var scrollExtent = this._calculateAllowableScrollExtent(); + + if(this.cfg.getProperty("wrap") && currEnd == scrollExtent.end) { + this.scrollTo(scrollExtent.start); // might need to check animation is on or not + return; + } + + // increment start by inc + var newStart = currStart + inc; + var newEnd = newStart + numVisible - 1; + + // If we are past the end, adjust or wrap + if(newEnd > scrollExtent.end) { + newEnd = scrollExtent.end; + newStart = newEnd - numVisible + 1; + } + + inc = newStart - currStart; + + // at this point the following variables are set + // inc... amount to increment by + // newStart... the firstVisible item after the scroll + // newEnd... the last item visible after the scroll + + this.cfg.setProperty("firstVisible", newStart, true); + + + if(inc > 0) { + if(this._isValidObj(this.cfg.getProperty("loadNextHandler"))) { + var visibleExtent = this._calculateVisibleExtent(newStart, newEnd); + var cacheStart = (currEnd+1) < visibleExtent.start ? (currEnd+1) : visibleExtent.start; + var alreadyCached = this._areAllItemsLoaded(cacheStart, visibleExtent.end); + this._loadNextHandlerEvt.fire(visibleExtent.start, visibleExtent.end, alreadyCached); + } + + if(showAnimation) { + var nextParams = { points: { by: [-this.scrollAmountPerInc*inc, 0] } }; + if(this.isVertical()) { + nextParams = { points: { by: [0, -this.scrollAmountPerInc*inc] } }; + } + + this._scrollNextAnim = new YAHOO.util.Motion(this.carouselList, + nextParams, + this.cfg.getProperty("animationSpeed"), + this.cfg.getProperty("animationMethod")); + +// is this getting added multiple times? + if(this.cfg.getProperty("animationCompleteHandler")) { + this._scrollNextAnim.onComplete.subscribe(this._handleAnimationComplete, [this, "next"]); + } + this._scrollNextAnim.animate(); + } else { + if(this.isVertical()) { + var currY = YAHOO.util.Dom.getY(this.carouselList); + + YAHOO.util.Dom.setY(this.carouselList, + currY - this.scrollAmountPerInc*inc); + } else { + var currX = YAHOO.util.Dom.getX(this.carouselList); + YAHOO.util.Dom.setX(this.carouselList, + currX - this.scrollAmountPerInc*inc); + } + } + + } + this._priorFirstVisible = newStart; + this._priorLastVisible = newEnd; + + this._enableDisableControls(); + return false; + }, + + // firstVisible is already set + _scrollPrevInc: function(dec, showAnimation) { + var numVisible = this.cfg.getProperty("numVisible"); + var currStart = this._priorFirstVisible; + var currEnd = this._priorLastVisible; + var size = this.cfg.getProperty("size"); + + // decrement start by dec + var newStart = currStart - dec; + + var scrollExtent = this._calculateAllowableScrollExtent(); + + // How to decide whether to stop at 1 or not + newStart = (newStart < scrollExtent.start) ? scrollExtent.start : newStart; + + // if we are going to extend past the end, then we need to correct the start + var newEnd = newStart + numVisible - 1; + if(newEnd > scrollExtent.end) { + newEnd = scrollExtent.end; + newStart = newEnd - numVisible + 1; + } + + dec = currStart - newStart; + + // at this point the following variables are set + // dec... amount to decrement by + // newStart... the firstVisible item after the scroll + // newEnd... the last item visible after the scroll + this.cfg.setProperty("firstVisible", newStart, true); + + // if we are decrementing + if(dec > 0) { + if(this._isValidObj(this.cfg.getProperty("loadPrevHandler"))) { + var visibleExtent = this._calculateVisibleExtent(newStart, newEnd); + var cacheEnd = (currStart-1) > visibleExtent.end ? (currStart-1) : visibleExtent.end; + var alreadyCached = this._areAllItemsLoaded(visibleExtent.start, cacheEnd); + + this._loadPrevHandlerEvt.fire(visibleExtent.start, visibleExtent.end, alreadyCached); + } + + if(showAnimation) { + var prevParams = { points: { by: [this.scrollAmountPerInc*dec, 0] } }; + if(this.isVertical()) { + prevParams = { points: { by: [0, this.scrollAmountPerInc*dec] } }; + } + + this._scrollPrevAnim = new YAHOO.util.Motion(this.carouselList, + prevParams, + this.cfg.getProperty("animationSpeed"), this.cfg.getProperty("animationMethod")); + if(this.cfg.getProperty("animationCompleteHandler")) { + this._scrollPrevAnim.onComplete.subscribe(this._handleAnimationComplete, [this, "prev"]); + } + this._scrollPrevAnim.animate(); + } else { + if(this.isVertical()) { + var currY = YAHOO.util.Dom.getY(this.carouselList); + YAHOO.util.Dom.setY(this.carouselList, currY + + this.scrollAmountPerInc*dec); + } else { + var currX = YAHOO.util.Dom.getX(this.carouselList); + YAHOO.util.Dom.setX(this.carouselList, currX + + this.scrollAmountPerInc*dec); + } + } + } + this._priorFirstVisible = newStart; + this._priorLastVisible = newEnd; + + this._enableDisableControls(); + + return false; + }, + + // Check for all cases and enable/disable controls as needed by current state + _enableDisableControls: function() { + + var firstVisible = this.cfg.getProperty("firstVisible"); + var lastVisible = this.getLastVisible(); + var scrollExtent = this._calculateAllowableScrollExtent(); + + // previous arrow is turned on. Check to see if we need to turn it off + if(this._prevEnabled) { + if(firstVisible === scrollExtent.start) { + this._disablePrev(); + } + } + + // previous arrow is turned off. Check to see if we need to turn it on + if(this._prevEnabled === false) { + if(firstVisible > scrollExtent.start) { + this._enablePrev(); + } + } + + // next arrow is turned on. Check to see if we need to turn it off + if(this._nextEnabled) { + if(lastVisible === scrollExtent.end) { + this._disableNext(); + } + } + + // next arrow is turned off. Check to see if we need to turn it on + if(this._nextEnabled === false) { + if(lastVisible < scrollExtent.end) { + this._enableNext(); + } + } + }, + + /** + * _loadInitial looks at firstItemVisible for the start (not necessarily 1) + */ + _loadInitial: function() { + var firstVisible = this.cfg.getProperty("firstVisible"); + this._priorLastVisible = this.getLastVisible(); + // Load from 1 to the last visible + // The _calculateSize method will adjust the scroll position + // for starts > 1 + if(this._loadInitHandlerEvt) { + var visibleExtent = this._calculateVisibleExtent(firstVisible, this._priorLastVisible); + // still treat the first real item as starting at 1 + var alreadyCached = this._areAllItemsLoaded(1, visibleExtent.end); + + this._loadInitHandlerEvt.fire(visibleExtent.start, visibleExtent.end, alreadyCached); + } + + if(this.cfg.getProperty("autoPlay") !== 0) { + this._autoPlayTimer = this.startAutoPlay(); + } + + this._enableDisableControls(); + }, + + _calculateAllowableScrollExtent: function() { + var scrollBeforeAmount = this.cfg.getProperty("scrollBeforeAmount"); + var scrollAfterAmount = this.cfg.getProperty("scrollAfterAmount"); + var size = this.cfg.getProperty("size"); + + var extent = {start: 1-scrollBeforeAmount, end: size+scrollAfterAmount}; + return extent; + + }, + + _calculateVisibleExtent: function(start, end) { + if(!start) { + start = this.cfg.getProperty("firstVisible"); + end = this.getLastVisible(); + } + + var size = this.cfg.getProperty("size"); + + // we ignore the firstItem property... this method is used + // for prebuilding the cache and signaling the developer + // what to render on a given scroll. + start = start<1?1:start; + end = end>size?size:end; + + var extent = {start: start, end: end}; + + // set up the indices for revealed items. If there is no item revealed, then set + // the index to -1 + this._firstItemRevealed = -1; + this._lastItemRevealed = -1; + if(this._isExtraRevealed()) { + if(start > 1) { + this._firstItemRevealed = start - 1; + extent.start = this._firstItemRevealed; + } + if(end < size) { + this._lastItemRevealed = end + 1; + extent.end = this._lastItemRevealed; + } + } + + return extent; + }, + + _disablePrev: function() { + this._prevEnabled = false; + if(this._prevButtonStateHandlerEvt) { + this._prevButtonStateHandlerEvt.fire(false, this._carouselPrev); + } + if(this._isValidObj(this._carouselPrev)) { + YAHOO.util.Event.removeListener(this._carouselPrev, "click", this._scrollPrev); + } + }, + + _enablePrev: function() { + this._prevEnabled = true; + if(this._prevButtonStateHandlerEvt) { + this._prevButtonStateHandlerEvt.fire(true, this._carouselPrev); + } + if(this._isValidObj(this._carouselPrev)) { + YAHOO.util.Event.addListener(this._carouselPrev, "click", this._scrollPrev, this); + } + }, + + _disableNext: function() { + if(this.cfg.getProperty("wrap")) { + return; + } + this._nextEnabled = false; + if(this._isValidObj(this._nextButtonStateHandlerEvt)) { + this._nextButtonStateHandlerEvt.fire(false, this._carouselNext); + } + if(this._isValidObj(this._carouselNext)) { + YAHOO.util.Event.removeListener(this._carouselNext, "click", this._scrollNext); + } + }, + + _enableNext: function() { + this._nextEnabled = true; + if(this._isValidObj(this._nextButtonStateHandlerEvt)) { + this._nextButtonStateHandlerEvt.fire(true, this._carouselNext); + } + if(this._isValidObj(this._carouselNext)) { + YAHOO.util.Event.addListener(this._carouselNext, "click", this._scrollNext, this); + } + }, + + _isValidObj: function(obj) { + + if (null == obj) { + return false; + } + if ("undefined" == typeof(obj) ) { + return false; + } + return true; + } +};