diff --git a/docs/changelog/7.x.x.txt b/docs/changelog/7.x.x.txt index 889226c90..2ccd67b87 100644 --- a/docs/changelog/7.x.x.txt +++ b/docs/changelog/7.x.x.txt @@ -4,6 +4,7 @@ - Added WebGUI::Fork api - Moved html export to Fork - Moved clipboard functions to Fork + - Moved trash functions to Fork - fixed #11884: Editing Templates impossible / Code editor not loaded - recommitted ukplayer. Removal broke Matrix. Licencing information was available but overlooked. - fixed #11883: Wiki "Add page" link does not encode special chars diff --git a/lib/WebGUI/Asset.pm b/lib/WebGUI/Asset.pm index 4c86c7353..b1d1a15bc 100644 --- a/lib/WebGUI/Asset.pm +++ b/lib/WebGUI/Asset.pm @@ -835,6 +835,54 @@ sub fixUrlFromParent { return $url; } +#------------------------------------------------------------------- + +=head2 forkWithProgressTree ($args) + +Kicks off a WebGUI::Fork running $method with $args (from the args hashref) +and redirects to a ProgressTree status page to show the progress. The +following arguments are required in $args: + +=head3 method + +The name of the WebGUI::Asset method to call + +=head3 args + +The arguments to pass that method (see WebGUI::Fork) + +=head3 title + +An key in Asset's i18n hash for the title of the rendered console page + +=head3 redirect + +The full url to redirect to after the fork has finished. + +=cut + +sub forkWithProgressTree { + my ( $self, $args ) = @_; + my $session = $self->session; + + my $process = WebGUI::Fork->start( $session, 'WebGUI::Asset', $args->{method}, $args->{args} ); + + if ( my $groupId = $args->{groupId} ) { + $process->setGroup($groupId); + } + + my $method = $session->form->get('proceed') || 'manageTrash'; + my $i18n = WebGUI::International->new( $session, 'Asset' ); + my $pairs = $process->contentPairs( + 'ProgressTree', { + title => $i18n->get( $args->{title} ), + icon => 'assets', + proceed => $args->{redirect} || '', + } + ); + $session->http->setRedirect( $self->getUrl($pairs) ); + return 'redirect'; +} ## end sub forkWithProgressTree #------------------------------------------------------------------- diff --git a/lib/WebGUI/Asset/Template.pm b/lib/WebGUI/Asset/Template.pm index 265d8a278..7749bc0a3 100644 --- a/lib/WebGUI/Asset/Template.pm +++ b/lib/WebGUI/Asset/Template.pm @@ -212,7 +212,7 @@ copy. sub duplicate { my $self = shift; - my $newTemplate = $self->SUPER::duplicate; + my $newTemplate = $self->SUPER::duplicate(@_); $newTemplate->update({isDefault => 0}); if ( my $storageId = $self->get('storageIdExample') ) { my $newStorage = WebGUI::Storage->get( $self->session, $storageId )->copy; diff --git a/lib/WebGUI/AssetClipboard.pm b/lib/WebGUI/AssetClipboard.pm index 2bb5c7609..52b4c98aa 100644 --- a/lib/WebGUI/AssetClipboard.pm +++ b/lib/WebGUI/AssetClipboard.pm @@ -51,13 +51,13 @@ sub canPaste { #------------------------------------------------------------------- -=head2 copyInBackground ( $process, $args ) +=head2 copyInFork ( $process, $args ) WebGUI::Fork method called by www_copy =cut -sub copyInBackground { +sub copyInFork { my ($process, $args) = @_; my $session = $process->session; my $asset = WebGUI::Asset->new($session, $args->{assetId}); @@ -72,23 +72,26 @@ sub copyInBackground { } my $ids = $asset->getLineage(\@pedigree); my $tree = WebGUI::ProgressTree->new($session, $ids); - my $patch = Monkey::Patch::patch_class 'WebGUI::Asset', 'duplicate', sub { - my $duplicate = shift; - my $self = shift; - my $id = $self->getId; - my $asset = eval { $self->$duplicate(@_) }; - my $e = $@; - if ($e) { - $tree->note($id, $e); - $tree->failure($id, 'Died'); + 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; } - 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}) { @@ -303,53 +306,50 @@ sub paste { #------------------------------------------------------------------- -=head2 pasteInBackground ( ) +=head2 pasteInFork ( ) WebGUI::Fork method called by www_pasteList =cut -sub pasteInBackground { - my ($process, $args) = @_; +sub pasteInFork { + my ( $process, $args ) = @_; my $session = $process->session; - my $self = WebGUI::Asset->new($session, $args->{assetId}); + my $self = WebGUI::Asset->new( $session, $args->{assetId} ); my @roots = grep { $_ && $_->canEdit } - map { WebGUI::Asset->newPending($session, $_) } - @{ $args->{list} }; + map { WebGUI::Asset->newPending( $session, $_ ) } @{ $args->{list} }; - my @ids; - for my $r (@roots) { - my $these = $r->getLineage( - ['self', 'descendants'], { - statesToInclude => ['clipboard', 'clipboard-limbo'] - } - ); - push(@ids, @$these); - } + my @ids = map { + my $list + = $_->getLineage( [ 'self', 'descendants' ], { statesToInclude => [ 'clipboard', 'clipboard-limbo' ] } ); + @$list; + } @roots; - my $tree = WebGUI::ProgressTree->new($session, \@ids); + my $tree = WebGUI::ProgressTree->new( $session, \@ids ); my $patch = Monkey::Patch::patch_class( - 'WebGUI::Asset', 'indexContent', sub { + 'WebGUI::Asset', + 'indexContent', + sub { my $indexContent = shift; my $self = shift; my $id = $self->getId; $tree->focus($id); my $ret = eval { $self->$indexContent(@_) }; - my $e = $@; + my $e = $@; if ($e) { - $tree->note($id, $e); - $tree->failure($id, 'Died'); + $tree->note( $id, $e ); + $tree->failure( $id, 'Died' ); } else { $tree->success($id); } - $process->update(sub { $tree->json }); + $process->update( sub { $tree->json } ); die $e if $e; return $ret; } ); - $self->paste($_->getId) for @roots; -} + $self->paste( $_->getId ) for @roots; +} ## end sub pasteInFork #------------------------------------------------------------------- @@ -400,19 +400,13 @@ sub www_copy { } $args{assetId} = $self->getId; - my $process = WebGUI::Fork->start( - $session, 'WebGUI::Asset', 'copyInBackground', \%args - ); - my $i18n = WebGUI::International->new($session, 'Asset'); - my $pairs = $process->contentPairs( - 'ProgressTree', { - title => $i18n->get('Copy Assets'), - icon => 'assets', - proceed => $redir + $self->forkWithProgressTree({ + title => 'Copy Assets', + redirect => $redir, + method => 'copyInFork', + args => \%args } ); - $http->setRedirect($self->getUrl($pairs)); - return 'redirect'; } #------------------------------------------------------------------- @@ -730,30 +724,24 @@ the Asset Manager. sub www_pasteList { my $self = shift; my $session = $self->session; + my $form = $session->form; return $session->privilege->insufficient() unless $self->canEdit && $session->form->validToken; - my $form = $session->form; - my $process = WebGUI::Fork->start( - $session, 'WebGUI::Asset', 'pasteInBackground', { - assetId => $self->getId, - list => [ $form->get('assetId') ], + $self->forkWithProgressTree( { + title => 'Paste Assets', + redirect => $self->getUrl( + $form->get('proceed') eq 'manageAssets' + ? 'op=assetManager' + : () + ), + method => 'pasteInFork', + args => { + assetId => $self->getId, + list => [ $form->get('assetId') ], + } } ); - - my $redir = $self->getUrl( - ($form->get('proceed') eq 'manageAssets') ? 'op=assetManager' : () - ); - my $i18n = WebGUI::International->new($session, 'Asset'); - my $pairs = $process->contentPairs( - 'ProgressTree', { - title => $i18n->get('Paste Assets'), - icon => 'assets', - proceed => $redir, - } - ); - $session->http->setRedirect($self->getUrl($pairs)); - return 'redirect'; -} +} ## end sub www_pasteList 1; diff --git a/lib/WebGUI/AssetExportHtml.pm b/lib/WebGUI/AssetExportHtml.pm index 9bccdd535..160ab19b4 100644 --- a/lib/WebGUI/AssetExportHtml.pm +++ b/lib/WebGUI/AssetExportHtml.pm @@ -1015,21 +1015,16 @@ sub www_exportStatus { my @vars = qw( index depth userId extrasUploadsAction rootUrlAction exportUrl ); - my $process = WebGUI::Fork->start( - $session, 'WebGUI::Asset', 'exportInFork', { - assetId => $self->getId, - map { $_ => scalar $form->get($_) } @vars + $self->forkWithProgressTree({ + title => 'Page Export Status', + method => 'exportInFork', + groupId => 13, + args => { + assetId => $self->getId, + map { $_ => scalar $form->get($_) } @vars + } } ); - $process->setGroup(13); - my $i18n = WebGUI::International->new( $session, 'Asset' ); - my $pairs = $process->contentPairs('ProgressTree', { - icon => 'assets', - title => $i18n->get('Page Export Status'), - } - ); - $session->http->setRedirect($self->getUrl($pairs)); - return 'redirect'; } #------------------------------------------------------------------- diff --git a/lib/WebGUI/AssetTrash.pm b/lib/WebGUI/AssetTrash.pm index f6c7110a5..c44e22f8f 100644 --- a/lib/WebGUI/AssetTrash.pm +++ b/lib/WebGUI/AssetTrash.pm @@ -200,6 +200,63 @@ sub purge { return 1; } +#------------------------------------------------------------------- + +=head2 purgeInFork + +WebGUI::Fork method called by www_purgeList + +=cut + +sub purgeInFork { + my ( $process, $list ) = @_; + my $session = $process->session; + my @roots = grep { $_ && $_->canEdit } + map { 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 ); + my $patch = Monkey::Patch::patch_class( + 'WebGUI::Asset', + 'purge', + sub { + my ( $purge, $self, $options ) = @_; + my $id = $self->getId; + my $zero = ''; + $tree->focus($id); + $options ||= {}; + local $options->{outputSub} = sub { $zero .= $_[0] }; + my $ret = eval { $self->$purge($options) }; + my $e = $@; + $tree->focus($id); + + if ($e) { + $tree->failure( $id, 'Died' ); + $tree->note( $id, $e ); + } + elsif ( !$ret ) { + $tree->failure( $id, 'Failed' ); + $tree->note( $id, $zero ); + } + else { + $tree->success($id); + } + $process->update( sub { $tree->json } ); + die $e if $e; + return $ret; + } + ); + $_->purge for @roots; +} ## end sub purgeInFork #------------------------------------------------------------------- @@ -246,7 +303,15 @@ sub trash { return undef; } - my $assetIter = $self->getLineageIterator(['self','descendants']); + my $assetIter = $self->getLineageIterator( + ['self','descendants'], { + statesToInclude => [qw(published clipboard clipboard-limbo trash trash-limbo)], + statusToInclude => [qw(approved archived pending)], + } + ); + my $rootId = $self->getId; + my $db = $session->db; + $db->beginTransaction; while ( 1 ) { my $asset; eval { $asset = $assetIter->() }; @@ -263,7 +328,9 @@ sub trash { $outputSub->($i18n->get('Clearing cache')); $asset->purgeCache; $asset->updateHistory("trashed"); + $asset->setState($asset->getId eq $rootId ? 'trash' : 'trash-limbo'); } + $db->commit; # Trash any shortcuts to this asset my $shortcuts @@ -273,16 +340,6 @@ sub trash { $shortcut->trash({ outputSub => $outputSub, }); } - # Raw database work is more efficient than $asset->update - my $db = $session->db; - $db->beginTransaction; - $outputSub->($i18n->get('Clearing asset tables')); - $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=?",[$session->user->userId, time(), $self->getId]); - $db->commit; - - # Update ourselves since we didn't use update() - $self->{_properties}{state} = "trash"; return 1; } @@ -315,7 +372,7 @@ sub _invokeWorkflowOnExportedFiles { =head2 www_delete -Moves self to trash, returns www_view() method of Container or Parent if canEdit. +Moves self to trash in fork, redirects to Container or Parent if canEdit. Otherwise returns AdminConsole rendered insufficient privilege. =cut @@ -325,13 +382,18 @@ sub www_delete { 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 (isIn($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->session->asset($asset); - return $asset->www_view; + $self->forkWithProgressTree({ + title => 'Delete Assets', + redirect => $asset->getUrl, + method => 'trashInFork', + args => [ $self->getId ], + } + ); } #------------------------------------------------------------------- @@ -343,35 +405,57 @@ Checks to see if a valid CSRF token was received. If not, then it returns insuf 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. - =cut -sub www_deleteList { - my $self = shift; - my $session = $self->session; - my $pb = WebGUI::ProgressBar->new($session); - my $i18n = WebGUI::International->new($session, 'Asset'); - my $form = $session->form; - my @assetIds = $form->param('assetId'); - $pb->start($i18n->get('Delete Assets'), $session->url->extras('adminConsole/assets.gif')); - return $self->session->privilege->insufficient() unless $session->form->validToken; - ASSETID: foreach my $assetId (@assetIds) { - my $asset = eval { WebGUI::Asset->newPending($session,$assetId); }; - if ($@) { - $pb->update(sprintf $i18n->get('Error getting asset with assetId %s'), $assetId); - next ASSETID; - } - if (! ($asset->canEdit && $asset->canEditIfLocked) ) { - $pb->update(sprintf $i18n->get('You cannot edit the asset %s, skipping'), $asset->getTitle); - } - else { - $asset->trash({outputSub => sub { $pb->update(@_); } }); - } - } - my $method = ($session->form->process("proceed")) ? $session->form->process('proceed') : 'manageTrash'; - $pb->finish($self->getUrl('func='.$method)); -} +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 ); + 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 + +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->forkWithProgressTree({ + title => 'Delete Assets', + redirect => $self->getUrl("func=$method"), + method => 'trashInFork', + args => [ $form->get('assetId') ], + } + ); +} ## end sub www_deleteList #------------------------------------------------------------------- @@ -478,29 +562,17 @@ Returns insufficient privileges unless the submitted form passes the validToken sub www_purgeList { my $self = shift; my $session = $self->session; + my $form = $session->form; return $session->privilege->insufficient() unless $session->form->validToken; - my $pb = WebGUI::ProgressBar->new($session); - my $i18n = WebGUI::International->new($session, 'Asset'); - $pb->start($i18n->get('purge'), $session->url->extras('adminConsole/assets.gif')); - - ASSETID: foreach my $id ($session->form->param("assetId")) { - my $asset = eval { WebGUI::Asset->newPending($session,$id); }; - if ($@) { - $pb->update(sprintf $i18n->get('Error getting asset with assetId %s'), $id); - next ASSETID; + my $method = $form->get('proceed') || 'manageTrash'; + $method .= ';systemTrash=1' if $form->get('systemTrash'); + $self->forkWithProgressTree({ + title => 'purge', + redirect => $self->getUrl("func=$method"), + method => 'purgeInFork', + args => [ $form->get('assetId') ], } - if (! $asset->canEdit) { - $pb->update(sprintf $i18n->get('You cannot edit the asset %s, skipping'), $asset->getTitle); - } - else { - $asset->purge({outputSub => sub { $pb->update(@_); } }); - } - } - my $method = ($session->form->process("proceed")) ? $session->form->process('proceed') : 'manageTrash'; - if ($session->form->process('systemTrash') ) { - $method .= ';systemTrash=1'; - } - $pb->finish($self->getUrl('func='.$method)); + ); } #------------------------------------------------------------------- diff --git a/lib/WebGUI/Fork.pm b/lib/WebGUI/Fork.pm index 954bdc2d7..babcf0c5a 100644 --- a/lib/WebGUI/Fork.pm +++ b/lib/WebGUI/Fork.pm @@ -278,11 +278,11 @@ sub forkAndExec { my $class = ref $self; my $json = JSON::encode_json($request); my @inc = map {"-I$_"} @INC; - my @argv = ( "webgui-fork-$id", @inc, "-M$class", "-e$class->runCmd()" ); + my @argv = (@inc, "-M$class", "-e$class->runCmd()" ); $class->daemonize( $json, sub { - exec { $Config{perlpath} } @argv or die "Could not exec: $!"; + exec ($Config{perlpath}, @argv) or die "Could not exec: $!"; } ); } @@ -541,8 +541,10 @@ sub runRequest { my ( $class, $args ) = @_; my ( $root, $config, $sid ) = @{$args}{qw(webguiRoot configFile sessionId)}; my $session = WebGUI::Session->open( $root, $config, undef, undef, $sid ); - my $self = $class->new( $session, $args->{id} ); + my $id = $args->{id}; + my $self = $class->new( $session, $id ); $self->set( { startTime => time } ); + $0 = "webgui-fork-$id"; eval { my ( $module, $subname, $data ) = @{$args}{qw(module subname data)}; WebGUI::Pluggable::run( $module, $subname, [ $self, $data ] );