www_copy and www_pasteList Forkified
This commit is contained in:
parent
ea607eb4c9
commit
e238f72278
12 changed files with 605 additions and 216 deletions
|
|
@ -37,6 +37,9 @@ use WebGUI::HTML;
|
|||
use WebGUI::HTMLForm;
|
||||
use WebGUI::Keyword;
|
||||
use WebGUI::ProgressBar;
|
||||
use WebGUI::ProgressTree;
|
||||
use Monkey::Patch;
|
||||
use WebGUI::Fork;
|
||||
use WebGUI::Search::Index;
|
||||
use WebGUI::TabForm;
|
||||
use WebGUI::Utility;
|
||||
|
|
@ -2555,6 +2558,34 @@ sub setSize {
|
|||
$self->{_properties}{assetSize} = $size;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 setState ( $state )
|
||||
|
||||
Updates the asset table with the new state of the asset.
|
||||
|
||||
=cut
|
||||
|
||||
sub setState {
|
||||
my ($self, $state) = @_;
|
||||
my $sql = q{
|
||||
UPDATE asset
|
||||
SET state = ?,
|
||||
stateChangedBy = ?,
|
||||
stateChanged = ?
|
||||
WHERE assetId = ?
|
||||
};
|
||||
$self->session->db->write(
|
||||
$sql, [
|
||||
$state,
|
||||
$self->session->user->userId,
|
||||
time,
|
||||
$self->getId,
|
||||
]
|
||||
);
|
||||
$self->{_properties}->{state} = $state;
|
||||
$self->purgeCache;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
|
|
|
|||
|
|
@ -46,13 +46,26 @@ Duplicates this asset and the entire subtree below it. Returns the root of the
|
|||
|
||||
If true, then only children, and not descendants, will be duplicated.
|
||||
|
||||
=head3 $state
|
||||
|
||||
Set this to "clipboard" if you want the resulting asset to be on the clipboard
|
||||
(rather than published) when we're done.
|
||||
|
||||
=cut
|
||||
|
||||
sub duplicateBranch {
|
||||
my $self = shift;
|
||||
my $childrenOnly = shift;
|
||||
my ($self, $childrenOnly, $state) = @_;
|
||||
my $session = $self->session;
|
||||
my $log = $session->log;
|
||||
my $clipboard = $state && $state =~ /^clipboard/;
|
||||
|
||||
my $newAsset = $self->duplicate(
|
||||
{ skipAutoCommitWorkflows => 1,
|
||||
skipNotification => 1,
|
||||
state => $state,
|
||||
}
|
||||
);
|
||||
|
||||
my $newAsset = $self->duplicate({skipAutoCommitWorkflows=>1,skipNotification=>1});
|
||||
# Correctly handle positions for Layout assets
|
||||
my $contentPositions = $self->get("contentPositions");
|
||||
my $assetsToHide = $self->get("assetsToHide");
|
||||
|
|
@ -66,7 +79,21 @@ sub duplicateBranch {
|
|||
next;
|
||||
}
|
||||
last unless $child;
|
||||
my $newChild = $childrenOnly ? $child->duplicate({skipAutoCommitWorkflows=>1, skipNotification=>1}) : $child->duplicateBranch;
|
||||
my $newChild;
|
||||
if ($childrenOnly) {
|
||||
$newChild = $child->duplicate(
|
||||
{ skipAutoCommitWorkflows => 1,
|
||||
skipNotification => 1,
|
||||
state => $clipboard && 'clipboard-limbo',
|
||||
}
|
||||
);
|
||||
}
|
||||
elsif($clipboard) {
|
||||
$newChild = $child->duplicateBranch(0, 'clipboard-limbo');
|
||||
}
|
||||
else {
|
||||
$newChild = $child->duplicateBranch;
|
||||
}
|
||||
$newChild->setParent($newAsset);
|
||||
my ($oldChildId, $newChildId) = ($child->getId, $newChild->getId);
|
||||
$contentPositions =~ s/\Q${oldChildId}\E/${newChildId}/g if ($contentPositions);
|
||||
|
|
|
|||
|
|
@ -49,6 +49,55 @@ sub canPaste {
|
|||
return $self->validParent($self->session); ##Lazy call to a class method
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 copyInBackground ( $process, $args )
|
||||
|
||||
WebGUI::Fork method called by www_copy
|
||||
|
||||
=cut
|
||||
|
||||
sub copyInBackground {
|
||||
my ($process, $args) = @_;
|
||||
my $session = $process->session;
|
||||
my $asset = WebGUI::Asset->new($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);
|
||||
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');
|
||||
}
|
||||
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 ( )
|
||||
|
|
@ -97,6 +146,10 @@ A hash reference of options that can modify how this method works.
|
|||
|
||||
Assets that normally autocommit their workflows (like CS Posts, and Wiki Pages) won't if this is true.
|
||||
|
||||
=head4 state
|
||||
|
||||
A state for the duplicated asset (defaults to 'published')
|
||||
|
||||
=cut
|
||||
|
||||
sub duplicate {
|
||||
|
|
@ -132,6 +185,10 @@ sub duplicate {
|
|||
keywords => $keywords,
|
||||
} );
|
||||
|
||||
if (my $state = $options->{state}) {
|
||||
$newAsset->setState($state);
|
||||
}
|
||||
|
||||
return $newAsset;
|
||||
}
|
||||
|
||||
|
|
@ -218,11 +275,12 @@ sub paste {
|
|||
my $i18n=WebGUI::International->new($session, 'Asset');
|
||||
$outputSub->(sprintf $i18n->get('pasting %s'), $pastedAsset->getTitle) if defined $outputSub;
|
||||
if ($self->getId eq $pastedAsset->get("parentId") || $pastedAsset->setParent($self)) {
|
||||
$pastedAsset->publish(['clipboard','clipboard-limbo']); # Paste only clipboard items
|
||||
$pastedAsset->updateHistory("pasted to parent ".$self->getId);
|
||||
|
||||
# Update lineage in search index.
|
||||
my $assetIter = $pastedAsset->getLineageIterator(['self', 'descendants']);
|
||||
my $assetIter = $pastedAsset->getLineageIterator(
|
||||
['self', 'descendants'], {
|
||||
statesToInclude => ['clipboard','clipboard-limbo']
|
||||
}
|
||||
);
|
||||
while ( 1 ) {
|
||||
my $asset;
|
||||
eval { $asset = $assetIter->() };
|
||||
|
|
@ -233,15 +291,67 @@ sub paste {
|
|||
last unless $asset;
|
||||
|
||||
$outputSub->(sprintf $i18n->get('indexing %s'), $pastedAsset->getTitle) if defined $outputSub;
|
||||
$asset->setState('published');
|
||||
$asset->indexContent();
|
||||
}
|
||||
|
||||
$pastedAsset->updateHistory("pasted to parent ".$self->getId);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 pasteInBackground ( )
|
||||
|
||||
WebGUI::Fork method called by www_pasteList
|
||||
|
||||
=cut
|
||||
|
||||
sub pasteInBackground {
|
||||
my ($process, $args) = @_;
|
||||
my $session = $process->session;
|
||||
my $self = WebGUI::Asset->new($session, $args->{assetId});
|
||||
my @roots = grep { $_ && $_->canEdit }
|
||||
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 $tree = WebGUI::ProgressTree->new($session, \@ids);
|
||||
my $patch = Monkey::Patch::patch_class(
|
||||
'WebGUI::Asset', 'indexContent', sub {
|
||||
my $indexContent = shift;
|
||||
my $self = shift;
|
||||
my $id = $self->getId;
|
||||
$tree->focus($id);
|
||||
my $ret = eval { $self->$indexContent(@_) };
|
||||
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 $ret;
|
||||
}
|
||||
);
|
||||
$self->paste($_->getId) for @roots;
|
||||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 www_copy ( )
|
||||
|
|
@ -255,89 +365,54 @@ If with children/descendants is selected, a progress bar will be rendered.
|
|||
sub www_copy {
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
my $http = $session->http;
|
||||
my $redir = $self->getParent->getUrl;
|
||||
return $session->privilege->insufficient unless $self->canEdit;
|
||||
|
||||
my $with = $session->form->get('with');
|
||||
my %args;
|
||||
if ($with eq 'children') {
|
||||
$self->_wwwCopyChildren;
|
||||
$args{childrenOnly} = 1;
|
||||
}
|
||||
elsif ($with eq 'descendants') {
|
||||
$self->_wwwCopyDescendants;
|
||||
elsif ($with ne 'descendants') {
|
||||
my $newAsset = $self->duplicate({
|
||||
skipAutoCommitWorkflows => 1,
|
||||
state => 'clipboard'
|
||||
}
|
||||
);
|
||||
$newAsset->update({ title => $newAsset->getTitle . ' (copy)'});
|
||||
my $result = WebGUI::VersionTag->autoCommitWorkingIfEnabled(
|
||||
$session, {
|
||||
allowComments => 1,
|
||||
returnUrl => $redir,
|
||||
}
|
||||
);
|
||||
$http->setRedirect($redir) unless $result eq 'redirect';
|
||||
return 'redirect';
|
||||
}
|
||||
else {
|
||||
$self->_wwwCopySingle;
|
||||
|
||||
my $tag = WebGUI::VersionTag->getWorking($session);
|
||||
if ($tag->canAutoCommit) {
|
||||
$args{commit} = 1;
|
||||
unless ($session->setting->get('skipCommitComments')) {
|
||||
$redir = $tag->autoCommitUrl($redir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
sub _wwwCopyChildren { shift->_wwwCopyProgress(1) }
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
sub _wwwCopyDescendants { shift->_wwwCopyProgress(0) }
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
sub _wwwCopyFinish {
|
||||
my ($self, $newAsset) = @_;
|
||||
my $session = $self->session;
|
||||
my $i18n = WebGUI::International->new($session, 'Asset');
|
||||
my $title = sprintf("%s (%s)", $self->getTitle, $i18n->get('copy'));
|
||||
$newAsset->update({ title => $title });
|
||||
$newAsset->cut;
|
||||
my $result = WebGUI::VersionTag->autoCommitWorkingIfEnabled(
|
||||
$session, {
|
||||
allowComments => 1,
|
||||
returnUrl => $self->getUrl,
|
||||
$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
|
||||
}
|
||||
);
|
||||
my $redirect = $result eq 'redirect';
|
||||
$session->asset($self->getContainer) unless $redirect;
|
||||
return $redirect;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
sub _wwwCopyProgress {
|
||||
my ($self, $childrenOnly) = @_;
|
||||
my $session = $self->session;
|
||||
my $i18n = WebGUI::International->new($session, 'Asset');
|
||||
|
||||
# This could potentially time out, so we'll render a progress bar.
|
||||
my $pb = WebGUI::ProgressBar->new($session);
|
||||
my @stack;
|
||||
|
||||
return $pb->run(
|
||||
title => $i18n->get('Copy Assets'),
|
||||
icon => $session->url->extras('adminConsole/assets.gif'),
|
||||
code => sub {
|
||||
my $bar = shift;
|
||||
my $newAsset = $self->duplicateBranch($childrenOnly);
|
||||
$bar->update($i18n->get('cut'));
|
||||
my $redirect = $self->_wwwCopyFinish($newAsset);
|
||||
return $redirect ? $self->getUrl : $self->getContainer->getUrl;
|
||||
},
|
||||
wrap => {
|
||||
'WebGUI::Asset::duplicateBranch' => sub {
|
||||
my ($bar, $original, $asset, @args) = @_;
|
||||
push(@stack, $asset->getTitle);
|
||||
my $ret = $asset->$original(@args);
|
||||
pop(@stack);
|
||||
return $ret;
|
||||
},
|
||||
'WebGUI::Asset::duplicate' => sub {
|
||||
my ($bar, $original, $asset, @args) = @_;
|
||||
my $name = join '/', @stack, $asset->getTitle;
|
||||
$bar->update($name);
|
||||
return $asset->$original(@args);
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
sub _wwwCopySingle {
|
||||
my $self = shift;
|
||||
my $newAsset = $self->duplicate({skipAutoCommitWorkflows => 1});
|
||||
my $redirect = $self->_wwwCopyFinish($newAsset);
|
||||
return $redirect ? undef : $self->getContainer->www_view;
|
||||
$http->setRedirect($self->getUrl($pairs));
|
||||
return 'redirect';
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
|
@ -363,9 +438,8 @@ sub www_copyList {
|
|||
foreach my $assetId ($session->form->param("assetId")) {
|
||||
my $asset = WebGUI::Asset->newByDynamicClass($session,$assetId);
|
||||
if ($asset->canEdit) {
|
||||
my $newAsset = $asset->duplicate({skipAutoCommitWorkflows => 1});
|
||||
my $newAsset = $asset->duplicate({skipAutoCommitWorkflows => 1, state => 'clipboard'});
|
||||
$newAsset->update({ title=>$newAsset->getTitle.' (copy)'});
|
||||
$newAsset->cut;
|
||||
}
|
||||
}
|
||||
if ($self->session->form->process("proceed") ne "") {
|
||||
|
|
@ -503,7 +577,7 @@ sub www_duplicateList {
|
|||
foreach my $assetId ($session->form->param("assetId")) {
|
||||
my $asset = WebGUI::Asset->newByDynamicClass($session,$assetId);
|
||||
if ($asset->canEdit) {
|
||||
my $newAsset = $asset->duplicate({skipAutoCommitWorkflows => 1, });
|
||||
my $newAsset = $asset->duplicate({skipAutoCommitWorkflows => 1});
|
||||
$newAsset->update({ title=>$newAsset->getTitle.' (copy)'});
|
||||
}
|
||||
}
|
||||
|
|
@ -657,25 +731,29 @@ sub www_pasteList {
|
|||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
return $session->privilege->insufficient() unless $self->canEdit && $session->form->validToken;
|
||||
my $form = $session->form;
|
||||
my $pb = WebGUI::ProgressBar->new($session);
|
||||
##Need to store the list of assetIds for the status subroutine
|
||||
my @assetIds = $form->param('assetId');
|
||||
##Need to set the URL that should be displayed when it is done
|
||||
my $i18n = WebGUI::International->new($session, 'Asset');
|
||||
$pb->start($i18n->get('Paste Assets'), $session->url->extras('adminConsole/assets.gif'));
|
||||
ASSET: foreach my $clipId (@assetIds) {
|
||||
next ASSET unless $clipId;
|
||||
my $pasteAsset = WebGUI::Asset->newPending($session, $clipId);
|
||||
if (! $pasteAsset && $pasteAsset->canEdit) {
|
||||
$pb->update(sprintf $i18n->get('skipping %s'), $pasteAsset->getTitle);
|
||||
next ASSET;
|
||||
}
|
||||
$self->paste($clipId, sub {$pb->update(@_);});
|
||||
}
|
||||
return $pb->finish( ($form->param('proceed') eq 'manageAssets') ? $self->getUrl('op=assetManager') : $self->getUrl );
|
||||
}
|
||||
|
||||
my $form = $session->form;
|
||||
my $process = WebGUI::Fork->start(
|
||||
$session, 'WebGUI::Asset', 'pasteInBackground', {
|
||||
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';
|
||||
}
|
||||
|
||||
1;
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ use WebGUI::Utility ();
|
|||
use WebGUI::Session;
|
||||
use URI::URL;
|
||||
use Scope::Guard qw(guard);
|
||||
use WebGUI::ProgressTree;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
|
|
@ -400,7 +401,7 @@ sub exportBranch {
|
|||
close $handle;
|
||||
$cs->var->end;
|
||||
$cs->close();
|
||||
$asset->$report('collateral notes', $output);
|
||||
$asset->$report('collateral notes', $output) if $output;
|
||||
};
|
||||
my $path = $asset->exportGetUrlAsPath;
|
||||
eval { $asset->exportAssetCollateral($path, $options, $cs) };
|
||||
|
|
@ -666,48 +667,33 @@ specified asset and keeps a json structure as the status.
|
|||
=cut
|
||||
|
||||
sub exportInFork {
|
||||
my ($process, $args) = @_;
|
||||
my $self = WebGUI::Asset->new($process->session, delete $args->{assetId});
|
||||
my ( $process, $args ) = @_;
|
||||
my $session = $process->session;
|
||||
my $self = WebGUI::Asset->new( $session, delete $args->{assetId} );
|
||||
$args->{indexFileName} = delete $args->{index};
|
||||
my %flat;
|
||||
|
||||
my $hashify; $hashify = sub {
|
||||
my ($asset, $depth) = @_;
|
||||
return if $depth < 1;
|
||||
my $hash = { url => $asset->getUrl };
|
||||
my $children = $asset->getLineage(['children'], { returnObjects => 1 });
|
||||
$hash->{children} = [ map { $hashify->($_, $depth - 1) } @$children ];
|
||||
$flat{$asset->getId} = $hash;
|
||||
return $hash;
|
||||
};
|
||||
my $tree = $hashify->($self, $args->{depth});
|
||||
my $last = $tree;
|
||||
my $assetIds = $self->exportGetDescendants( undef, $args->{depth} );
|
||||
my $tree = WebGUI::ProgressTree->new( $session, $assetIds );
|
||||
my %reports = (
|
||||
'bad user privileges' => sub { shift->{badUserPrivileges} = 1 },
|
||||
'not exportable' => sub { shift->{notExportable} = 1 },
|
||||
'done' => sub { shift->{done} = 1 },
|
||||
'exporting page' => sub {
|
||||
my $hash = shift;
|
||||
$hash->{current} = 1;
|
||||
delete $last->{current};
|
||||
$last = $hash;
|
||||
'done' => sub { $tree->success(shift) },
|
||||
'exporting page' => sub { $tree->focus(shift) },
|
||||
'collateral notes' => sub { $tree->note(@_) },
|
||||
'bad user privileges' => sub {
|
||||
$tree->failure( shift, 'Bad User Privileges' );
|
||||
},
|
||||
'collateral notes' => sub {
|
||||
my ($hash, $text) = @_;
|
||||
$hash->{collateralNotes} = $text if $text;
|
||||
'not exportable' => sub {
|
||||
$tree->failure( shift, 'Not Exportable' );
|
||||
},
|
||||
);
|
||||
$args->{report} = sub {
|
||||
my ($asset, $key, @args) = @_;
|
||||
my ( $asset, $key, @args ) = @_;
|
||||
my $code = $reports{$key};
|
||||
my $hash = $flat{$asset->getId};
|
||||
$code->($hash, @args);
|
||||
$process->update(sub { JSON::encode_json($tree) });
|
||||
$code->( $asset->getId, @args );
|
||||
$process->update( sub { $tree->json } );
|
||||
};
|
||||
$self->exportAsHtml($args);
|
||||
delete $last->{current};
|
||||
$process->update(JSON::encode_json($tree));
|
||||
}
|
||||
$tree->focus(undef);
|
||||
$process->update( $tree->json );
|
||||
} ## end sub exportInFork
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
|
|
@ -1036,7 +1022,12 @@ sub www_exportStatus {
|
|||
}
|
||||
);
|
||||
$process->setGroup(13);
|
||||
my $pairs = $process->contentPairs('AssetExport');
|
||||
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';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,41 +88,59 @@ sub canView {
|
|||
my $user = shift || $session->user;
|
||||
$user = WebGUI::User->new( $session, $user )
|
||||
unless eval { $user->isa('WebGUI::User') };
|
||||
return 1 if $user->isAdmin;
|
||||
return $user->isInGroup( $self->getGroupId );
|
||||
return
|
||||
$user->isAdmin
|
||||
|| $user->userId eq $self->getUserId
|
||||
|| $user->isInGroup( $self->getGroupId );
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 contentPairs ($module, $pid)
|
||||
=head2 contentPairs ($module, $pid, $extra)
|
||||
|
||||
Returns a bit of query string useful for redirecting to a
|
||||
WebGUI::Operation::Fork plugin. $module should be the bit that comes after
|
||||
WebGUI::Operation::Fork, e.g. $process->contentPairs('Foo') should return
|
||||
something like "op=fork;module=Foo;pid=adlfjafo87ad9f78a7", which will
|
||||
get dispatched to WebGUI::Operation::Fork::Foo::handler($process)
|
||||
get dispatched to WebGUI::Operation::Fork::Foo::handler($process).
|
||||
|
||||
$extra is an optional hashref that will add further parameters onto the list
|
||||
of pairs, e.g. { foo => 'bar' } becomes ';foo=bar'
|
||||
|
||||
=cut
|
||||
|
||||
sub contentPairs {
|
||||
my ( $self, $module ) = @_;
|
||||
my $pid = $self->getId;
|
||||
return "op=fork;module=$module;pid=$pid";
|
||||
}
|
||||
my ( $self, $module, $extra ) = @_;
|
||||
my $url = $self->session->url;
|
||||
my $pid = $self->getId;
|
||||
my %params = (
|
||||
op => 'fork',
|
||||
module => $module,
|
||||
pid => $self->getId,
|
||||
$extra ? %$extra : ()
|
||||
);
|
||||
return join(
|
||||
';',
|
||||
map {
|
||||
my $k = $_;
|
||||
join( '=', map { $url->escape($_) } ( $k, $params{$k} ) );
|
||||
} keys %params
|
||||
);
|
||||
} ## end sub contentPairs
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 create ( )
|
||||
|
||||
Internal class method. Creates a new Fork object and inserts a
|
||||
blank row of data into the db.
|
||||
Internal class method. Creates a new Fork object inserts it into the db.
|
||||
|
||||
=cut
|
||||
|
||||
sub create {
|
||||
my ( $class, $session ) = @_;
|
||||
my $id = $session->id->generate;
|
||||
$session->db->setRow( $class->tableName, 'id', {}, $id );
|
||||
my %data = ( userId => $session->user->userId );
|
||||
$session->db->setRow( $class->tableName, 'id', \%data, $id );
|
||||
bless { session => $session, id => $id }, $class;
|
||||
}
|
||||
|
||||
|
|
@ -275,9 +293,9 @@ sub forkAndExec {
|
|||
|
||||
Get data from the database record for this process (returned as a simple list,
|
||||
not an arrayref). Valid keys are: id, status, error, startTime, endTime,
|
||||
finished, groupId. They all have more specific accessors, but you can use
|
||||
this to get several at once if you're very careful. You should probably use
|
||||
the accessors, though, since some of them have extra logic.
|
||||
finished, groupId, userId. They all have more specific accessors, but you can
|
||||
use this to get several at once if you're very careful. You should probably
|
||||
use the accessors, though, since some of them have extra logic.
|
||||
|
||||
=cut
|
||||
|
||||
|
|
@ -356,6 +374,16 @@ sub getStatus {
|
|||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 getUserId
|
||||
|
||||
Returns the userId of the user who initiated this Fork.
|
||||
|
||||
=cut
|
||||
|
||||
sub getUserId { $_[0]->get('userId') }
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 init ( )
|
||||
|
||||
Spawn a master process from which Forks will fork(). The intent
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
package WebGUI::Fork::AssetExport;
|
||||
package WebGUI::Fork::ProgressTree;
|
||||
|
||||
=head1 LEGAL
|
||||
|
||||
|
|
@ -19,12 +19,12 @@ use warnings;
|
|||
|
||||
=head1 NAME
|
||||
|
||||
WebGUI::Fork::AssetExport
|
||||
WebGUI::Fork::ProgressTree
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Renders an admin console page that polls ::Status to draw a friendly graphical
|
||||
representation of how an export is coming along.
|
||||
representation of how progress on a tree of assets is coming along.
|
||||
|
||||
=head1 SUBROUTINES
|
||||
|
||||
|
|
@ -33,77 +33,99 @@ These subroutines are available from this package:
|
|||
=cut
|
||||
|
||||
use Template;
|
||||
use HTML::Entities;
|
||||
use JSON;
|
||||
|
||||
my $template = <<'TEMPLATE';
|
||||
<p>
|
||||
Currently exporting <span id='current'></span>
|
||||
<div id='meter'>
|
||||
<div id='meter-bar'>
|
||||
<div id='meter-text'></div>
|
||||
</div>
|
||||
</div>
|
||||
Current asset: <span id='focus'></span>
|
||||
(<span id='finished'></span>/<span id='total'></span>).<br />
|
||||
<span id='elapsed'></span> seconds elapsed.
|
||||
</p>
|
||||
<ul id='tree'></ul>
|
||||
[% MACRO yui(file) BLOCK %]
|
||||
<script src="$extras/yui/build/$file"></script>
|
||||
[% END %]
|
||||
[% MACRO inc(file) BLOCK %]<script src="$extras/$file"></script>[% END %]
|
||||
[% MACRO yui(file) BLOCK %][% inc("yui/build/$file") %][% END %]
|
||||
[% yui("yahoo/yahoo-min.js") %]
|
||||
[% yui("json/json-min.js") %]
|
||||
[% yui("event/event-min.js") %]
|
||||
[% yui("connection/connection_core-min.js") %]
|
||||
[% inc("underscore/underscore-min.js") %]
|
||||
<script>
|
||||
(function (statusUrl) {
|
||||
var JSON = YAHOO.lang.JSON;
|
||||
(function (params) {
|
||||
var JSON = YAHOO.lang.JSON, statusUrl = params.statusUrl;
|
||||
|
||||
function finish() {
|
||||
var redir = params.redirect;
|
||||
if (redir) {
|
||||
setTimeout(function() {
|
||||
// The idea here is to only allow local redirects
|
||||
var loc = window.location;
|
||||
loc.href = loc.protocol + '//' + loc.host + redir;
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
function error(msg) {
|
||||
alert(msg);
|
||||
}
|
||||
function setHtml(id, html) {
|
||||
document.getElementById(id).innerHTML = html;
|
||||
}
|
||||
function draw(data) {
|
||||
var ul, old, finished = 0, total = 0, current;
|
||||
var tree, finished = 0, total = 0, focus, pct;
|
||||
function recurse(asset, node) {
|
||||
var li = document.createElement('li'), txt, notes, ul, i;
|
||||
|
||||
total += 1;
|
||||
|
||||
txt = asset.url;
|
||||
if (asset.current) {
|
||||
li.className += 'current';
|
||||
current = asset.url;
|
||||
if (asset.focus) {
|
||||
li.className += 'focus';
|
||||
focus = asset.url;
|
||||
}
|
||||
else if (asset.badUserPrivileges) {
|
||||
li.className = 'error';
|
||||
txt += ' (bad user privileges)';
|
||||
else if (asset.failure) {
|
||||
li.className = 'failure';
|
||||
txt += ' (' + asset.failure + ')';
|
||||
finished += 1;
|
||||
}
|
||||
else if (asset.notExportable) {
|
||||
li.className = 'error';
|
||||
txt += ' (not exportable)';
|
||||
finished += 1;
|
||||
}
|
||||
else if (asset.done) {
|
||||
li.className = 'done';
|
||||
else if (asset.success) {
|
||||
li.className = 'success';
|
||||
finished += 1;
|
||||
}
|
||||
li.appendChild(document.createTextNode(txt));
|
||||
if (asset.collateralNotes) {
|
||||
notes = document.createElement('p');
|
||||
notes.innerHTML = asset.collateralNotes;
|
||||
li.appendChild(notes);
|
||||
if (notes = asset.notes) {
|
||||
_.each(notes, function (note) {
|
||||
var p = document.createElement('p');
|
||||
p.innerHTML = note;
|
||||
li.appendChild(p);
|
||||
});
|
||||
}
|
||||
if (asset.children) {
|
||||
ul = document.createElement('ul');
|
||||
for (i = 0; i < asset.children.length; i += 1) {
|
||||
recurse(asset.children[i], ul);
|
||||
li.appendChild(ul);
|
||||
}
|
||||
_.each(asset.children, function (child) {
|
||||
recurse(child, ul);
|
||||
});
|
||||
li.appendChild(ul);
|
||||
}
|
||||
node.appendChild(li);
|
||||
}
|
||||
ul = document.createElement('ul');
|
||||
old = document.getElementById('tree');
|
||||
ul.id = old.id;
|
||||
recurse(JSON.parse(data.status), ul);
|
||||
old.parentNode.replaceChild(ul, old);
|
||||
document.getElementById('total').innerHTML = total;
|
||||
document.getElementById('finished').innerHTML = finished;
|
||||
document.getElementById('current').innerHTML = current || 'nothing';
|
||||
document.getElementById('elapsed').innerHTML = data.elapsed;
|
||||
tree = document.getElementById('tree');
|
||||
tree.innerHTML = '';
|
||||
_.each(JSON.parse(data.status), function (root) {
|
||||
recurse(root, tree);
|
||||
});
|
||||
pct = Math.floor((finished/total)*100) + '%';
|
||||
|
||||
setHtml('meter-text', pct);
|
||||
document.getElementById('meter-bar').style.width = pct;
|
||||
|
||||
setHtml('total', total);
|
||||
setHtml('finished', finished);
|
||||
setHtml('focus', focus || 'nothing');
|
||||
setHtml('elapsed', data.elapsed);
|
||||
}
|
||||
function fetch() {
|
||||
var callback = {
|
||||
|
|
@ -119,6 +141,7 @@ Currently exporting <span id='current'></span>
|
|||
}
|
||||
else if (data.finished) {
|
||||
draw(data);
|
||||
finish();
|
||||
}
|
||||
else {
|
||||
draw(data);
|
||||
|
|
@ -132,16 +155,21 @@ Currently exporting <span id='current'></span>
|
|||
YAHOO.util.Connect.asyncRequest('GET', statusUrl, callback, null);
|
||||
}
|
||||
YAHOO.util.Event.onDOMReady(fetch);
|
||||
}("$statusUrl"));
|
||||
}([% params %]));
|
||||
</script>
|
||||
TEMPLATE
|
||||
|
||||
my $stylesheet = <<'STYLESHEET';
|
||||
<style>
|
||||
#meter { border: thin solid black; position: relative }
|
||||
#meter-bar { background-color: lime; font-size: 18pt;
|
||||
height: 20pt; line-height: 20pt }
|
||||
#meter-text { position: absolute; top: 0; left: 0; width: 100%;
|
||||
text-align: center }
|
||||
#tree li { color: black }
|
||||
#tree li.current { color: cyan }
|
||||
#tree li.error { color: red }
|
||||
#tree li.done { color: green }
|
||||
#tree li.focus { color: cyan }
|
||||
#tree li.failure { color: red }
|
||||
#tree li.success { color: green }
|
||||
</style>
|
||||
STYLESHEET
|
||||
|
||||
|
|
@ -157,17 +185,21 @@ sub handler {
|
|||
my $process = shift;
|
||||
my $session = $process->session;
|
||||
my $url = $session->url;
|
||||
my $form = $session->form;
|
||||
my $tt = Template->new( { INTERPOLATE => 1 } );
|
||||
my %vars = (
|
||||
statusUrl => $url->page( $process->contentPairs('Status') ),
|
||||
extras => $session->url->extras,
|
||||
params => JSON::encode_json( {
|
||||
statusUrl => $url->page( $process->contentPairs('Status') ),
|
||||
redirect => scalar $form->get('proceed'),
|
||||
}
|
||||
),
|
||||
extras => $url->extras,
|
||||
);
|
||||
$tt->process( \$template, \%vars, \my $content ) or die $tt->error;
|
||||
|
||||
my $console = WebGUI::AdminConsole->new( $process->session, 'assets' );
|
||||
my $console = WebGUI::AdminConsole->new( $session, $form->get('icon') );
|
||||
$session->style->setRawHeadTags($stylesheet);
|
||||
my $i18n = WebGUI::International->new( $session, 'Asset' );
|
||||
return $console->render( $content, $i18n->get('Page Export Status') );
|
||||
return $console->render( $content, encode_entities( $form->get('title') ) );
|
||||
} ## end sub handler
|
||||
|
||||
1;
|
||||
172
lib/WebGUI/ProgressTree.pm
Normal file
172
lib/WebGUI/ProgressTree.pm
Normal file
|
|
@ -0,0 +1,172 @@
|
|||
package WebGUI::ProgressTree;
|
||||
|
||||
use warnings;
|
||||
use strict;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
WebGUI::ProgressTree
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Helper functions for maintaining a JSON represtentation of the progress of an
|
||||
operation that modifies a tree of assets. See WebGUI::Fork::ProgressTree for a
|
||||
status page that renders this.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
my $tree = WebGUI::ProgressTree->new($session, \@assetIds);
|
||||
$tree->success($assetId);
|
||||
$tree->failure($assetId, $reason);
|
||||
$tree->note($assetId, 'something about this one...');
|
||||
|
||||
=head1 LEGAL
|
||||
|
||||
-------------------------------------------------------------------
|
||||
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
|
||||
-------------------------------------------------------------------
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 new ($session, $assetIds)
|
||||
|
||||
Constructs new tree object for tracking the progress of $assetIds.
|
||||
|
||||
=cut
|
||||
|
||||
sub new {
|
||||
my ( $class, $session, $assetIds ) = @_;
|
||||
my $db = $session->db;
|
||||
my $dbh = $db->dbh;
|
||||
my $set = join( ',', map { $dbh->quote($_) } @$assetIds );
|
||||
my $sql = qq{
|
||||
SELECT a.assetId, a.parentId, d.url
|
||||
FROM asset a INNER JOIN assetData d ON a.assetId = d.assetId
|
||||
WHERE a.assetId IN ($set)
|
||||
ORDER BY a.lineage ASC, d.revisionDate DESC
|
||||
};
|
||||
my $sth = $db->read($sql);
|
||||
my ( %flat, @roots );
|
||||
|
||||
while ( my $asset = $sth->hashRef ) {
|
||||
my ( $id, $parentId ) = delete @{$asset}{ 'assetId', 'parentId' };
|
||||
|
||||
# We'll get back multiple rows for each asset, but the first one is
|
||||
# the latest. Skip the others.
|
||||
next if $flat{$id};
|
||||
$flat{$id} = $asset;
|
||||
if ( my $parent = $flat{$parentId} ) {
|
||||
push( @{ $parent->{children} }, $asset );
|
||||
}
|
||||
else {
|
||||
push( @roots, $asset );
|
||||
}
|
||||
}
|
||||
my $self = {
|
||||
session => $session,
|
||||
tree => \@roots,
|
||||
flat => \%flat,
|
||||
};
|
||||
bless $self, $class;
|
||||
} ## end sub new
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 success ($assetId)
|
||||
|
||||
Whatever we were doing to $assetId succeeded. Woohoo!
|
||||
|
||||
=cut
|
||||
|
||||
sub success {
|
||||
my ( $self, $assetId ) = @_;
|
||||
$self->{flat}->{$assetId}->{success} = 1;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 failure ($assetId, $reason)
|
||||
|
||||
Whatever we were doing to $assetId didn't work for $reason. Aww.
|
||||
|
||||
=cut
|
||||
|
||||
sub failure {
|
||||
my ( $self, $assetId, $reason ) = @_;
|
||||
$self->{flat}->{$assetId}->{failure} = $reason;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 note ($assetId, $note)
|
||||
|
||||
Add some extra text. WebGUI::Fork::ProgressTree displays these as paragraphs
|
||||
under the node for this asset.
|
||||
|
||||
=cut
|
||||
|
||||
sub note {
|
||||
my ( $self, $assetId, $note ) = @_;
|
||||
push( @{ $self->{flat}->{$assetId}->{notes} }, $note );
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 focus ($assetId)
|
||||
|
||||
Make a note that this is the asset that we are currently doing something with.
|
||||
|
||||
=cut
|
||||
|
||||
sub focus {
|
||||
my ( $self, $assetId ) = @_;
|
||||
if ( my $last = delete $self->{last} ) {
|
||||
delete $last->{focus};
|
||||
}
|
||||
if ($assetId) {
|
||||
my $focus = $self->{last} = $self->{flat}->{$assetId};
|
||||
$focus->{focus} = 1;
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 tree
|
||||
|
||||
A hashy representation of the status of this tree of assets.
|
||||
|
||||
=cut
|
||||
|
||||
sub tree { $_[0]->{tree} }
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 json
|
||||
|
||||
$self->tree encoded as json.
|
||||
|
||||
=cut
|
||||
|
||||
sub json { JSON::encode_json( $_[0]->tree ) }
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 session
|
||||
|
||||
The WebGUI::Session this progress tree is associated with.
|
||||
|
||||
=cut
|
||||
|
||||
sub session { $_[0]->{session} }
|
||||
|
||||
1;
|
||||
|
|
@ -37,6 +37,23 @@ These methods are available from this class:
|
|||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 autoCommitUrl ( $base )
|
||||
|
||||
Returns the url autoCommitWorkingIfEnabled would redirect to if it were going
|
||||
to.
|
||||
|
||||
=cut
|
||||
|
||||
sub autoCommitUrl {
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
my $url = $session->url;
|
||||
my $base = shift || $url->page;
|
||||
my $id = $self->getId;
|
||||
return $url->append($base, "op=commitVersionTag;tagId=$id");
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
|
|
@ -75,25 +92,13 @@ sub autoCommitWorkingIfEnabled {
|
|||
return undef
|
||||
unless $versionTag;
|
||||
|
||||
#Auto commit is no longer determined from autoRequestCommit
|
||||
|
||||
# auto commit assets
|
||||
# save and commit button and site wide auto commit work the same
|
||||
# Do not auto commit if tag is system wide tag or tag belongs to someone else
|
||||
if (
|
||||
$options->{override}
|
||||
|| ( $class->getVersionTagMode($session) eq q{autoCommit}
|
||||
&& ! $versionTag->get(q{isSiteWide})
|
||||
&& $versionTag->get(q{createdBy}) eq $session->user()->userId()
|
||||
)
|
||||
) {
|
||||
if ($options->{override} || $versionTag->canAutoCommit) {
|
||||
if ($session->setting->get("skipCommitComments") || !$options->{allowComments}) {
|
||||
$versionTag->requestCommit;
|
||||
return 'commit';
|
||||
}
|
||||
else {
|
||||
my $url = $options->{returnUrl} || $session->url->page;
|
||||
$url = $session->url->append($url, "op=commitVersionTag;tagId=" . $versionTag->getId);
|
||||
my $url = $versionTag->autoCommitUrl($options->{returnUrl});
|
||||
$session->http->setRedirect($url);
|
||||
return 'redirect';
|
||||
}
|
||||
|
|
@ -103,6 +108,24 @@ sub autoCommitWorkingIfEnabled {
|
|||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 canAutoCommit
|
||||
|
||||
Returns true if we would autocommit this tag without an override.
|
||||
|
||||
=cut
|
||||
|
||||
sub canAutoCommit {
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
my $class = ref $self;
|
||||
my $mode = $class->getVersionTagMode($session);
|
||||
return $mode eq 'autoCommit'
|
||||
&& !$self->get('isSiteWide')
|
||||
&& $self->get('createdBy') eq $session->user->userId;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 clearWorking ( )
|
||||
|
||||
Makes it so this tag is no longer the working tag for any user.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue