Merge branch 'master' into WebGUI8. Merged up to 7.10.4
This commit is contained in:
commit
5f3014aaee
66 changed files with 3078 additions and 997 deletions
|
|
@ -391,6 +391,9 @@ use WebGUI::HTML;
|
|||
use WebGUI::HTMLForm;
|
||||
use WebGUI::Keyword;
|
||||
require WebGUI::ProgressBar;
|
||||
use WebGUI::ProgressTree;
|
||||
use Monkey::Patch;
|
||||
use WebGUI::Fork;
|
||||
use WebGUI::Search::Index;
|
||||
use WebGUI::TabForm;
|
||||
use WebGUI::PassiveAnalytics::Logging;
|
||||
|
|
@ -836,6 +839,59 @@ sub fixUrl {
|
|||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 forkWithStatusPage ($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 plugin
|
||||
|
||||
The WebGUI::Operation::Fork plugin to render (e.g. ProgressTree)
|
||||
|
||||
=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 forkWithStatusPage {
|
||||
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(
|
||||
$args->{plugin}, {
|
||||
title => $i18n->get( $args->{title} ),
|
||||
icon => 'assets',
|
||||
proceed => $args->{redirect} || '',
|
||||
}
|
||||
);
|
||||
$session->http->setRedirect( $self->getUrl($pairs) );
|
||||
return 'redirect';
|
||||
} ## end sub forkWithStatusPage
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 getClassById ( $session, $assetId )
|
||||
|
||||
Class method that looks up a className for an object in the database, using it's assetId.
|
||||
|
|
@ -2412,6 +2468,35 @@ sub setSize {
|
|||
$self->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 = ?
|
||||
};
|
||||
my @props = ($state, $self->session->user->userId, time);
|
||||
$self->session->db->write(
|
||||
$sql, [
|
||||
@props,
|
||||
$self->getId,
|
||||
]
|
||||
);
|
||||
$self->state($state);
|
||||
$self->stateChangedBy($props[1]);
|
||||
$self->stateChanged($props[2]);
|
||||
$self->purgeCache;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
|
|
@ -2477,23 +2562,7 @@ sub write {
|
|||
|
||||
Returns the asset's url without any site specific prefixes. If you want a browser friendly url see the getUrl() method.
|
||||
|
||||
# set the property
|
||||
if ($propertyDefinition->{serialize}) {
|
||||
# Only serialize references
|
||||
if ( ref $value ) {
|
||||
$setPairs{$property} = JSON->new->canonical->encode($value);
|
||||
}
|
||||
# Passing already serialized JSON string
|
||||
elsif ( $value ) {
|
||||
$setPairs{$property} = $value;
|
||||
$value = JSON->new->decode( $value ); # for setting in _properties, below
|
||||
}
|
||||
}
|
||||
else {
|
||||
$setPairs{$property} = $value;
|
||||
}
|
||||
$self->{_properties}{$property} = $value;
|
||||
}
|
||||
=head3 value
|
||||
|
||||
The new value to set the URL to.
|
||||
|
||||
|
|
|
|||
|
|
@ -400,6 +400,22 @@ sub canEdit {
|
|||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 duplicate ( )
|
||||
|
||||
Extend the super class to duplicate the storage location.
|
||||
|
||||
=cut
|
||||
|
||||
sub duplicate {
|
||||
my $self = shift;
|
||||
my $newAsset = $self->SUPER::duplicate(@_);
|
||||
my $newStorage = $self->getStorageLocation->copy;
|
||||
$newAsset->update({storageId=>$newStorage->getId});
|
||||
return $newAsset;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 generateRecurrence (date)
|
||||
|
||||
Creates an recurrence event in the parent calendar for the given date
|
||||
|
|
|
|||
|
|
@ -277,7 +277,7 @@ sub getContentLastModified {
|
|||
my $shortcut = $self->getShortcut; # XXX "newById must get an assetId"
|
||||
my $shortcuttedRev;
|
||||
if (defined $shortcut) {
|
||||
$shortcuttedRev = $shortcut->get('revisionDate');
|
||||
$shortcuttedRev = $shortcut->getContentLastModified;
|
||||
return $assetRev > $shortcuttedRev ? $assetRev : $shortcuttedRev;
|
||||
} else {
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -762,7 +762,7 @@ ENDHTML
|
|||
. $i18n->get( "warning default template" )
|
||||
. q{</p><p>}
|
||||
. sprintf( q{<a href="} . $duplicateUrl . q{">%s</a>}, $i18n->get( "make duplicate label" ) )
|
||||
. q{</p></div}
|
||||
. q{</p></div>}
|
||||
;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ property mailAccount => (
|
|||
tab => 'mail',
|
||||
label => [ "mail account", 'Asset_Collaboration' ],
|
||||
hoverHelp => [ "mail account help", 'Asset_Collaboration' ],
|
||||
extras => 'autocomplete="off"',
|
||||
);
|
||||
property mailPassword => (
|
||||
fieldType => "password",
|
||||
|
|
|
|||
|
|
@ -107,6 +107,24 @@ sub _fetchDepartments {
|
|||
}
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 getStatusList
|
||||
|
||||
Returns the statusList property as an array
|
||||
|
||||
=cut
|
||||
|
||||
sub getStatusList {
|
||||
my $self = shift;
|
||||
my $text = $self->get('statusList');
|
||||
|
||||
return
|
||||
grep { $_ } # no empty lines
|
||||
map { s/^\s+//; s/\s+$//; $_ } # trim
|
||||
split(/\r\n|\r|\n/, $text); # seperated by any kind of newline
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 prepareView ( )
|
||||
|
|
@ -174,16 +192,9 @@ sub view {
|
|||
}
|
||||
|
||||
my $statusUserId = $self->session->scratch->get("userId") || $self->session->user->userId;
|
||||
my $statusListString = $self->statusList;
|
||||
my @statusListArray = split("\n",$statusListString);
|
||||
my $statusListHashRef;
|
||||
tie %$statusListHashRef, 'Tie::IxHash';
|
||||
|
||||
foreach my $status (@statusListArray) {
|
||||
chomp($status);
|
||||
next if $status eq "";
|
||||
$statusListHashRef->{$status} = $status;
|
||||
}
|
||||
tie my %statusOptions, 'Tie::IxHash', (
|
||||
map { $_ => $_ } $self->getStatusList
|
||||
);
|
||||
|
||||
#$self->session->log->warn("VIEW: userId: ".$statusUserId."\n" );
|
||||
my ($status) = $session->db->quickArray(
|
||||
|
|
@ -222,7 +233,7 @@ sub view {
|
|||
$f->radioList(
|
||||
-name=>"status",
|
||||
-value=>$status,
|
||||
-options=>$statusListHashRef,
|
||||
-options=>\%statusOptions,
|
||||
-label=>$i18n->get(5),
|
||||
-hoverHelp=>$i18n->get('5 description'),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -298,9 +298,10 @@ sub getFolder {
|
|||
my ($self, $date) = @_;
|
||||
my $session = $self->session;
|
||||
my $folderName = $session->datetime->epochToHuman($date, DATE_FORMAT);
|
||||
my $folderUrl = join '/', $self->getUrl, $folderName;
|
||||
my $folderUrl = $self->getFolderUrl($folderName);
|
||||
my $folder = eval { WebGUI::Asset->newByUrl($session, $folderUrl); };
|
||||
return $folder if !Exception::Class->caught();
|
||||
|
||||
##The requested folder doesn't exist. Make it and autocommit it.
|
||||
|
||||
##For a fully automatic commit, save the current tag, create a new one
|
||||
|
|
@ -335,6 +336,26 @@ sub getFolder {
|
|||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 getFolderUrl ( name )
|
||||
|
||||
Constructs a url for a subfolder with the given name.
|
||||
|
||||
=cut
|
||||
|
||||
sub getFolderUrl {
|
||||
my ($self, $name) = @_;
|
||||
my $session = $self->session;
|
||||
my $base = $self->getUrl;
|
||||
$base =~ s/(.*)\..*/$1/;
|
||||
my $url = "$base/$name";
|
||||
if (my $ext = $session->setting->get('urlExtension')) {
|
||||
$url .= ".$ext";
|
||||
}
|
||||
return $session->url->urlize($url);
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 getKeywordFilename ( $keyword )
|
||||
|
||||
Returns the name for the file containing stories that match this keyword. Used
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ sub _defaultThingId_options {
|
|||
return $things;
|
||||
}
|
||||
|
||||
use WebGUI::ProgressBar;
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
|
|
@ -2628,6 +2630,11 @@ sub www_export {
|
|||
my $thingProperties = $self->getThing($thingId);
|
||||
return $session->privilege->insufficient() unless $self->hasPrivileges($thingProperties->{groupIdExport});
|
||||
|
||||
my $i18n = WebGUI::International->new($session, 'Asset_Thingy');
|
||||
my $pb = WebGUI::ProgressBar->new($session);
|
||||
$pb->start($i18n->get('export label').' '.$thingProperties->{label}, $session->url->extras('assets/thingy.gif'));
|
||||
$pb->update($i18n->get('Creating column headers'));
|
||||
my $tempStorage = WebGUI::Storage->createTemp($session);
|
||||
$fields = $session->db->read('select * from Thingy_fields where assetId =? and thingId = ? order by sequenceNumber',
|
||||
[$self->getId,$thingId]);
|
||||
while (my $field = $fields->hashRef) {
|
||||
|
|
@ -2649,9 +2656,13 @@ sub www_export {
|
|||
|
||||
### Loop through the returned structure and put it through Text::CSV
|
||||
# Column heads
|
||||
$out = WebGUI::Text::joinCSV(@fieldLabels);
|
||||
my $csv_filename = 'export_'.$thingProperties->{label}.'.csv';
|
||||
$tempStorage->addFileFromScalar($csv_filename, WebGUI::Text::joinCSV(@fieldLabels));
|
||||
open my $CSV, '>', $tempStorage->getPath($csv_filename);
|
||||
|
||||
# Data lines
|
||||
$pb->update($i18n->get('Writing data'));
|
||||
my $rowCounter = 0;
|
||||
while (my $data = $sth->hashRef) {
|
||||
my @fieldValues;
|
||||
foreach my $field (@fields){
|
||||
|
|
@ -2660,19 +2671,20 @@ sub www_export {
|
|||
my $value = $self->getFieldValue($data->{"field_".$fieldId},$field->{properties},"%y-%m-%d","%y-%m-%d %j:%n:%s");
|
||||
push(@fieldValues, $value);
|
||||
}
|
||||
foreach my $metaDataField (@metaDataFields){
|
||||
push(@fieldValues,$data->{$metaDataField});
|
||||
if ($thingProperties->{exportMetaData}) {
|
||||
foreach my $metaDataField (@metaDataFields){
|
||||
push(@fieldValues,$data->{$metaDataField});
|
||||
}
|
||||
}
|
||||
$out .= "\n".WebGUI::Text::joinCSV(
|
||||
@fieldValues
|
||||
);
|
||||
print $CSV "\n".WebGUI::Text::joinCSV( @fieldValues );
|
||||
#if (! ++$rowCounter % 25) {
|
||||
$pb->update($i18n->get('Writing data'));
|
||||
#}
|
||||
}
|
||||
|
||||
$fileName = "export_".$thingProperties->{label}.".csv";
|
||||
$self->session->http->setFilename($fileName,"application/octet-stream");
|
||||
$self->session->http->sendHeader;
|
||||
return $out;
|
||||
close $CSV;
|
||||
|
||||
$pb->update(sprintf q|<a href="%s">%s</a>|, $self->getUrl, sprintf($i18n->get('Return to %s'), $thingProperties->{label}));
|
||||
return $pb->finish($tempStorage->getUrl($csv_filename));
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -50,6 +50,58 @@ sub canPaste {
|
|||
return $self->validParent($self->session); ##Lazy call to a class method
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=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->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;
|
||||
$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 ( )
|
||||
|
|
@ -98,6 +150,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 {
|
||||
|
|
@ -134,6 +190,10 @@ sub duplicate {
|
|||
keywords => $keywords,
|
||||
} );
|
||||
|
||||
if (my $state = $options->{state}) {
|
||||
$newAsset->setState($state);
|
||||
}
|
||||
|
||||
return $newAsset;
|
||||
}
|
||||
|
||||
|
|
@ -224,7 +284,11 @@ sub paste {
|
|||
|
||||
# Update lineage in search index.
|
||||
$self->purgeCache;
|
||||
my $assetIter = $pastedAsset->getLineageIterator(['self', 'descendants']);
|
||||
my $assetIter = $pastedAsset->getLineageIterator(
|
||||
['self', 'descendants'], {
|
||||
statesToInclude => ['clipboard','clipboard-limbo']
|
||||
}
|
||||
);
|
||||
while ( 1 ) {
|
||||
my $asset;
|
||||
eval { $asset = $assetIter->() };
|
||||
|
|
@ -235,15 +299,64 @@ 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 pasteInFork ( )
|
||||
|
||||
WebGUI::Fork method called by www_pasteList
|
||||
|
||||
=cut
|
||||
|
||||
sub pasteInFork {
|
||||
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 = map {
|
||||
my $list
|
||||
= $_->getLineage( [ 'self', 'descendants' ], { statesToInclude => [ 'clipboard', 'clipboard-limbo' ] } );
|
||||
@$list;
|
||||
} @roots;
|
||||
|
||||
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;
|
||||
} ## end sub pasteInFork
|
||||
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 www_copy ( )
|
||||
|
|
@ -257,89 +370,49 @@ 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;
|
||||
$self->forkWithStatusPage({
|
||||
plugin => 'ProgressTree',
|
||||
title => 'Copy Assets',
|
||||
redirect => $redir,
|
||||
method => 'copyInFork',
|
||||
args => \%args
|
||||
}
|
||||
);
|
||||
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;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
|
@ -365,9 +438,8 @@ sub www_copyList {
|
|||
foreach my $assetId ($session->form->param("assetId")) {
|
||||
my $asset = WebGUI::Asset->newById($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 "") {
|
||||
|
|
@ -505,7 +577,7 @@ sub www_duplicateList {
|
|||
foreach my $assetId ($session->form->param("assetId")) {
|
||||
my $asset = WebGUI::Asset->newById($session,$assetId);
|
||||
if ($asset->canEdit) {
|
||||
my $newAsset = $asset->duplicate({skipAutoCommitWorkflows => 1, });
|
||||
my $newAsset = $asset->duplicate({skipAutoCommitWorkflows => 1});
|
||||
$newAsset->update({ title=>$newAsset->getTitle.' (copy)'});
|
||||
}
|
||||
}
|
||||
|
|
@ -658,26 +730,25 @@ the Asset Manager.
|
|||
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 );
|
||||
}
|
||||
return $session->privilege->insufficient() unless $self->canEdit && $session->form->validToken;
|
||||
|
||||
$self->forkWithStatusPage( {
|
||||
plugin => 'ProgressTree',
|
||||
title => 'Paste Assets',
|
||||
redirect => $self->getUrl(
|
||||
$form->get('proceed') eq 'manageAssets'
|
||||
? 'op=assetManager'
|
||||
: ()
|
||||
),
|
||||
method => 'pasteInFork',
|
||||
args => {
|
||||
assetId => $self->getId,
|
||||
list => [ $form->get('assetId') ],
|
||||
}
|
||||
}
|
||||
);
|
||||
} ## end sub www_pasteList
|
||||
|
||||
1;
|
||||
|
||||
|
|
|
|||
|
|
@ -21,8 +21,9 @@ use Scalar::Util qw(looks_like_number);
|
|||
use WebGUI::International;
|
||||
use WebGUI::Exception;
|
||||
use WebGUI::Session;
|
||||
use URI::URL ();
|
||||
use Scope::Guard;
|
||||
use URI::URL;
|
||||
use Scope::Guard qw(guard);
|
||||
use WebGUI::ProgressTree;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
|
|
@ -299,21 +300,53 @@ sub exportAsHtml {
|
|||
}
|
||||
|
||||
sub exportBranch {
|
||||
my $self = shift;
|
||||
my $options = shift;
|
||||
my $reportSession = shift;
|
||||
my ($self, $options, $reportSession) = @_;
|
||||
my $i18n = $reportSession &&
|
||||
WebGUI::International->new($self->session, 'Asset');
|
||||
|
||||
my $depth = $options->{depth};
|
||||
my $indexFileName = $options->{indexFileName};
|
||||
my $extrasUploadAction = $options->{extrasUploadAction};
|
||||
my $rootUrlAction = $options->{rootUrlAction};
|
||||
my $exportedCount = 0;
|
||||
my $report = $options->{report};
|
||||
|
||||
my $i18n;
|
||||
if ( $reportSession ) {
|
||||
$i18n = WebGUI::International->new($self->session, 'Asset');
|
||||
unless ($report) {
|
||||
if ($reportSession) {
|
||||
# We got a report session and no report coderef, so we'll print
|
||||
# messages out. NOTE: this is for backcompat, but I'm not sure we
|
||||
# even need it any more. I think the only thing using it was the
|
||||
# old iframe-based export status report. --frodwith
|
||||
my %reports = (
|
||||
'bad user privileges' => sub {
|
||||
my $asset = shift;
|
||||
my $url = $asset->getUrl;
|
||||
$i18n->get('bad user privileges') . "\n$url"
|
||||
},
|
||||
'not exportable' => sub {
|
||||
my $asset = shift;
|
||||
my $fullPath = $asset->exportGetUrlAsPath;
|
||||
"$fullPath skipped, not exportable<br />";
|
||||
},
|
||||
'exporting page' => sub {
|
||||
my $asset = shift;
|
||||
my $fullPath = $asset->exportGetUrlAsPath;
|
||||
sprintf $i18n->get('exporting page'), $fullPath;
|
||||
},
|
||||
'collateral notes' => sub { pop },
|
||||
'done' => sub { $i18n->get('done') },
|
||||
);
|
||||
$report = sub {
|
||||
my ($asset, $key, @args) = @_;
|
||||
my $code = $reports{$key};
|
||||
my $message = $asset->$code();
|
||||
$reportSession->output->print($message, @args);
|
||||
};
|
||||
}
|
||||
else {
|
||||
$report = sub {};
|
||||
}
|
||||
}
|
||||
|
||||
my $exportedCount = 0;
|
||||
|
||||
my $exportAsset = sub {
|
||||
my ( $assetId ) = @_;
|
||||
|
|
@ -330,26 +363,18 @@ sub exportBranch {
|
|||
|
||||
# skip this asset if we can't view it as this user.
|
||||
unless( $asset->canView ) {
|
||||
if( $reportSession ) {
|
||||
my $message = sprintf( $i18n->get('bad user privileges') . "\n") . $asset->getUrl;
|
||||
$reportSession->output->print($message);
|
||||
}
|
||||
$asset->$report('bad user privileges');
|
||||
next;
|
||||
}
|
||||
|
||||
# skip this asset if it's not exportable.
|
||||
unless ( $asset->exportCheckExportable ) {
|
||||
if ( $reportSession ) {
|
||||
$reportSession->output->print("$fullPath skipped, not exportable<br />");
|
||||
}
|
||||
$asset->$report('not exportable');
|
||||
next;
|
||||
}
|
||||
|
||||
# tell the user which asset we're exporting.
|
||||
if ( $reportSession ) {
|
||||
my $message = sprintf $i18n->get('exporting page'), $fullPath;
|
||||
$reportSession->output->print($message);
|
||||
}
|
||||
$asset->$report('exporting page');
|
||||
|
||||
# try to write the file
|
||||
eval { $asset->exportWriteFile };
|
||||
|
|
@ -359,9 +384,25 @@ sub exportBranch {
|
|||
|
||||
# next, tell the asset that we're exporting, so that it can export any
|
||||
# of its collateral or other extra data.
|
||||
eval { $asset->exportAssetCollateral($asset->exportGetUrlAsPath, $options, $reportSession) };
|
||||
if($@) {
|
||||
WebGUI::Error->throw(error => "failed to export asset collateral for URL " . $asset->getUrl . ": $@");
|
||||
{
|
||||
# For backcompat we want to capture anything that
|
||||
# exportAssetCollateral may have printed and report it to the
|
||||
# coderef. We should get rid of this as soon as we're ready to
|
||||
# break that api.
|
||||
my $cs = $self->session->duplicate();
|
||||
open my $handle, '>', \my $output;
|
||||
$cs->output->setHandle($handle);
|
||||
my $guard = guard {
|
||||
close $handle;
|
||||
$cs->var->end;
|
||||
$cs->close();
|
||||
$asset->$report('collateral notes', $output) if $output;
|
||||
};
|
||||
my $path = $asset->exportGetUrlAsPath;
|
||||
eval { $asset->exportAssetCollateral($path, $options, $cs) };
|
||||
if($@) {
|
||||
WebGUI::Error->throw(error => "failed to export asset collateral for URL " . $asset->getUrl . ": $@");
|
||||
}
|
||||
}
|
||||
|
||||
# we exported this one successfully, so count it
|
||||
|
|
@ -371,19 +412,12 @@ sub exportBranch {
|
|||
$self->session->db->write( "UPDATE asset SET lastExportedAs = ? WHERE assetId = ?",
|
||||
[ $fullPath, $asset->getId ] );
|
||||
|
||||
$self->updateHistory("exported");
|
||||
$asset->updateHistory('exported');
|
||||
|
||||
# tell the user we did this asset correctly
|
||||
if ( $reportSession ) {
|
||||
$reportSession->output->print($i18n->get('done'));
|
||||
}
|
||||
|
||||
#use Devel::Cycle;
|
||||
#warn "CHECKING on " . ref( $asset ) . ' ID: ' . $asset->getId . "\n";
|
||||
#find_cycle( $asset );
|
||||
$asset->$report('done');
|
||||
};
|
||||
|
||||
|
||||
my $assetIds = $self->exportGetDescendants(undef, $depth);
|
||||
foreach my $assetId ( @{$assetIds} ) {
|
||||
$exportAsset->( $assetId );
|
||||
|
|
@ -616,6 +650,44 @@ sub exportGetUrlAsPath {
|
|||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 exportInFork
|
||||
|
||||
Intended to be called by WebGUI::Fork. Runs exportAsHtml on the
|
||||
specified asset and keeps a json structure as the status.
|
||||
|
||||
=cut
|
||||
|
||||
sub exportInFork {
|
||||
my ( $process, $args ) = @_;
|
||||
my $session = $process->session;
|
||||
my $self = WebGUI::Asset->new( $session, delete $args->{assetId} );
|
||||
$args->{indexFileName} = delete $args->{index};
|
||||
my $assetIds = $self->exportGetDescendants( undef, $args->{depth} );
|
||||
my $tree = WebGUI::ProgressTree->new( $session, $assetIds );
|
||||
my %reports = (
|
||||
'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' );
|
||||
},
|
||||
'not exportable' => sub {
|
||||
$tree->failure( shift, 'Not Exportable' );
|
||||
},
|
||||
);
|
||||
$args->{report} = sub {
|
||||
my ( $asset, $key, @args ) = @_;
|
||||
my $code = $reports{$key};
|
||||
$code->( $asset->getId, @args );
|
||||
$process->update( sub { $tree->json } );
|
||||
};
|
||||
$self->exportAsHtml($args);
|
||||
$tree->focus(undef);
|
||||
$process->update( $tree->json );
|
||||
} ## end sub exportInFork
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 exportSymlinkExtrasUploads ( [ session ] )
|
||||
|
||||
Class or object method. Sets up the extras and uploads symlinks.
|
||||
|
|
@ -926,16 +998,25 @@ Displays the export status page
|
|||
=cut
|
||||
|
||||
sub www_exportStatus {
|
||||
my $self = shift;
|
||||
return $self->session->privilege->insufficient() unless ($self->session->user->isInGroup(13));
|
||||
my $i18n = WebGUI::International->new($self->session, "Asset");
|
||||
my $iframeUrl = $self->getUrl('func=exportGenerate');
|
||||
foreach my $formVar (qw/index depth userId extrasUploadsAction rootUrlAction exportUrl/) {
|
||||
$iframeUrl = $self->session->url->append($iframeUrl, $formVar . '=' . $self->session->form->process($formVar));
|
||||
}
|
||||
|
||||
my $output = '<iframe src="' . $iframeUrl . '" title="' . $i18n->get('Page Export Status') . '" width="100%" height="500"></iframe>';
|
||||
$self->getAdminConsole->render($output, $i18n->get('Page Export Status'), "Asset");
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
return $session->privilege->insufficient
|
||||
unless $session->user->isInGroup(13);
|
||||
my $form = $session->form;
|
||||
my @vars = qw(
|
||||
index depth userId extrasUploadsAction rootUrlAction exportUrl
|
||||
);
|
||||
$self->forkWithStatusPage({
|
||||
plugin => 'ProgressTree',
|
||||
title => 'Page Export Status',
|
||||
method => 'exportInFork',
|
||||
groupId => 13,
|
||||
args => {
|
||||
assetId => $self->getId,
|
||||
map { $_ => scalar $form->get($_) } @vars
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -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,13 @@ 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;
|
||||
while ( 1 ) {
|
||||
my $asset;
|
||||
eval { $asset = $assetIter->() };
|
||||
|
|
@ -263,6 +326,18 @@ sub trash {
|
|||
$outputSub->($i18n->get('Clearing cache'));
|
||||
$asset->purgeCache;
|
||||
$asset->updateHistory("trashed");
|
||||
if ($asset->getId eq $rootId) {
|
||||
$asset->setState('trash');
|
||||
# setState will take care of _properties in $asset, but not in
|
||||
# $self (whooops!), so we need to manually update.
|
||||
my @keys = qw(state stateChangedBy stateChanged);
|
||||
$self->state($asset->state);
|
||||
$self->stateChangedBy($asset->stateChangedBy);
|
||||
$self->stateChanged($asset->stateChanged);
|
||||
}
|
||||
else {
|
||||
$asset->setState('trash-limbo');
|
||||
}
|
||||
}
|
||||
|
||||
# Trash any shortcuts to this asset
|
||||
|
|
@ -287,6 +362,50 @@ 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 );
|
||||
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;
|
||||
|
|
@ -316,7 +435,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
|
||||
|
|
@ -331,8 +450,14 @@ sub www_delete {
|
|||
if ($self->getId eq $asset->getId) {
|
||||
$asset = $self->getParent;
|
||||
}
|
||||
$self->session->asset($asset);
|
||||
return $asset->www_view;
|
||||
$self->forkWithStatusPage({
|
||||
plugin => 'ProgressTree',
|
||||
title => 'Delete Assets',
|
||||
redirect => $asset->getUrl,
|
||||
method => 'trashInFork',
|
||||
args => [ $self->getId ],
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
|
@ -348,31 +473,20 @@ by the form variable C<proceeed>.
|
|||
=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;
|
||||
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') ],
|
||||
}
|
||||
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));
|
||||
}
|
||||
|
||||
);
|
||||
} ## end sub www_deleteList
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
|
|
@ -479,29 +593,18 @@ 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->forkWithStatusPage({
|
||||
plugin => 'ProgressTree',
|
||||
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));
|
||||
);
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -400,7 +400,8 @@ sub editUserForm {
|
|||
$f->password(
|
||||
name=>"authWebGUI.identifier",
|
||||
label=>$i18n->get(51),
|
||||
value=>"password"
|
||||
value=>"password",
|
||||
extras=>'autocomplete="off"',
|
||||
);
|
||||
$f->interval(
|
||||
-name=>"authWebGUI.passwordTimeout",
|
||||
|
|
@ -1131,7 +1132,7 @@ sub emailRecoverPasswordFinish {
|
|||
|
||||
my $mail = WebGUI::Mail::Send->create($session, { to=>$email, subject=>$i18n->get('WebGUI password recovery')});
|
||||
my $vars = { };
|
||||
$vars->{recoverPasswordUrl} = $session->url->append($session->url->getSiteURL,'?op=auth;method=emailResetPassword;token='.$recoveryGuid);
|
||||
$vars->{recoverPasswordUrl} = $session->url->append($session->url->getSiteURL,'op=auth;method=emailResetPassword;token='.$recoveryGuid);
|
||||
my $template = WebGUI::Asset->newByDynamicClass($session, $session->setting->get('webguiPasswordRecoveryEmailTemplate'));
|
||||
my $emailText = $template->process($vars);
|
||||
WebGUI::Macro::process($session, \$emailText);
|
||||
|
|
|
|||
668
lib/WebGUI/Fork.pm
Normal file
668
lib/WebGUI/Fork.pm
Normal file
|
|
@ -0,0 +1,668 @@
|
|||
package WebGUI::Fork;
|
||||
|
||||
use warnings;
|
||||
use strict;
|
||||
|
||||
use File::Spec;
|
||||
use JSON;
|
||||
use POSIX;
|
||||
use Config;
|
||||
use IO::Pipe;
|
||||
use WebGUI::Session;
|
||||
use WebGUI::Pluggable;
|
||||
use Time::HiRes qw(sleep);
|
||||
|
||||
=head1 NAME
|
||||
|
||||
WebGUI::Fork
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Safely and portably spawn a long running process that you can check the
|
||||
status of.
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
package WebGUI::Some::Class;
|
||||
|
||||
sub doWork {
|
||||
my ($process, $data) = @_;
|
||||
$process->update("Starting...");
|
||||
...
|
||||
$process->update("About half way done...");
|
||||
...
|
||||
$process->update("Finished!");
|
||||
}
|
||||
|
||||
sub www_doWork {
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
my $process = WebGUI::Fork->start(
|
||||
$session, 'WebGUI::Some::Class', 'doWork', { some => 'data' }
|
||||
);
|
||||
# See WebGUI::Operation::Fork
|
||||
my $pairs = $process->contentPairs('DoWork');
|
||||
$session->http->setRedirect($self->getUrl($pairs));
|
||||
return 'redirect';
|
||||
}
|
||||
|
||||
package WebGUI::Operation::Fork::DoWork;
|
||||
|
||||
sub handler {
|
||||
my $process = shift;
|
||||
my $session = $process->session;
|
||||
return $session->style->userStyle($process->status);
|
||||
|
||||
# or better yet, an ajaxy page that polls.
|
||||
}
|
||||
|
||||
|
||||
=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 canView ($user?)
|
||||
|
||||
Returns whether the current user (or the user passed in, if there is one) has
|
||||
permission to view the status of the fork. By default, only admins can view,
|
||||
but see setGroup.
|
||||
|
||||
=cut
|
||||
|
||||
sub canView {
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
my $user = shift || $session->user;
|
||||
$user = WebGUI::User->new( $session, $user )
|
||||
unless eval { $user->isa('WebGUI::User') };
|
||||
return
|
||||
$user->isAdmin
|
||||
|| $user->userId eq $self->getUserId
|
||||
|| $user->isInGroup( $self->getGroupId );
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=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).
|
||||
|
||||
$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, $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 inserts it into the db.
|
||||
|
||||
=cut
|
||||
|
||||
sub create {
|
||||
my ( $class, $session ) = @_;
|
||||
my $id = $session->id->generate;
|
||||
my %data = ( userId => $session->user->userId );
|
||||
$session->db->setRow( $class->tableName, 'id', \%data, $id );
|
||||
bless { session => $session, id => $id }, $class;
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 daemonize ( $stdin, $sub )
|
||||
|
||||
Internal lass method. Runs the given $sub in daemon, and prints $stdin to its
|
||||
stdin.
|
||||
|
||||
=cut
|
||||
|
||||
sub daemonize {
|
||||
my ( $class, $stdin, $sub ) = @_;
|
||||
my $pid = fork();
|
||||
die "Cannot fork: $!" unless defined $pid;
|
||||
if ($pid) {
|
||||
|
||||
# The child process will fork again and exit immediately, so we can
|
||||
# wait for it (and thus not have zombie processes).
|
||||
waitpid( $pid, 0 );
|
||||
return;
|
||||
}
|
||||
|
||||
eval {
|
||||
|
||||
# detach from controlling terminal, get us into a new process group
|
||||
die "Cannot become session leader: $!" if POSIX::setsid() < 0;
|
||||
|
||||
# Fork again so we never get a controlling terminal
|
||||
my $worker = IO::Pipe->new;
|
||||
my $pid = fork();
|
||||
die "Child cannot fork: $!" unless defined $pid;
|
||||
|
||||
# We don't want to call any destructors, as it would mess with the
|
||||
# parent's mysql connection, etc.
|
||||
if ($pid) {
|
||||
$worker->writer;
|
||||
$worker->printflush($stdin);
|
||||
POSIX::_exit(0);
|
||||
}
|
||||
|
||||
# We're now in the final target process. STDIN should be whatever the
|
||||
# parent printed to us, and all output should go to /dev/null.
|
||||
$worker->reader();
|
||||
open STDIN, '<&', $worker or die "Cannot dup stdin: $!";
|
||||
open STDOUT, '>', '/dev/null' or die "Cannot write /dev/null: $!";
|
||||
open STDERR, '>&', \*STDOUT or die "Cannot dup stdout: $!";
|
||||
|
||||
# Standard daemon-y things...
|
||||
$SIG{HUP} = 'IGNORE';
|
||||
chdir '/';
|
||||
umask 0;
|
||||
|
||||
# Forcibly close any non-std open file descriptors that remain
|
||||
my $max = POSIX::sysconf(&POSIX::_SC_OPEN_MAX) || 1024;
|
||||
POSIX::close($_) for ( $^F .. $max );
|
||||
|
||||
# Do whatever we're supposed to do
|
||||
&$sub();
|
||||
};
|
||||
|
||||
POSIX::_exit( $@ ? -1 : 0 );
|
||||
} ## end sub daemonize
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 delete ( )
|
||||
|
||||
Clean up the information for this process from the database.
|
||||
|
||||
=cut
|
||||
|
||||
sub delete {
|
||||
my $self = shift;
|
||||
$self->session->db->deleteRow( $self->tableName, 'id', $self->getId );
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 endTime ( )
|
||||
|
||||
Returns the epoch time indicating when the subroutine passed to run() finished
|
||||
executing, or undef if it hasn't finished. Note that even if the sub passed
|
||||
to run dies, an endTime will be recorded.
|
||||
|
||||
=cut
|
||||
|
||||
sub endTime { $_[0]->get('endTime') }
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 error ( $msg )
|
||||
|
||||
Call this to record an error status. You probably shouldn't, though -- just
|
||||
dying from your subroutine will cause this to be set.
|
||||
|
||||
=cut
|
||||
|
||||
sub error { $_[0]->set( { error => $_[1] } ) }
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 finish ( )
|
||||
|
||||
Mark the process as being finished. This is called for you when your
|
||||
subroutine is finished. If update() wasn't computed on the last call, it will
|
||||
be computed now.
|
||||
|
||||
=cut
|
||||
|
||||
sub finish {
|
||||
my $self = shift;
|
||||
my %props = ( finished => 1 );
|
||||
if ( my $calc = delete $self->{delay} ) {
|
||||
$props{status} = $calc->();
|
||||
$props{latch} = 0;
|
||||
}
|
||||
$props{endTime} = time();
|
||||
$self->set( \%props );
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 forkAndExec ($request)
|
||||
|
||||
Internal method. Forks and execs a new perl process to run $request. This is
|
||||
used as a fallback if the master daemon runner is not working.
|
||||
|
||||
=cut
|
||||
|
||||
sub forkAndExec {
|
||||
my ( $self, $request ) = @_;
|
||||
my $id = $self->getId;
|
||||
my $class = ref $self;
|
||||
my $json = JSON::encode_json($request);
|
||||
my @inc = map {"-I$_"} map { File::Spec->rel2abs($_) } grep { !ref } @INC;
|
||||
my @argv = (@inc, "-M$class", "-e$class->runCmd()" );
|
||||
$class->daemonize(
|
||||
$json,
|
||||
sub {
|
||||
exec ($Config{perlpath}, @argv) or die "Could not exec: $!";
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 get ( @keys )
|
||||
|
||||
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, 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
|
||||
|
||||
sub get {
|
||||
my ( $self, @keys ) = @_;
|
||||
my $db = $self->session->db;
|
||||
my $dbh = $db->dbh;
|
||||
my $tbl = $dbh->quote_identifier( $self->tableName );
|
||||
my $key
|
||||
= @keys
|
||||
? join( ',', map { $dbh->quote_identifier($_) } @keys )
|
||||
: '*';
|
||||
my $id = $dbh->quote( $self->getId );
|
||||
my @values = $db->quickArray("SELECT $key FROM $tbl WHERE id = $id");
|
||||
return ( @values > 1 ) ? @values : $values[0];
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 getError ( )
|
||||
|
||||
If the process died, this will be set to stringified $@.
|
||||
|
||||
=cut
|
||||
|
||||
sub getError { $_[0]->get('error') }
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 getGroupId
|
||||
|
||||
Returns the group ID (not the actual WebGUI::Group) of users who are allowed
|
||||
to view this process.
|
||||
|
||||
=cut
|
||||
|
||||
sub getGroupId {
|
||||
my $id = $_[0]->get('groupId');
|
||||
return $id || 3;
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 getId ( )
|
||||
|
||||
The unique id for this fork. Note: this is NOT the pid, but a WebGUI guid.
|
||||
|
||||
=cut
|
||||
|
||||
sub getId { shift->{id} }
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 getStatus()
|
||||
|
||||
Signals the fork that it should report its next status, then polls at a
|
||||
configurable, fractional interval (default: .1 seconds) waiting for the fork
|
||||
to claim that its status has been updated. Returns the updated status. See
|
||||
setWait() for a way to change the interval (or disable the waiting procedure
|
||||
entirely). We will only wait for a maximum of 100 intervals.
|
||||
|
||||
=cut
|
||||
|
||||
sub getStatus {
|
||||
my $self = shift;
|
||||
if ( my $interval = $self->{interval} ) {
|
||||
$self->set( { latch => 1 } );
|
||||
my $maxWait;
|
||||
while ($maxWait++ < 100) {
|
||||
sleep $interval;
|
||||
my ( $finished, $latch ) = $self->get( 'finished', 'latch' );
|
||||
last if $finished || !$latch;
|
||||
}
|
||||
}
|
||||
return $self->get('status');
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=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
|
||||
is for this to be called once at server startup time, after you've preloaded
|
||||
modules and before you start listening for requests. Returns a filehandle that
|
||||
can be used to print requests to the master process, and which you almost
|
||||
certainly shouldn't use (it's mostly for testing).
|
||||
|
||||
=cut
|
||||
|
||||
my $pipe;
|
||||
|
||||
sub init {
|
||||
my $class = shift;
|
||||
$pipe = IO::Pipe->new;
|
||||
|
||||
my $pid = fork();
|
||||
die "Cannot fork: $!" unless defined $pid;
|
||||
|
||||
if ($pid) {
|
||||
$pipe->writer;
|
||||
return $pipe;
|
||||
}
|
||||
|
||||
$0 = 'webgui-fork-master';
|
||||
$pipe->reader;
|
||||
local $/ = "\x{0}";
|
||||
while ( my $request = $pipe->getline ) {
|
||||
chomp $request;
|
||||
eval {
|
||||
$class->daemonize( $request, sub { $class->runCmd } );
|
||||
};
|
||||
}
|
||||
exit 0;
|
||||
} ## end sub init
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 isFinished ( )
|
||||
|
||||
A simple flag indicating that the fork is no longer running.
|
||||
|
||||
=cut
|
||||
|
||||
sub isFinished { $_[0]->get('finished') }
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 new ( $session, $id )
|
||||
|
||||
Returns an object capable of checking on the status of the fork indicated by
|
||||
$id. Returns undef if there is no such process.
|
||||
|
||||
=cut
|
||||
|
||||
sub new {
|
||||
my ( $class, $session, $id ) = @_;
|
||||
my $db = $session->db;
|
||||
my $tbl = $db->dbh->quote_identifier( $class->tableName );
|
||||
my $sql = "SELECT COUNT(*) FROM $tbl WHERE id = ?";
|
||||
my $exists = $db->quickScalar( $sql, [$id] );
|
||||
return $exists
|
||||
? bless( { session => $session, id => $id, interval => .1 }, $class )
|
||||
: undef;
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 session ()
|
||||
|
||||
Get the WebGUI::Session this process was created with. Note: this is safe to
|
||||
call in the child process, as it is a duplicated session (same session id) and
|
||||
doesn't share any handles with the parent process.
|
||||
|
||||
=cut
|
||||
|
||||
sub session { $_[0]->{session} }
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 set ($properties)
|
||||
|
||||
Updates the database row with the properties given by the $properties hashref.
|
||||
See get() for a list of valid keys.
|
||||
|
||||
=cut
|
||||
|
||||
sub set {
|
||||
my ( $self, $values ) = @_;
|
||||
my %row = ( id => $self->getId, %$values );
|
||||
$self->session->db->setRow( $self->tableName, 'id', \%row );
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 setGroup($groupId)
|
||||
|
||||
Allow the given group (in addition to admins) the ability to check on the
|
||||
status of this process
|
||||
|
||||
=cut
|
||||
|
||||
sub setGroup {
|
||||
my ( $self, $groupId ) = @_;
|
||||
$groupId = eval { $groupId->getId } || $groupId;
|
||||
$self->set( { groupId => $groupId } );
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 request ($module, $subname, $data)
|
||||
|
||||
Internal method. Generates a hashref suitable for passing to runRequest.
|
||||
|
||||
=cut
|
||||
|
||||
sub request {
|
||||
my ( $self, $module, $subname, $data ) = @_;
|
||||
my $session = $self->session;
|
||||
my $config = $session->config;
|
||||
return {
|
||||
webguiRoot => $config->getWebguiRoot,
|
||||
configFile => $config->getFilename,
|
||||
sessionId => $session->getId,
|
||||
module => $module,
|
||||
subname => $subname,
|
||||
id => $self->getId,
|
||||
data => $data,
|
||||
};
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 runCmd ()
|
||||
|
||||
Internal class method. Decodes json off of stdin and passes it to runRequest.
|
||||
|
||||
=cut
|
||||
|
||||
sub runCmd {
|
||||
my $class = shift;
|
||||
my $slurp = do { local $/; <STDIN> };
|
||||
$class->runRequest( JSON::decode_json($slurp) );
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 runRequest ($hashref)
|
||||
|
||||
Internal class method. Expects a hash of arguments describing what to run.
|
||||
|
||||
=cut
|
||||
|
||||
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 $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 ] );
|
||||
};
|
||||
$self->error($@) if $@;
|
||||
$self->finish();
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 sendRequestToMaster ($request)
|
||||
|
||||
Internal method. Attempts to send a request to the master daemon runner.
|
||||
Returns 1 on success and 0 on failure.
|
||||
|
||||
=cut
|
||||
|
||||
sub sendRequestToMaster {
|
||||
my ( $self, $request ) = @_;
|
||||
my $json = JSON::encode_json($request);
|
||||
eval {
|
||||
die 'pipe' unless $pipe && $pipe->isa('IO::Handle');
|
||||
local $SIG{PIPE} = sub { die 'pipe' };
|
||||
$pipe->printflush("$json\x{0}") or die 'pipe';
|
||||
};
|
||||
return 1 unless $@;
|
||||
undef $pipe;
|
||||
$self->session->log->error('Problems talking to master daemon process. Please restart the web server.');
|
||||
return 0;
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 setWait ( $interval )
|
||||
|
||||
Use this to control the pace at which getStatus will poll for updated
|
||||
statuses. By default, this is a tenth of a second. If you set it to 0,
|
||||
getStatus will still signal the fork for an update, but will take whatever is
|
||||
currently recorded as the status and return immediately.
|
||||
|
||||
=cut
|
||||
|
||||
sub setWait { $_[0]->{interval} = $_[1] }
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 start ( $session, $module, $subname, $data )
|
||||
|
||||
Class method. Executes $module::subname in a forked process with ($process,
|
||||
$data) as its arguments. The only restriction on $data is that it be
|
||||
serializable by JSON.
|
||||
|
||||
=head3 $0
|
||||
|
||||
The process name (as it appears in ps) will be set to webgui-fork-$id,
|
||||
where $id is the value returned by $process->getId. It thus won't look like a
|
||||
modperl process to anyone monitoring the process table (wremonitor.pl, for
|
||||
example).
|
||||
|
||||
=cut
|
||||
|
||||
sub start {
|
||||
my ( $class, $session, $module, $subname, $data ) = @_;
|
||||
my $self = $class->create($session);
|
||||
my $request = $self->request( $module, $subname, $data );
|
||||
$self->sendRequestToMaster($request) or $self->forkAndExec($request);
|
||||
return $self;
|
||||
}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 startTime ( )
|
||||
|
||||
Returns the time this process started running in epoch format.
|
||||
|
||||
=cut
|
||||
|
||||
sub startTime { $_[0]->get('startTime') }
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 tableName ( )
|
||||
|
||||
Class method: a constant, for convenience. The name of the table that process
|
||||
data is stored in.
|
||||
|
||||
=cut
|
||||
|
||||
sub tableName {'Fork'}
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
|
||||
=head2 update ( $msg )
|
||||
|
||||
Set a new status for the fork. This can be anything, and will overwrite the
|
||||
old status. JSON is recommended for complex statuses. Optionally, $msg can
|
||||
be a subroutine that returns the new status -- if your status may take a long
|
||||
time to compute, you should use this, as you may be able to avoid computing
|
||||
some (or all) of your status updates, depending on how often they're being
|
||||
asked for. See the getStatus method for details.
|
||||
|
||||
=cut
|
||||
|
||||
sub update {
|
||||
my ( $self, $msg ) = @_;
|
||||
if ( ref $msg eq 'CODE' ) {
|
||||
if ( $self->get('latch') ) {
|
||||
$msg = $msg->();
|
||||
}
|
||||
else {
|
||||
$self->{delay} = $msg;
|
||||
return;
|
||||
}
|
||||
}
|
||||
delete $self->{delay};
|
||||
$self->set( { latch => 0, status => $msg } );
|
||||
}
|
||||
|
||||
1;
|
||||
140
lib/WebGUI/Fork/ProgressBar.pm
Normal file
140
lib/WebGUI/Fork/ProgressBar.pm
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
package WebGUI::Fork::ProgressBar;
|
||||
|
||||
=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
|
||||
-------------------------------------------------------------------
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
WebGUI::Fork::ProgressBar
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Renders an admin console page that polls ::Status to draw a simple progress
|
||||
bar along with some kind of message.
|
||||
|
||||
=head1 SUBROUTINES
|
||||
|
||||
These subroutines are available from this package:
|
||||
|
||||
=cut
|
||||
|
||||
use Template;
|
||||
use HTML::Entities;
|
||||
use JSON;
|
||||
|
||||
my $template = <<'TEMPLATE';
|
||||
<div id='loading'>[% i18n('WebGUI', 'Loading...') %]</div>
|
||||
<div id='ui' style='display: none'>
|
||||
<p id='message'></p>
|
||||
<div id='meter'></div>
|
||||
<p>
|
||||
[% i18n('Fork_ProgressBar', 'time elapsed') %]:
|
||||
<span id='elapsed'></span> [% i18n('Fork_ProgressBar', 'seconds') %].
|
||||
</p>
|
||||
</div>
|
||||
<script>
|
||||
(function (params) {
|
||||
var bar = new YAHOO.WebGUI.Fork.ProgressBar();
|
||||
YAHOO.util.Event.onDOMReady(function () {
|
||||
bar.render('meter');
|
||||
YAHOO.WebGUI.Fork.poll({
|
||||
url : params.statusUrl,
|
||||
draw : function (data) {
|
||||
var status = YAHOO.lang.JSON.parse(data.status);
|
||||
bar.update(status.finished, status.total);
|
||||
document.getElementById('message').innerHTML = status.message;
|
||||
document.getElementById('elapsed').innerHTML = data.elapsed;
|
||||
},
|
||||
first : function () {
|
||||
document.getElementById('loading').style.display = 'none';
|
||||
document.getElementById('ui').style.display = 'block';
|
||||
},
|
||||
finish : function() {
|
||||
YAHOO.WebGUI.Fork.redirect(params.redirect);
|
||||
},
|
||||
error : function (msg) {
|
||||
alert(msg);
|
||||
}
|
||||
});
|
||||
});
|
||||
}([% params %]));
|
||||
</script>
|
||||
TEMPLATE
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 handler ( process )
|
||||
|
||||
See WebGUI::Operation::Fork.
|
||||
|
||||
=cut
|
||||
|
||||
sub handler { renderBar( shift, $template ) }
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 renderBar ( process, template )
|
||||
|
||||
Renders $template, passing a "params" variable to it that is JSON of a
|
||||
statusUrl to poll and a page to redirect to and an i18n function. Includes
|
||||
WebGUI.Fork.redirect, poll, and ProgressBar js and CSS (as well as all their
|
||||
YUI dependancies), and puts the whole template inside an adminConsole rendered
|
||||
based off some form parameters.
|
||||
|
||||
=cut
|
||||
|
||||
sub renderBar {
|
||||
my ( $process, $template ) = @_;
|
||||
my $session = $process->session;
|
||||
my $url = $session->url;
|
||||
my $form = $session->form;
|
||||
my $style = $session->style;
|
||||
my $tt = Template->new;
|
||||
my %vars = (
|
||||
i18n => sub {
|
||||
my ($namespace, $key) = @_;
|
||||
return WebGUI::International->new($session, $namespace)->get($key);
|
||||
},
|
||||
params => JSON::encode_json( {
|
||||
statusUrl => $url->page( $process->contentPairs('Status') ),
|
||||
redirect => scalar $form->get('proceed'),
|
||||
}
|
||||
),
|
||||
);
|
||||
$tt->process( \$template, \%vars, \my $content ) or die $tt->error;
|
||||
|
||||
my $console = WebGUI::AdminConsole->new( $session, $form->get('icon') );
|
||||
$style->setLink( $url->extras("Fork/ProgressBar.css"), { rel => 'stylesheet' } );
|
||||
$style->setScript( $url->extras("$_.js") )
|
||||
for ( (
|
||||
map {"yui/build/$_"}
|
||||
qw(
|
||||
yahoo/yahoo-min
|
||||
dom/dom-min
|
||||
json/json-min
|
||||
event/event-min
|
||||
connection/connection_core-min
|
||||
)
|
||||
),
|
||||
'Fork/ProgressBar',
|
||||
'Fork/poll',
|
||||
'Fork/redirect'
|
||||
);
|
||||
return $console->render( $content, encode_entities( $form->get('title') ) );
|
||||
} ## end sub renderBar
|
||||
|
||||
1;
|
||||
155
lib/WebGUI/Fork/ProgressTree.pm
Normal file
155
lib/WebGUI/Fork/ProgressTree.pm
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
package WebGUI::Fork::ProgressTree;
|
||||
|
||||
=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
|
||||
-------------------------------------------------------------------
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
WebGUI::Fork::ProgressTree
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Renders an admin console page that polls ::Status to draw a friendly graphical
|
||||
representation of how progress on a tree of assets is coming along.
|
||||
|
||||
=head1 SUBROUTINES
|
||||
|
||||
These subroutines are available from this package:
|
||||
|
||||
=cut
|
||||
|
||||
use Template;
|
||||
use HTML::Entities;
|
||||
use JSON;
|
||||
use WebGUI::Fork::ProgressBar;
|
||||
|
||||
my $template = <<'TEMPLATE';
|
||||
<div id='loading'>[% i18n('WebGUI', 'Loading...') %]</div>
|
||||
<div id='ui' style='display: none'>
|
||||
<div id='meter'></div>
|
||||
[% i18n('Fork_ProgressBar', 'current asset') %]: <span id='focus'></span>
|
||||
(<span id='finished'></span>/<span id='total'></span>).<br />
|
||||
[% i18n('Fork_ProgressBar', 'time elapsed') %]:
|
||||
<span id='elapsed'></span>
|
||||
[% i18n('Fork_ProgressBar', 'seconds') %].
|
||||
<ul id='tree'></ul>
|
||||
</div>
|
||||
<script>
|
||||
(function (params) {
|
||||
var bar = new YAHOO.WebGUI.Fork.ProgressBar();
|
||||
|
||||
function setHtml(id, html) {
|
||||
document.getElementById(id).innerHTML = html;
|
||||
}
|
||||
function draw(data) {
|
||||
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.success) {
|
||||
li.className = 'success';
|
||||
finished += 1;
|
||||
}
|
||||
else if (asset.failure) {
|
||||
li.className = 'failure';
|
||||
txt += ' (' + asset.failure + ')';
|
||||
finished += 1;
|
||||
}
|
||||
if (asset.focus) {
|
||||
li.className += 'focus';
|
||||
focus = asset.url;
|
||||
}
|
||||
li.appendChild(document.createTextNode(txt));
|
||||
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');
|
||||
_.each(asset.children, function (child) {
|
||||
recurse(child, ul);
|
||||
});
|
||||
li.appendChild(ul);
|
||||
}
|
||||
node.appendChild(li);
|
||||
}
|
||||
tree = document.getElementById('tree');
|
||||
tree.innerHTML = '';
|
||||
_.each(JSON.parse(data.status), function (root) {
|
||||
recurse(root, tree);
|
||||
});
|
||||
bar.update(finished, total);
|
||||
setHtml('total', total);
|
||||
setHtml('finished', finished);
|
||||
setHtml('focus', focus || 'nothing');
|
||||
setHtml('elapsed', data.elapsed);
|
||||
}
|
||||
YAHOO.util.Event.onDOMReady(function () {
|
||||
bar.render('meter');
|
||||
YAHOO.WebGUI.Fork.poll({
|
||||
url : params.statusUrl,
|
||||
draw : draw,
|
||||
first : function () {
|
||||
document.getElementById('loading').style.display = 'none';
|
||||
document.getElementById('ui').style.display = 'block';
|
||||
},
|
||||
finish : function () {
|
||||
YAHOO.WebGUI.Fork.redirect(params.redirect);
|
||||
},
|
||||
error : function (msg) {
|
||||
alert(msg)
|
||||
}
|
||||
});
|
||||
});
|
||||
}([% params %]));
|
||||
</script>
|
||||
TEMPLATE
|
||||
|
||||
my $stylesheet = <<'STYLESHEET';
|
||||
<style>
|
||||
#tree li { color: black }
|
||||
#tree li.focus { color: cyan }
|
||||
#tree li.failure { color: red }
|
||||
#tree li.success { color: green }
|
||||
</style>
|
||||
STYLESHEET
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 handler ( process )
|
||||
|
||||
See WebGUI::Operation::Fork.
|
||||
|
||||
=cut
|
||||
|
||||
sub handler {
|
||||
my $process = shift;
|
||||
my $session = $process->session;
|
||||
my $style = $session->style;
|
||||
my $url = $session->url;
|
||||
$style->setRawHeadTags($stylesheet);
|
||||
$style->setScript($url->extras('underscore/underscore-min.js'));
|
||||
WebGUI::Fork::ProgressBar::renderBar($process, $template);
|
||||
}
|
||||
|
||||
1;
|
||||
84
lib/WebGUI/Fork/Status.pm
Normal file
84
lib/WebGUI/Fork/Status.pm
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
package WebGUI::Fork::Status;
|
||||
|
||||
use JSON;
|
||||
|
||||
=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
|
||||
-------------------------------------------------------------------
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
WebGUI::Fork::Status
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Returns a json response of the following form:
|
||||
|
||||
{
|
||||
"finished" : true,
|
||||
"elapsed" : 10,
|
||||
"status" : "whatever is in the status field. Could be anything.",
|
||||
"error" : "whatever is in the error field"
|
||||
}
|
||||
|
||||
Note that if your status is JSON, you'll have to decode that seperately, so
|
||||
something like:
|
||||
|
||||
decoded = JSON.parse(r.responseText);
|
||||
status = JSON.parse(decoded.status);
|
||||
|
||||
Finished is obviously true or false. Notably, it will be true in the error
|
||||
case: so to status.finished && !status.error means successful completion.
|
||||
Error will only be present if the process died for some reason.
|
||||
|
||||
Status will always be present, mostly so you can see what the last status was
|
||||
before it died.
|
||||
|
||||
Elapsed will be the number of seconds since the process started (or until the
|
||||
process finished, if it is finished).
|
||||
|
||||
=head1 SUBROUTINES
|
||||
|
||||
These subroutines are available from this package:
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 handler ( process )
|
||||
|
||||
See the synopsis for what kind of response this generates.
|
||||
|
||||
=cut
|
||||
|
||||
sub handler {
|
||||
my $process = shift;
|
||||
my $status = $process->getStatus;
|
||||
my ( $finished, $startTime, $endTime, $error ) = $process->get( 'finished', 'startTime', 'endTime', 'error' );
|
||||
|
||||
$endTime = time() unless $finished;
|
||||
|
||||
my %status = (
|
||||
status => $status,
|
||||
elapsed => ( $endTime - $startTime ),
|
||||
finished => ( $finished ? \1 : \0 ),
|
||||
);
|
||||
$status{error} = $error if $finished;
|
||||
$process->session->http->setMimeType('text/plain');
|
||||
JSON::encode_json( \%status );
|
||||
} ## end sub handler
|
||||
|
||||
1;
|
||||
|
|
@ -302,25 +302,25 @@ sub toHtml {
|
|||
value => 'upload',
|
||||
id => $self->get('id')
|
||||
})->toHtml
|
||||
. "<br />";
|
||||
. "\n";
|
||||
}
|
||||
else {
|
||||
$uploadControl .= WebGUI::Form::Hidden->new($self->session, {
|
||||
name => $self->get("name"),
|
||||
value => $self->getOriginalValue,
|
||||
id => $self->get("id")
|
||||
})->toHtml()."<br />";
|
||||
})->toHtml()."\n";
|
||||
$uploadControl .= WebGUI::Form::Hidden->new($self->session, {
|
||||
name => $self->privateName('action'),
|
||||
value => 'keep',
|
||||
id => $self->get("id")
|
||||
})->toHtml()."<br />";
|
||||
})->toHtml()."\n";
|
||||
}
|
||||
if (scalar(@files)) {
|
||||
if ($self->get('maxAttachments') == 1) {
|
||||
$self->set("");
|
||||
}
|
||||
$uploadControl .= $self->getFilePreview($storage);
|
||||
$uploadControl .= "<br />".$self->getFilePreview($storage);
|
||||
}
|
||||
return $uploadControl;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -187,7 +187,7 @@ Renders an HTML area field.
|
|||
sub toHtml {
|
||||
my $self = shift;
|
||||
##Do not display a rich editor on any mobile browser.
|
||||
if ($self->session->style->useMobileStyle) {
|
||||
if ($self->session->style->mobileBrowser) {
|
||||
return $self->SUPER::toHtml;
|
||||
}
|
||||
my $i18n = WebGUI::International->new($self->session);
|
||||
|
|
|
|||
|
|
@ -113,7 +113,6 @@ sub getValue {
|
|||
my ( $self, $value ) = @_;
|
||||
$value ||= $self->SUPER::getValue;
|
||||
|
||||
$self->session->log->info( "JsonTable Got $value from form" );
|
||||
$value = JSON->new->decode( $value );
|
||||
|
||||
for my $row ( @{$value} ) {
|
||||
|
|
|
|||
|
|
@ -409,20 +409,18 @@ sub processReplacements {
|
|||
my $session = shift;
|
||||
my ($content) = @_;
|
||||
my $replacements = $session->stow->get("replacements");
|
||||
if (defined $replacements) {
|
||||
foreach my $searchFor (keys %{$replacements}) {
|
||||
my $replaceWith = $replacements->{$searchFor};
|
||||
$content =~ s/\Q$searchFor/$replaceWith/gs;
|
||||
}
|
||||
} else {
|
||||
if (! defined $replacements) {
|
||||
my $sth = $session->dbSlave->read("select searchFor,replaceWith from replacements");
|
||||
while (my ($searchFor,$replaceWith) = $sth->array) {
|
||||
while (my ($searchFor,$replaceWith) = $sth->array) {
|
||||
$replacements->{$searchFor} = $replaceWith;
|
||||
$content =~ s/\Q$searchFor/$replaceWith/gs;
|
||||
}
|
||||
$sth->finish;
|
||||
$session->stow->set("replacements",$replacements);
|
||||
}
|
||||
$sth->finish;
|
||||
$session->stow->set("replacements",$replacements);
|
||||
}
|
||||
foreach my $searchFor (keys %{$replacements}) {
|
||||
my $replaceWith = $replacements->{$searchFor};
|
||||
$content =~ s/\b\Q$searchFor\E\b/$replaceWith/gs;
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ Returns a hash reference containing operation and package names.
|
|||
|
||||
sub getOperations {
|
||||
return {
|
||||
'fork' => 'Fork',
|
||||
'killSession' => 'ActiveSessions',
|
||||
'viewActiveSessions' => 'ActiveSessions',
|
||||
|
||||
|
|
|
|||
74
lib/WebGUI/Operation/Fork.pm
Normal file
74
lib/WebGUI/Operation/Fork.pm
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
package WebGUI::Operation::Fork;
|
||||
|
||||
=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
|
||||
-------------------------------------------------------------------
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use WebGUI::Fork;
|
||||
use WebGUI::Pluggable;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
WebGUI::Operation::Fork
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
URL dispatching for WebGUI::Fork monitoring
|
||||
|
||||
=head1 SUBROUTINES
|
||||
|
||||
These subroutines are available from this package:
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 www_fork ( session )
|
||||
|
||||
Dispatches to the proper module based on the module form parameter if op is
|
||||
fork. Returns insufficient privilege page if the user doesn't pass canView on
|
||||
the process before dispatching.
|
||||
|
||||
=cut
|
||||
|
||||
sub www_fork {
|
||||
my $session = shift;
|
||||
my $form = $session->form;
|
||||
my $module = $form->get('module') || 'Status';
|
||||
my $pid = $form->get('pid') || return undef;
|
||||
|
||||
my $process = WebGUI::Fork->new( $session, $pid );
|
||||
|
||||
return $session->privilege->insufficient unless $process->canView;
|
||||
|
||||
my $log = $session->log;
|
||||
|
||||
unless ($process) {
|
||||
$log->error("Tried to get info for nonexistent process $pid");
|
||||
return undef;
|
||||
}
|
||||
|
||||
my $output = eval { WebGUI::Pluggable::run( "WebGUI::Fork::$module", 'handler', [$process] ); };
|
||||
|
||||
if ($@) {
|
||||
$log->error($@);
|
||||
return undef;
|
||||
}
|
||||
|
||||
return $output;
|
||||
} ## end sub www_fork
|
||||
|
||||
1;
|
||||
|
|
@ -645,6 +645,7 @@ sub www_editUser {
|
|||
-name=>"username",
|
||||
-label=>$i18n->get(50),
|
||||
-value=>$username
|
||||
-extras=>'autocomplete="off"',
|
||||
);
|
||||
my %status;
|
||||
tie %status, 'Tie::IxHash';
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ use WebGUI::VersionTag;
|
|||
use WebGUI::HTMLForm;
|
||||
use WebGUI::Paginator;
|
||||
use Tie::IxHash;
|
||||
use WebGUI::Fork;
|
||||
use Monkey::Patch;
|
||||
use JSON;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
|
|
@ -138,6 +141,50 @@ sub getVersionTagOptions {
|
|||
return %tag;
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
=head2 rollbackInFork ($process, $tagId)
|
||||
|
||||
WebGUI::Fork method called by www_rollbackVersionTag
|
||||
|
||||
=cut
|
||||
|
||||
sub rollbackInFork {
|
||||
my ( $process, $tagId ) = @_;
|
||||
my $session = $process->session;
|
||||
my $tag = WebGUI::VersionTag->new( $session, $tagId );
|
||||
my %status = (
|
||||
finished => 0,
|
||||
total => $process->session->db->quickScalar( 'SELECT count(*) FROM assetData WHERE tagId = ?', [$tagId] ),
|
||||
message => '',
|
||||
);
|
||||
my $update = sub {
|
||||
$process->update( sub { JSON::encode_json( \%status ) } );
|
||||
};
|
||||
my $patch = Monkey::Patch::patch_class(
|
||||
'WebGUI::Asset',
|
||||
'purgeRevision',
|
||||
sub {
|
||||
my $purgeRevision = shift;
|
||||
my $self = shift;
|
||||
$self->$purgeRevision(@_);
|
||||
$status{finished}++;
|
||||
$update->();
|
||||
}
|
||||
);
|
||||
$tag->rollback( {
|
||||
outputSub => sub {
|
||||
$status{message} = shift;
|
||||
$update->();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
# need to get at least one of these in for the degenerate case of no
|
||||
# revisions in tag
|
||||
$update->();
|
||||
} ## end sub rollbackInFork
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 www_approveVersionTag ( session )
|
||||
|
|
@ -656,8 +703,9 @@ sub www_manageRevisionsInTag {
|
|||
|
||||
# Process any actions
|
||||
my $action = lc $session->form->get('action');
|
||||
my $form = $session->form;
|
||||
my $validToken = $session->form->validToken;
|
||||
if ( $action eq "purge" && $validToken) {
|
||||
if ( $form->get('purge') && $validToken) {
|
||||
# Purge these revisions
|
||||
my @assetInfo = $session->form->get('assetInfo');
|
||||
for my $assetInfo ( @assetInfo ) {
|
||||
|
|
@ -672,7 +720,7 @@ sub www_manageRevisionsInTag {
|
|||
return www_manageVersions( $session );
|
||||
}
|
||||
}
|
||||
elsif ( $action eq "move to:" && $validToken) {
|
||||
elsif ( $form->get('moveto') && $validToken) {
|
||||
# Get the new version tag
|
||||
my $moveToTagId = $session->form->get('moveToTagId');
|
||||
my $moveToTag;
|
||||
|
|
@ -700,7 +748,7 @@ sub www_manageRevisionsInTag {
|
|||
return www_manageVersions( $session );
|
||||
}
|
||||
}
|
||||
elsif ( $action eq "update version tag" && $validToken) {
|
||||
elsif ( $form->get('update') && $validToken) {
|
||||
my $startTime = WebGUI::DateTime->new($session,$session->form->process("startTime","dateTime"))->toDatabase;
|
||||
my $endTime = WebGUI::DateTime->new($session,$session->form->process("endTime","dateTime"))->toDatabase;
|
||||
|
||||
|
|
@ -786,19 +834,19 @@ sub www_manageRevisionsInTag {
|
|||
value => WebGUI::DateTime->new($session,$filterEndTime)->epoch,
|
||||
})
|
||||
. '<br />'
|
||||
. '<input type="submit" name="action" value="'. $i18n->get('manageRevisionsInTag update') . '" />'
|
||||
. '<input type="submit" name="update" value="'. $i18n->get('manageRevisionsInTag update') . '" />'
|
||||
. '</td>'
|
||||
. '</tr>'
|
||||
. '<tr><td colspan="5"> </td></tr>'
|
||||
. '<tr>'
|
||||
. '<td colspan="5">'
|
||||
. $i18n->get("manageRevisionsInTag with selected")
|
||||
. '<input type="submit" name="action" value="'. $i18n->get("manageRevisionsInTag move") . '" />'
|
||||
. '<input type="submit" name="moveto" value="'. $i18n->get("manageRevisionsInTag move") . '" />'
|
||||
. WebGUI::Form::SelectBox( $session, {
|
||||
name => 'moveToTagId',
|
||||
options => \%moveToTagOptions,
|
||||
} )
|
||||
. ' <input type="submit" name="action" value="'. $i18n->get('manageRevisionsInTag purge') . '" class="red" />'
|
||||
. ' <input type="submit" name="purge" value="'. $i18n->get('manageRevisionsInTag purge') . '" class="red" />'
|
||||
. '</td>'
|
||||
. '</tr>'
|
||||
. '<tr>'
|
||||
|
|
@ -854,16 +902,27 @@ sub www_rollbackVersionTag {
|
|||
return $session->privilege->adminOnly() unless canView($session);
|
||||
my $tagId = $session->form->process("tagId");
|
||||
return $session->privilege->vitalComponent() if ($tagId eq "pbversion0000000000001");
|
||||
my $pb = WebGUI::ProgressBar->new($session);
|
||||
my $i18n = WebGUI::International->new($session, 'VersionTag');
|
||||
$pb->start($i18n->get('rollback version tag'), $session->url->extras('adminConsole/versionTags.gif'));
|
||||
if ($tagId) {
|
||||
my $tag = WebGUI::VersionTag->new($session, $tagId);
|
||||
$tag->rollback({ outputSub => sub { $pb->update(@_) }, }) if defined $tag;
|
||||
}
|
||||
|
||||
my $process = WebGUI::Fork->start(
|
||||
$session, 'WebGUI::Operation::VersionTag', 'rollbackInFork', $tagId
|
||||
);
|
||||
|
||||
my $i18n = WebGUI::International->new($session, 'VersionTag');
|
||||
my $method = $session->form->process("proceed");
|
||||
$method = $method eq "manageCommittedVersions" ? $method : 'manageVersions';
|
||||
$pb->finish(WebGUI::Asset->getDefault($session)->getUrl('op='.$method));
|
||||
my $redir = WebGUI::Asset->getDefault($session)->getUrl("op=$method");
|
||||
$session->http->setRedirect(
|
||||
$session->url->page(
|
||||
$process->contentPairs(
|
||||
'ProgressBar', {
|
||||
icon => 'versions',
|
||||
title => $i18n->get('rollback version tag'),
|
||||
proceed => $redir,
|
||||
}
|
||||
)
|
||||
)
|
||||
);
|
||||
return 'redirect';
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
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;
|
||||
|
|
@ -648,7 +648,7 @@ sub quickCSV {
|
|||
my $sql = shift;
|
||||
my $params = shift;
|
||||
|
||||
my $csv = Text::CSV_XS->new({ eol => "\n" });
|
||||
my $csv = Text::CSV_XS->new({ eol => "\n", binary => 1 });
|
||||
|
||||
my $sth = $self->prepare($sql);
|
||||
$sth->execute(@$params);
|
||||
|
|
@ -656,9 +656,12 @@ sub quickCSV {
|
|||
return undef unless $csv->combine($sth->getColumnNames);
|
||||
my $output = $csv->string;
|
||||
|
||||
while (my @data = $sth->fetchrow_array) {
|
||||
return undef unless $csv->combine(@data);
|
||||
$output .= $csv->string;
|
||||
while (my @data = $sth->array) {
|
||||
if ( ! $csv->combine(@data) ) {
|
||||
$self->session->log->error( "Problem creating CSV row: " . $csv->error_diag );
|
||||
return undef;
|
||||
}
|
||||
$output .= $csv->string();
|
||||
}
|
||||
|
||||
$sth->finish;
|
||||
|
|
|
|||
|
|
@ -110,6 +110,25 @@ sub makePrintable {
|
|||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 mobileBrowser ( )
|
||||
|
||||
Returns true if the user's browser matches any of the mobile browsers set in the config file.
|
||||
|
||||
=cut
|
||||
|
||||
sub mobileBrowser {
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
my $ua = $session->env->get('HTTP_USER_AGENT');
|
||||
for my $mobileUA (@{ $session->config->get('mobileUserAgents') }) {
|
||||
if ($ua =~ m/$mobileUA/) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 useMobileStyle
|
||||
|
||||
Returns a true value if we are on a mobile display.
|
||||
|
|
@ -130,7 +149,6 @@ sub useMobileStyle {
|
|||
if (! $session->setting->get('useMobileStyle')) {
|
||||
return $self->{_useMobileStyle} = 0;
|
||||
}
|
||||
|
||||
if ($session->request->browser->mobile) {
|
||||
return $self->{_useMobileStyle} = 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,8 +40,8 @@ use List::MoreUtils qw(any);
|
|||
use File::Copy ();
|
||||
use File::Temp ();
|
||||
use Try::Tiny;
|
||||
use Monkey::Patch qw( patch_object );
|
||||
use Scope::Guard;
|
||||
use Try::Tiny;
|
||||
use WebGUI::Paths -inc;
|
||||
use namespace::clean;
|
||||
|
||||
|
|
@ -537,6 +537,26 @@ sub originalConfig {
|
|||
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
=head2 overrideSetting (name, val)
|
||||
|
||||
Overrides WebGUI::Test->session->setting->get($name) to return $val until the
|
||||
handle this method returns goes out of scope.
|
||||
|
||||
=cut
|
||||
|
||||
sub overrideSetting {
|
||||
my ($class, $name, $val) = @_;
|
||||
patch_object $class->session->setting => get => sub {
|
||||
my $get = shift;
|
||||
return $val if $_[1] eq $name;
|
||||
goto &$get;
|
||||
};
|
||||
}
|
||||
|
||||
#----------------------------------------------------------------------------
|
||||
|
||||
=head2 cleanupAdminInbox ( )
|
||||
|
||||
Push a list of Asset objects onto the stack of assets to be automatically purged
|
||||
|
|
|
|||
|
|
@ -38,6 +38,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");
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
|
|
@ -76,25 +93,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';
|
||||
}
|
||||
|
|
@ -104,6 +109,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.
|
||||
|
|
|
|||
|
|
@ -76,6 +76,22 @@ These methods are available from this class:
|
|||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 cleanup ( )
|
||||
|
||||
Override this activity to add a cleanup routine to be run if an instance
|
||||
is deleted with this activity currently in a waiting state. This is a stub
|
||||
and will do nothing unless overridden.
|
||||
|
||||
=cut
|
||||
|
||||
sub cleanup {
|
||||
my $self = shift;
|
||||
my $instance = shift;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 create ( session, workflowId [, id, classname ] )
|
||||
|
||||
Creates a new instance of this activity in a workflow.
|
||||
|
|
|
|||
82
lib/WebGUI/Workflow/Activity/RemoveOldForks.pm
Normal file
82
lib/WebGUI/Workflow/Activity/RemoveOldForks.pm
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
package WebGUI::Workflow::Activity::RemoveOldForks;
|
||||
|
||||
=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
|
||||
-------------------------------------------------------------------
|
||||
|
||||
=cut
|
||||
|
||||
use warnings;
|
||||
use strict;
|
||||
|
||||
use base 'WebGUI::Workflow::Activity';
|
||||
|
||||
use WebGUI::International;
|
||||
use WebGUI::Fork;
|
||||
|
||||
=head1 NAME
|
||||
|
||||
WebGUI::Workflow::Activity::RemoveOldForks
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
Remove forks that are older than a configurable threshold.
|
||||
|
||||
=head1 METHODS
|
||||
|
||||
These methods are available from this class:
|
||||
|
||||
=cut
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 definition ( session, definition )
|
||||
|
||||
See WebGUI::Workflow::Activity::definition() for details.
|
||||
|
||||
=cut
|
||||
|
||||
sub definition {
|
||||
my ( $class, $session, $definition ) = @_;
|
||||
my $i18n = WebGUI::International->new( $session, 'Workflow_Activity_RemoveOldForks' );
|
||||
my %def = (
|
||||
name => $i18n->get('activityName'),
|
||||
properties => {
|
||||
interval => {
|
||||
fieldType => 'interval',
|
||||
label => $i18n->get('interval'),
|
||||
defaultValue => 60 * 60 * 24 * 7,
|
||||
hoverHelp => $i18n->get('interval help')
|
||||
}
|
||||
}
|
||||
);
|
||||
push @$definition, \%def;
|
||||
return $class->SUPER::definition( $session, $definition );
|
||||
} ## end sub definition
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 execute ( [ object ] )
|
||||
|
||||
See WebGUI::Workflow::Activity::execute() for details.
|
||||
|
||||
=cut
|
||||
|
||||
sub execute {
|
||||
my $self = shift;
|
||||
my $db = $self->session->db;
|
||||
my $tbl = $db->dbh->quote_identifier( WebGUI::Fork->tableName );
|
||||
my $time = time - $self->get('interval');
|
||||
$db->write( "DELETE FROM $tbl WHERE endTime <= ?", [$time] );
|
||||
return $self->COMPLETE;
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
@ -44,6 +44,23 @@ These methods are available from this class:
|
|||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 cleanup ( )
|
||||
|
||||
Override this activity to add a cleanup routine to be run if an instance
|
||||
is deleted with this activity currently in a waiting state. This is a stub
|
||||
and will do nothing unless overridden.
|
||||
|
||||
=cut
|
||||
|
||||
sub cleanup {
|
||||
my $self = shift;
|
||||
my $instance = shift;
|
||||
$self->setMessageCompleted($instance);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------
|
||||
|
||||
=head2 definition ( session, definition )
|
||||
|
||||
See WebGUI::Workflow::Activity::definition() for details.
|
||||
|
|
|
|||
|
|
@ -101,11 +101,22 @@ A boolean, that if true will not notify Spectre of the delete.
|
|||
=cut
|
||||
|
||||
sub delete {
|
||||
my $self = shift;
|
||||
my $self = shift;
|
||||
my $session = $self->session;
|
||||
my $skipNotify = shift;
|
||||
$self->session->db->write("delete from WorkflowInstanceScratch where instanceId=?",[$self->getId]);
|
||||
$self->session->db->deleteRow("WorkflowInstance","instanceId",$self->getId);
|
||||
WebGUI::Workflow::Spectre->new($self->session)->notify("workflow/deleteInstance",$self->getId) unless ($skipNotify);
|
||||
|
||||
if ( $self->hasNextActivity ) {
|
||||
#We are deleting in the middle of a workflow - Get the current activity and call the cleanup routine
|
||||
my $activity = $self->getNextActivity;
|
||||
eval { $activity->cleanup($self) };
|
||||
if ($@) {
|
||||
$session->errorHandler->error("Caught exception executing cleanup routine which was not run on workflow activity ".$activity->getId." for instance ".$self->getId.". The following error was reported: ".$@);
|
||||
}
|
||||
}
|
||||
|
||||
$session->db->write("delete from WorkflowInstanceScratch where instanceId=?",[$self->getId]);
|
||||
$session->db->deleteRow("WorkflowInstance","instanceId",$self->getId);
|
||||
WebGUI::Workflow::Spectre->new($session)->notify("workflow/deleteInstance",$self->getId) unless ($skipNotify);
|
||||
|
||||
# We will need to remember that we were deleted if we get realtime-run
|
||||
# during start().
|
||||
|
|
|
|||
|
|
@ -1127,6 +1127,24 @@ search has been done.|,
|
|||
lastUpdated => 1231180362,
|
||||
},
|
||||
|
||||
'Creating column headers' => {
|
||||
message => q|Creating column headers.|,
|
||||
lastUpdated => 1231180362,
|
||||
context => q|Status message in the Export Thingy progress bar.|,
|
||||
},
|
||||
|
||||
'Writing data' => {
|
||||
message => q|Writing data.|,
|
||||
lastUpdated => 1231180362,
|
||||
context => q|Status message in the Export Thingy progress bar.|,
|
||||
},
|
||||
|
||||
'Return to %s' => {
|
||||
message => q|Return to %s.|,
|
||||
lastUpdated => 1231180362,
|
||||
context => q|Status message in the Export Thingy progress bar. %s is the name of the Thing that is being exported.|,
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
1;
|
||||
|
|
|
|||
22
lib/WebGUI/i18n/English/Fork_ProgressBar.pm
Normal file
22
lib/WebGUI/i18n/English/Fork_ProgressBar.pm
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package WebGUI::i18n::English::Fork_ProgressBar;
|
||||
|
||||
use strict;
|
||||
|
||||
our $I18N = {
|
||||
'time elapsed' => {
|
||||
message => 'Time Elapsed',
|
||||
lastUpdated => 1286466369,
|
||||
context => 'Used as a label to indicate how many seconds have gone by since the forked process started running',
|
||||
},
|
||||
'seconds' => {
|
||||
message => 'seconds',
|
||||
lastUpdated => 1286466433,
|
||||
},
|
||||
'current asset' => {
|
||||
message => 'Current Asset',
|
||||
lastUpdated => 1286466701,
|
||||
context => 'Used as a label to indicate which asset is in "focus"',
|
||||
},
|
||||
};
|
||||
|
||||
1;
|
||||
20
lib/WebGUI/i18n/English/Workflow_Activity_RemoveOldForks.pm
Normal file
20
lib/WebGUI/i18n/English/Workflow_Activity_RemoveOldForks.pm
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package WebGUI::i18n::English::Workflow_Activity_RemoveOldForks;
|
||||
|
||||
use strict;
|
||||
|
||||
our $I18N = {
|
||||
'interval help' => {
|
||||
message => 'How long do we wait after process completion before deleting it?',
|
||||
lastUpdated => 1285358250,
|
||||
},
|
||||
'interval' => {
|
||||
message => q|Interval|,
|
||||
lastUpdated => 1285358250,
|
||||
},
|
||||
'activityName' => {
|
||||
message => q|Remove Old Forks|,
|
||||
lastUpdated => 1285358250,
|
||||
},
|
||||
};
|
||||
|
||||
1;
|
||||
Loading…
Add table
Add a link
Reference in a new issue