Tree view buttons now work. Changed progress bar helpers to use fork. Added forkId option to AssetHelpers. Added updateAsset service to Admin.

This commit is contained in:
Doug Bell 2011-03-03 19:25:20 -06:00
parent 399aa5368b
commit b27bc19e4d
14 changed files with 757 additions and 277 deletions

View file

@ -24,12 +24,12 @@ Admin Plugins do the administrative tasks.
use Moose;
use JSON qw( from_json to_json );
use namespace::autoclean;
use Scalar::Util;
use Search::QueryParser;
use WebGUI::Pluggable;
use WebGUI::Macro;
use WebGUI::Search;
use WebGUI::Fork;
has 'session' => (
is => 'ro',
@ -740,6 +740,55 @@ sub www_searchAssets {
return to_json( $assetInfo );
}
#----------------------------------------------------------------------------
=head2 www_updateAsset( )
Update an asset. The assetId is given in a query parameter. The updated
properties are given as a JSON string in the POST body.
The actual update will happen in a forked process, due to rank being a long
update.
=cut
sub www_updateAsset {
my ( $self ) = @_;
my $session = $self->session;
my $assetId = $session->form->get( 'assetId' );
my $asset = eval { WebGUI::Asset->newById( $session, $assetId ); };
if ( $@ || !$asset ) {
return to_json( { error => "Could not find asset" } );
}
my $props = eval { JSON->new->decode( $session->request->raw_body ); };
if ( $@ ) {
return to_json( { error => "Unable to decode JSON body" } );
}
my $fork = WebGUI::Fork->start(
$session, __PACKAGE__, 'updateAsset', { assetId => $assetId, properties => $props },
);
return to_json( { forkId => $fork->getId } );
}
sub updateAsset {
my ( $process, $args ) = @_;
my $session = $process->session;
my $props = $args->{properties};
my $assetId = $args->{assetId};
my $asset = WebGUI::Asset->newById( $session, $assetId );
# Update rank specially
if ( my $rank = delete $props->{rank} ) {
$asset->setRank( $rank );
}
# Update other properties
# TODO: Do we add a revision? or do they request a revision?
}
#----------------------------------------------------------------------
=head2 www_view ( session )
@ -783,6 +832,7 @@ sub www_view {
$style->setCss( $url->extras('yui/build/container/assets/skins/sam/container.css'));
$style->setCss( $url->extras('yui/build/autocomplete/assets/skins/sam/autocomplete.css'));
$style->setCss( $url->extras('yui/build/menu/assets/skins/sam/menu.css'));
$style->setCss( $url->extras('yui/build/progressbar/assets/skins/sam/progressbar.css') );
$style->setCss( $url->extras('admin/admin.css'));
$style->setScript($url->extras('yui/build/yahoo-dom-event/yahoo-dom-event.js'));
$style->setScript($url->extras('yui/build/utilities/utilities.js'));
@ -799,7 +849,9 @@ sub www_view {
$style->setScript($url->extras('yui/build/button/button-min.js'));
$style->setScript($url->extras('yui/build/autocomplete/autocomplete-min.js'));
$style->setScript( $url->extras( 'yui/build/json/json-min.js' ) );
$style->setScript( $url->extras( 'yui/build/progressbar/progressbar-min.js' ) );
$style->setScript( $url->extras( 'yui-webgui/build/i18n/i18n.js' ) );
$style->setScript( $url->extras( 'Fork/poll.js' ) );
$style->setScript($url->extras('admin/admin.js'));
# Use the template in our __DATA__ block

View file

@ -1208,6 +1208,10 @@ sub getHelpers {
className => 'WebGUI::AssetHelper::CreateShortcut',
label => 'Create Shortcut',
},
duplicate => {
className => 'WebGUI::AssetHelper::Duplicate',
label => 'Duplicate',
},
cut => {
className => 'WebGUI::AssetHelper::Cut',
label => 'Cut',
@ -1232,6 +1236,10 @@ sub getHelpers {
className => 'WebGUI::AssetHelper::Lock',
label => 'Lock',
},
delete => {
className => 'WebGUI::AssetHelper::Delete',
label => 'Delete',
},
};
# Merge additional helpers for this class from config

View file

@ -51,59 +51,6 @@ sub canPaste {
return $class->validParent($self->session);
}
#-------------------------------------------------------------------
=head2 copyInFork ( $process, $args )
WebGUI::Fork method called by www_copy
=cut
sub copyInFork {
my ($process, $args) = @_;
my $session = $process->session;
my $asset = WebGUI::Asset->newById($session, $args->{assetId});
my @pedigree = ('self');
my $childrenOnly = 0;
if ($args->{childrenOnly}) {
$childrenOnly = 1;
push @pedigree, 'children';
}
else {
push @pedigree, 'descendants';
}
my $ids = $asset->getLineage(\@pedigree);
my $tree = WebGUI::ProgressTree->new($session, $ids);
$process->update(sub { $tree->json });
my $patch = Monkey::Patch::patch_class(
'WebGUI::Asset', 'duplicate', sub {
my $duplicate = shift;
my $self = shift;
my $id = $self->getId;
$tree->focus($id);
my $asset = eval { $self->$duplicate(@_) };
my $e = $@;
if ($e) {
$tree->note($id, $e);
$tree->failure($id, 'Died');
}
else {
$tree->success($id);
}
$process->update(sub { $tree->json });
die $e if $e;
return $asset;
}
);
my $newAsset = $asset->duplicateBranch($childrenOnly, 'clipboard');
$newAsset->update({ title => $newAsset->getTitle . ' (copy)'});
if ($args->{commit}) {
my $tag = WebGUI::VersionTag->getWorking($session);
$tag->requestCommit();
}
}
#-------------------------------------------------------------------
=head2 cut ( )

View file

@ -94,6 +94,11 @@ A URL. Will open a tab in the Admin Console. Anything returned by the URL will
A URL. Puts new content into the View tab from the requested URL.
=head4 forkId
The ID for a WebGUI::Fork process. The Admin Console can then open a progress
dialog to poll for the progress of the forked process.
=head4 scriptFile
Loads the requested JavaScript file, referenced by URL.

View file

@ -3,6 +3,7 @@ package WebGUI::AssetHelper::Copy;
use strict;
use Class::C3;
use base qw/WebGUI::AssetHelper/;
use Scalar::Util qw( blessed );
=head1 LEGAL
@ -34,69 +35,54 @@ These methods are available from this class:
=head2 process ( $asset )
Open a progress dialog for the copy operation
Fork the copy operation
=cut
sub process {
my ($self, $asset) = @_;
my $session = $self->session;
# Should we autocommit?
my $commit = $session->setting->get('versionTagMode') eq 'autoCommit';
# Fork the copy. Forking makes sure it won't get interrupted
my $fork = WebGUI::Fork->start(
$session, blessed( $self ), 'copy', { assetId => $asset->getId, commit => $commit },
);
return {
openDialog => '?op=assetHelper;helperId=' . $self->id . ';method=copy;assetId=' . $asset->getId,
forkId => $fork->getId,
};
}
#----------------------------------------------------------------------------
#-------------------------------------------------------------------
=head2 www_copy ( $asset )
=head2 copy ( $process, $args )
Perform the copy operation, showing the progress.
Perform the copy stuff in a forked process
=cut
sub www_copy {
my ( $self, $asset ) = @_;
my $session = $asset->session;
my $i18n = WebGUI::International->new($session, 'Asset');
sub copy {
my ($process, $args) = @_;
my $session = $process->session;
my $asset = WebGUI::Asset->newById($session, $args->{assetId});
my $tree = WebGUI::ProgressTree->new($session, [ $asset->getId ] );
$process->update(sub { $tree->json });
my $newAsset = $asset->duplicate({ state => "clipboard" });
return $session->response->stream( sub {
my ( $session ) = @_;
my $pb = WebGUI::ProgressBar->new($session);
my @stack;
# If we aren't committing, add to a tag
if ( !$args->{commit} ) {
$newAsset->update({
status => "pending",
tagId => WebGUI::VersionTag->getWorking( $session )->getId,
});
}
$newAsset->update({ title => $newAsset->getTitle . ' (copy)'});
return $pb->run(
admin => 1,
total => 2,
title => $i18n->get('Copy Assets'),
icon => $session->url->extras('adminConsole/assets.gif'),
code => sub {
my $bar = shift;
my $newAsset = $asset->duplicate;
$bar->update($i18n->get('cut'));
my $title = sprintf("%s (%s)", $asset->getTitle, $i18n->get('copy'));
$newAsset->update({ title => $title });
$newAsset->cut;
my $result = WebGUI::VersionTag->autoCommitWorkingIfEnabled(
$session, {
allowComments => 1,
returnUrl => $asset->getUrl,
}
);
if ( $result eq 'redirect' ) {
return $asset->getUrl;
}
return { message => 'Your asset is now copied!' };
},
wrap => {
'WebGUI::Asset::duplicate' => sub {
my ($bar, $original, $asset, @args) = @_;
my $name = join '/', @stack, $asset->getTitle;
$bar->update($name);
return $asset->$original(@args);
},
}
);
} );
$tree->success($asset->getId);
$process->update(sub { $tree->json });
}
1;

View file

@ -3,6 +3,8 @@ package WebGUI::AssetHelper::Cut;
use strict;
use Class::C3;
use base qw/WebGUI::AssetHelper/;
use Scalar::Util qw( blessed );
use Monkey::Patch;
=head1 LEGAL
@ -51,58 +53,47 @@ sub process {
return { error => $i18n->get('41'), };
}
# Fork the cut. Forking makes sure it won't get interrupted
my $fork = WebGUI::Fork->start(
$session, blessed( $self ), 'cut', { assetId => $asset->getId },
);
return {
openDialog => '?op=assetHelper;helperId=' . $self->id . ';method=cut;assetId=' . $asset->getId,
forkId => $fork->getId,
};
}
#----------------------------------------------------------------------------
=head2 www_cut ( $asset )
=head2 cut ( process, args )
Show the progress bar while cutting the asset.
Handle the actual cutting in the forked process.
=cut
sub www_cut {
my ( $self, $asset ) = @_;
my $session = $asset->session;
my $i18n = WebGUI::International->new($session, 'Asset');
sub cut {
my ( $process, $args ) = @_;
my $asset = WebGUI::Asset->newById( $process->session, $args->{assetId} );
return $session->response->stream( sub {
my ( $session ) = @_;
my $pb = WebGUI::ProgressBar->new($session);
my @stack;
# All the Assets we need to work on
my $assetIds = $asset->getLineage( ['self','descendants'] );
return $pb->run(
admin => 1,
title => $i18n->get('Copy Assets'),
icon => $session->url->extras('adminConsole/assets.gif'),
code => sub {
my $bar = shift;
$bar->update( "Preparing... (i18n)" );
$bar->total( $asset->getDescendantCount + 2 );
$bar->update( "Cutting... (i18n)" );
my $success = $asset->cut();
if (! $success) {
return { error => $i18n->get('41', 'WebGUI'), };
}
return { message => "Your asset is cut!" };
},
wrap => {
'WebGUI::Asset::getLineageIterator' => sub {
my ($bar, $orig, $asset, @args) = @_;
$bar->update("Updating descendants... (i18n)");
return $asset->$orig(@args);
},
'WebGUI::Asset::updateHistory' => sub {
my ( $bar, $orig, $asset, @args ) = @_;
$bar->update( "Updating " . $asset->getTitle );
return $asset->$orig(@args);
},
},
);
} );
# Build a tree and update process status
my $tree = WebGUI::ProgressTree->new( $process->session, $assetIds );
$process->update( sub { $tree->json } );
# Monkeypatch a sub to get a status update
my $patch = Monkey::Patch::patch_class(
'WebGUI::Asset', 'updateHistory', sub {
my ( $orig, $self, @args ) = @_;
$tree->success( $self->assetId );
$process->update( sub { $tree->json } );
$self->$orig( @args );
}
);
# Do the actual work
$asset->cut;
}
1;

View file

@ -0,0 +1,107 @@
package WebGUI::AssetHelper::Delete;
use strict;
use Class::C3;
use base qw/WebGUI::AssetHelper/;
use Scalar::Util qw( blessed );
=head1 LEGAL
-------------------------------------------------------------------
WebGUI is Deleteright 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
-------------------------------------------------------------------
=head1 NAME
Package WebGUI::AssetHelper::Delete
=head1 DESCRIPTION
Delete an Asset, and all descendants
=head1 METHODS
These methods are available from this class:
=cut
#-------------------------------------------------------------------
=head2 process ( $asset )
Fork the Delete operation
=cut
sub process {
my ($self, $asset) = @_;
my $session = $self->session;
my $i18n = WebGUI::International->new($session, 'WebGUI');
if (! $asset->canEdit) {
return { error => $i18n->get('38'), };
}
elsif ( $asset->get('isSystem') ) {
return { error => $i18n->get('41'), };
}
# Fork the Delete. Forking makes sure it won't get interrupted
my $fork = WebGUI::Fork->start(
$session, blessed( $self ), 'delete', { assetId => $asset->getId, },
);
return {
forkId => $fork->getId,
};
}
#-------------------------------------------------------------------
=head2 delete ( $process, $args )
Perform the delete stuff in a forked process
=cut
sub delete {
my ($process, $args) = @_;
my $session = $process->session;
my $asset = WebGUI::Asset->newById($session, $args->{assetId});
# Prepare a tree with all the ids
my $ids =
$asset->getLineage(
[ 'self', 'descendants' ], {
statesToInclude => [qw(published clipboard clipboard-limbo trash trash-limbo)],
statusToInclude => [qw(approved archived pending)],
}
);
my $tree = WebGUI::ProgressTree->new( $session, $ids );
$process->update(sub { $tree->json });
# Patch a sub to get a status update
my $patch = Monkey::Patch::patch_class(
'WebGUI::Asset',
'setState',
sub {
my ( $setState, $self, $state ) = @_;
my $id = $self->getId;
$tree->focus($id);
my $ret = $self->$setState($state);
$tree->success($id);
$process->update(sub { $tree->json });
return $ret;
}
);
# Do the dirty deed, cheap
$asset->trash;
}
1;

View file

@ -0,0 +1,88 @@
package WebGUI::AssetHelper::Duplicate;
use strict;
use Class::C3;
use base qw/WebGUI::AssetHelper/;
use Scalar::Util qw( blessed );
=head1 LEGAL
-------------------------------------------------------------------
WebGUI is Duplicateright 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
-------------------------------------------------------------------
=head1 NAME
Package WebGUI::AssetHelper::Duplicate
=head1 DESCRIPTION
Duplicate an Asset, with no children.
=head1 METHODS
These methods are available from this class:
=cut
#-------------------------------------------------------------------
=head2 process ( $asset )
Fork the duplicate operation
=cut
sub process {
my ($self, $asset) = @_;
my $session = $self->session;
# Should we autocommit?
my $commit = $session->setting->get('versionTagMode') eq 'autoCommit';
# Fork the Duplicate. Forking makes sure it won't get interrupted
my $fork = WebGUI::Fork->start(
$session, blessed( $self ), 'duplicate', { assetId => $asset->getId, commit => $commit },
);
return {
forkId => $fork->getId,
};
}
#-------------------------------------------------------------------
=head2 duplicate ( $process, $args )
Perform the duplicate stuff in a forked process
=cut
sub duplicate {
my ($process, $args) = @_;
my $session = $process->session;
my $asset = WebGUI::Asset->newById($session, $args->{assetId});
my $tree = WebGUI::ProgressTree->new($session, [ $asset->getId ] );
$process->update(sub { $tree->json });
my $newAsset = $asset->duplicate;
# If we aren't committing, add to a tag
if ( !$args->{commit} ) {
$newAsset->update({
status => "pending",
tagId => WebGUI::VersionTag->getWorking( $session )->getId,
});
}
$newAsset->update({ title => $newAsset->getTitle . ' (Duplicate)'});
$tree->success($asset->getId);
$process->update(sub { $tree->json });
}
1;

View file

@ -363,51 +363,6 @@ sub trash {
return 1;
}
#-------------------------------------------------------------------
=head2 trashInFork
WebGUI::Fork method called by www_deleteList and www_delete to move assets
into the trash.
=cut
sub trashInFork {
my ( $process, $list ) = @_;
my $session = $process->session;
my @roots = grep { $_->canEdit && $_->canEditIfLocked }
map {
eval { WebGUI::Asset->newPending( $session, $_ ) }
} @$list;
my @ids = map {
my $list = $_->getLineage(
[ 'self', 'descendants' ], {
statesToInclude => [qw(published clipboard clipboard-limbo trash trash-limbo)],
statusToInclude => [qw(approved archived pending)],
}
);
@$list;
} @roots;
my $tree = WebGUI::ProgressTree->new( $session, \@ids );
$process->update(sub { $tree->json });
my $patch = Monkey::Patch::patch_class(
'WebGUI::Asset',
'setState',
sub {
my ( $setState, $self, $state ) = @_;
my $id = $self->getId;
$tree->focus($id);
my $ret = $self->$setState($state);
$tree->success($id);
$process->update(sub { $tree->json });
return $ret;
}
);
$_->trash() for @roots;
} ## end sub trashInFork
require WebGUI::Workflow::Activity::DeleteExportedFiles;
sub _invokeWorkflowOnExportedFiles {
my $self = shift;
@ -435,63 +390,6 @@ sub _invokeWorkflowOnExportedFiles {
#-------------------------------------------------------------------
=head2 www_delete
Moves self to trash in fork, redirects to Container or Parent if canEdit.
Otherwise returns AdminConsole rendered insufficient privilege.
=cut
sub www_delete {
my $self = shift;
return $self->session->privilege->insufficient() unless ($self->canEdit && $self->canEditIfLocked);
return $self->session->privilege->vitalComponent() if $self->get('isSystem');
return $self->session->privilege->vitalComponent() if ($self->getId ~~ [$self->session->setting->get("defaultPage"), $self->session->setting->get("notFoundPage")]);
$self->trash;
my $asset = $self->getContainer;
if ($self->getId eq $asset->getId) {
$asset = $self->getParent;
}
$self->forkWithStatusPage({
plugin => 'ProgressTree',
title => 'Delete Assets',
redirect => $asset->getUrl,
method => 'trashInFork',
args => [ $self->getId ],
}
);
}
#-------------------------------------------------------------------
=head2 www_deleteList
Checks to see if a valid CSRF token was received. If not, then it returns insufficient privilege.
Moves list of assets to trash, checking each to see if the user canEdit,
and canEditIfLocked. Returns the user to manageTrash, or to the screen set
by the form variable C<proceeed>.
=cut
sub www_deleteList {
my $self = shift;
my $session = $self->session;
my $form = $session->form;
return $session->privilege->insufficient() unless $session->form->validToken;
my $method = $form->get('proceed') || 'manageTrash';
$self->forkWithStatusPage({
plugin => 'ProgressTree',
title => 'Delete Assets',
redirect => $self->getUrl("func=$method"),
method => 'trashInFork',
args => [ $form->get('assetId') ],
}
);
} ## end sub www_deleteList
#-------------------------------------------------------------------
=head2 www_manageTrash ( )
Returns an AdminConsole to deal with assets in the Trash. If user isn't in the Turn On Admin group, renders an insufficient privilege page.