Add drag'n'drop sorting to the Gallery Album Edit View (RFE 11007).

This commit is contained in:
Doug Bell 2010-04-07 09:50:09 -05:00
parent 9d760ee406
commit eee547b0cc
6 changed files with 433 additions and 0 deletions

View file

@ -1,4 +1,5 @@
7.9.3
- added #11007: Added drag'n'drop sorting in Gallery Album Edit View
7.9.2
- added: Workflow to extend recurring Calendar events 2 years from the

View file

@ -17,6 +17,7 @@ use Carp qw( croak );
use File::Find;
use File::Spec;
use File::Temp qw{ tempdir };
use JSON;
use Tie::IxHash;
use WebGUI::International;
use WebGUI::Utility;
@ -34,6 +35,7 @@ use Archive::Any;
=head1 DIAGNOSTICS
=head1 METHODS
=cut
#-------------------------------------------------------------------
@ -1204,6 +1206,172 @@ sub www_deleteConfirm {
#----------------------------------------------------------------------------
=head2 www_ajax ( )
Generic AJAX service for gallery.
Arguments are accepted in JSON format in the form variable C<args>. The single
obligatory argument is C<action> determining the service to be called. A list
of available services is given in the following. Additional arguments may be
required depending on the service.
Results are returned in JSON format. The information returned depends on the
service called. Generally, success is indicated by a value of 0 in C<err>.
=head3 moveFile
Service for changing the rank of files. Accepts the asset Id of the photo to be moved
in C<target>. The asset Id of the photo to be replaced is specified in C<before>
or C<after> depending on the desired order. Returns -1 in C<err> and an error
message in C<errMessage> if moving of the photo failed.
=cut
sub www_ajax {
my $self = shift;
my $session = $self->session;
my $form = $self->session->form;
my $result;
# Get arguments encoded in json format
my $args = decode_json($form->get("args"));
# Log some debug information
$session->log->debug("Ajax service called with args=" . $form->get("args"));
# Process requests depending on action argument
SWITCH: {
# Return if no action was specified
if ( $args->{action} eq '' ) {
$session->log->error("Call of ajax service without action argument.");
$result->{ errMessage } = "Action argument is missing.";
last;
}
# ----- Move file action -----
$args->{action} eq 'moveFile' && do { $result = $self->_moveFileAjaxRequest( $args ); last; };
# ----- Unkown action -----
$session->log->error("Call of ajax service with unknown action '" . $args->{action} . "'.");
$result->{ errMessage } = "Action '" . $args->{action} ."' is unknown.";
}
# Set error flag if error message exists
$result->{ err } = -1 if $result->{ errMessage };
# Return results encoded in json format
return encode_json( $result );
}
#----------------------------------------------------------------------------
=head2 _moveFileAjaxRequest ( args )
AJAX service for changing the rank of single files. Returns a hash ref with
error information. Arguments passed to the ajax service are provided via the
hash ref C<args>. Note that this is a private function owned by www_ajax. It
should not be used directly.
=cut
sub _moveFileAjaxRequest {
my $self = shift;
my $args = shift;
my $session = $self->session;
my %result;
# Return if current user is not allowed to edit this album
unless ( $self->canEdit ) {
$session->log->error("Call of moveFile action without having edit permission.");
$result{ errMessage } = "You do not have permission to move files.";
return \%result;
}
# Return if no target was specified
if ( $args->{target} eq '') {
$session->log->error("Call of moveFile action without target argument.");
$result{ errMessage } = "Target argument is missing.";
return \%result;
}
# Return if before or after argument is missing
unless( $args->{before} or $args->{after} ) {
$session->log->error("Call of moveFile action without before/after argument.");
$result{ errMessage } = "Before/after argument is missing.";
return \%result;
}
# Return if before and after arguments were specified
unless( $args->{before} xor $args->{after} ) {
$session->log->error("Call of moveFile action with before *and* after argument.");
$result{ errMessage } = "Both, before and after arguments were specified.";
return \%result;
}
# Get Id of target photo and instantiate asset
my $targetId = $args->{target};
my $target = WebGUI::Asset->newByDynamicClass( $session, $targetId );
# Return if target photo could not be instantiated
unless ( $target ) {
$session->log->error("Couldn't move file '$targetId' because we couldn't instantiate it.");
$result{ errMessage } = "ID of target file seems to be invalid.";
return \%result;
}
# Return if target is not a child of the current album
unless ( $target->getParent->getId eq $self->getId ) {
$session->log->error("Couldn't move file '$targetId' because it is not a child of this album.");
$result{ errMessage } = "ID of target file seems to be invalid.";
return \%result;
}
my ($destId, $dest);
# Instantiate file with ID in before/after argument
$destId = $args->{before} ? $args->{before} : $args->{after};
$dest = WebGUI::Asset->newByDynamicClass( $session, $destId );
# Return if destination file could not be instantiated
unless ( $dest ) {
$session->log->error("Couldn't move file '$targetId' before/after file '$destId' because we couldn't instantiate the latter.");
$result{ errMessage } = "ID in before/after argument seems to be invalid.";
return \%result;
}
# Return if destination file is not a child of the current album
unless ( $dest->getParent->getId eq $self->getId ) {
$session->log->error("Couldn't move file '$targetId' before/after file '$destId' because the latter is not a child of the same album.");
$result{ errMessage } = "ID in before/after argument seems to be invalid.";
return \%result;
}
# Check for use of after argument when lowering the rank
if ( $args->{after} && $target->getRank() > $dest->getRank() ) {
# Get ID of next sibling
$destId = $self->getNextFileId( $destId );
# Instantiate next sibling
$dest = WebGUI::Asset->newByDynamicClass( $session, $destId );
}
# Check for use of before argument when increasing the rank
if ( $args->{before} && $target->getRank() < $dest->getRank() ) {
# Get ID of previous sibling
$destId = $self->getPreviousFileId( $destId );
# Instantiate previous sibling
$dest = WebGUI::Asset->newByDynamicClass( $session, $destId );
}
# Update rank of target photo
$target->setRank( $dest->getRank );
# Log some debug information
$session->log->debug("Successfully moved file '$targetId' before/after file '$destId'.");
# Return reporting success
$result{ err } = 0;
return \%result;
}
#----------------------------------------------------------------------------
=head2 www_edit ( )
Show the form to add / edit a GalleryAlbum asset.

View file

@ -0,0 +1,264 @@
#-------------------------------------------------------------------
# WebGUI is Copyright 2001-2009 Plain Black Corporation.
#-------------------------------------------------------------------
# Please read the legal notices (docs/legal.txt) and the license
# (docs/license.txt) that came with this distribution before using
# this software.
#-------------------------------------------------------------------
# http://www.plainblack.com info@plainblack.com
#-------------------------------------------------------------------
use FindBin;
use strict;
use lib "$FindBin::Bin/../../../lib";
## The goal of this test is to test the creation and deletion of album assets
use JSON;
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);
my %user;
$user{'1'} = WebGUI::User->new( $session, "new" );
$user{'1'}->addToGroups( ['3'] ); # Admins
WebGUI::Test->usersToDelete($user{'1'});
$user{'2'} = WebGUI::User->new( $session, "new" );
WebGUI::Test->usersToDelete($user{'2'});
# Create everything as user no. 1
$session->user({ user => $user{'1'} });
$versionTag->set({name=>"Album Test"});
# Create gallery and a single album
my $gallery
= $node->addChild({
className => "WebGUI::Asset::Wobject::Gallery",
groupIdEdit => 3, # Admins
},
undef,
undef,
{
skipAutoCommitWorkflows => 1,
});
my $album
= $gallery->addChild({
className => "WebGUI::Asset::Wobject::GalleryAlbum",
},
undef,
undef,
{
skipAutoCommitWorkflows => 1,
});
# Create 5 photos inside the gallery
my @photoId;
for (my $i = 0; $i < 5; $i++)
{
my $photo
= $album->addChild({
className => "WebGUI::Asset::File::GalleryFile::Photo",
},
undef,
undef,
{
skipAutoCommitWorkflows => 1,
});
$photoId[$i] = $photo->getId;
}
# Commit all changes
$versionTag->commit;
# Make album default asset
$session->asset( $album );
# Define some general variables
my $result;
#----------------------------------------------------------------------------
# Tests
plan tests => 19;
#----------------------------------------------------------------------------
# Test module compiles okay
use_ok("WebGUI::Asset::Wobject::GalleryAlbum");
#----------------------------------------------------------------------------
# Test calling without arguments
diag("general testing");
# Provide no arguments at all
$result = callAjaxService({ });
ok( $result->{ err } != 0 && $result->{ errMessage }, "Error after call without arguments." );
#----------------------------------------------------------------------------
# Test moveFile action with incomplete of invalid arguments
diag("moveFile action");
# Omit target
$result = callAjaxService({
action => 'moveFile',
after => $photoId[4],
});
ok( $result->{ err } != 0 && $result->{ errMessage }, "Error after request of moveFile action without 'target' specified." );
# Omit before/after
$result = callAjaxService({
action => 'moveFile',
target => $photoId[0],
});
ok( $result->{ err } != 0 && $result->{ errMessage }, "Error after request of moveFile action without 'before/after' specified." );
# Specify invalid target ID
$result = callAjaxService({
action => 'moveFile',
target => '123456',
after => $photoId[4],
});
ok( $result->{ err } != 0 && $result->{ errMessage }, "Error after request of moveFile action with invalid 'target' ID." );
# Specify invalid ID in after argument
$result = callAjaxService({
action => 'moveFile',
target => $photoId[0],
after => '123456',
});
ok( $result->{ err } != 0 && $result->{ errMessage }, "Error after request of moveFile action with invalid ID in 'after' argument." );
# Specify invalid ID in before argument
$result = callAjaxService({
action => 'moveFile',
target => $photoId[0],
before => '123456',
});
ok( $result->{ err } != 0 && $result->{ errMessage }, "Error after request of moveFile action with invalid ID in 'before' argument." );
# Specify non-child target ID
$result = callAjaxService({
action => 'moveFile',
target => $album->getId,
after => $photoId[4],
});
ok( $result->{ err } != 0 && $result->{ errMessage }, "Error after request of moveFile action with non-child 'target' ID." );
# Specify non-child ID in after argument
$result = callAjaxService({
action => 'moveFile',
target => $photoId[0],
after => $album->getId,
});
ok( $result->{ err } != 0 && $result->{ errMessage }, "Error after request of moveFile action with non-child ID in 'after' argument." );
# Specify non-child ID in before argument
$result = callAjaxService({
action => 'moveFile',
target => $photoId[0],
before => $album->getId,
});
ok( $result->{ err } != 0 && $result->{ errMessage }, "Error after request of moveFile action with non-child ID in 'before' argument." );
#----------------------------------------------------------------------------
# Test moving photos
# Move photo no. 0 after photo no. 4
$result = callAjaxService({
action => 'moveFile',
target => $photoId[0],
after => $photoId[4],
});
is($result->{ err }, 0, 'Moving of photo no. 0 after photo no. 4 successful.');
is($album->getPreviousFileId($photoId[0]), $photoId[4], 'Photo no. 0 is after photo no. 4.');
# Move photo no. 0 before photo no. 1 (restore initial order)
$result = callAjaxService({
action => 'moveFile',
target => $photoId[0],
before => $photoId[1],
});
# Delete all stow variables. This is necessary or the list of file IDs will
# not get updated.
$session->stow->deleteAll;
is($result->{ err }, 0, 'Moving of photo no. 0 before photo no. 1 successful.');
is($album->getNextFileId($photoId[0]), $photoId[1], 'Photo no. 0 is before photo no. 1.');
# Move photo no. 0 before photo no. 0
$result = callAjaxService({
action => 'moveFile',
target => $photoId[0],
before => $photoId[0],
});
$session->stow->deleteAll;
is($result->{ err }, 0, 'Moving of photo no. 0 before photo no. 0 successful.');
is($album->getNextFileId($photoId[0]), $photoId[1], 'Photo no. 0 is still before photo no. 1.');
# Move photo no. 0 after photo no. 0
$result = callAjaxService({
action => 'moveFile',
target => $photoId[0],
after => $photoId[0],
});
$session->stow->deleteAll;
is($result->{ err }, 0, 'Moving of photo no. 0 after photo no. 0 successful.');
is($album->getNextFileId($photoId[0]), $photoId[1], 'Photo no. 0 is still before photo no. 1.');
# Try to move photo with insufficient permissions
$session->user({ user => $user{'2'} });
$result = callAjaxService({
action => 'moveFile',
target => $photoId[0],
after => $photoId[4],
});
$session->user({ user => $user{'1'} });
ok( $result->{ err } != 0 && $result->{ errMessage }, "Error after request of moveFile action with insufficient permissions." );
#----------------------------------------------------------------------------
# callAjaxService( args )
# Makes a call to the www_ajax method of $album and returns the reply. The
# only argument is a hash ref pointing to arguments for the ajax service.
# The sub uses the global $session and $album variables.
sub callAjaxService {
my $args = shift;
# Setup the mock request object
$session->request->method('POST');
$session->request->setup_body({ args => encode_json($args) });
# Call ajax service function and decode reply
return decode_json( $album->www_ajax() );
}
#----------------------------------------------------------------------------
# Cleanup
END {
$versionTag->rollback();
}