- Added a realtime workflow option for content publishing.

- Added switches to auto request commit operations and skip adding comments.
This commit is contained in:
JT Smith 2007-05-30 16:07:43 +00:00
parent c09b2cae1b
commit 3259da6750
403 changed files with 350 additions and 50605 deletions

View file

@ -2144,10 +2144,22 @@ sub www_editSave {
}
$object->updateHistory("edited");
if ($self->session->form->process("saveAndCommit") ne "") {
$self->session->http->setRedirect($self->getUrl("op=commitVersionTag;tagId=".WebGUI::VersionTag->getWorking($self->session)->getId));
if ($self->session->setting->get("skipCommitComments")) {
$self->session->http->setRedirect($self->getUrl("op=commitVersionTagConfirm;tagId=".WebGUI::VersionTag->getWorking($self->session)->getId));
} else {
$self->session->http->setRedirect($self->getUrl("op=commitVersionTag;tagId=".WebGUI::VersionTag->getWorking($self->session)->getId));
}
return "1";
}
if ($self->session->setting->get("autoRequestCommit")) {
if ($self->session->setting->get("skipCommitComments")) {
WebGUI::VersionTag->getWorking($self->session)->requestCommit;
} else {
$self->session->http->setRedirect($self->getUrl("op=commitVersionTag;tagId=".WebGUI::VersionTag->getWorking($self->session)->getId));
}
}
if ($self->session->form->process("proceed") eq "manageAssets") {
$self->session->asset($object->getParent);
return $self->session->asset->www_manageAssets;
@ -2166,6 +2178,7 @@ sub www_editSave {
return $self->session->asset->www_view;
}
#-------------------------------------------------------------------

View file

@ -200,6 +200,24 @@ sub editSave {
}
$tempStorage->delete;
# deal with special commit rules
if ($self->session->form->process("saveAndCommit") ne "") {
if ($self->session->setting->get("skipCommitComments")) {
$self->session->http->setRedirect($self->getUrl("op=commitVersionTagConfirm;tagId=".WebGUI::VersionTag->getWorking($self->session)->getId));
} else {
$self->session->http->setRedirect($self->getUrl("op=commitVersionTag;tagId=".WebGUI::VersionTag->getWorking($self->session)->getId));
}
return "1";
}
if ($self->session->setting->get("autoRequestCommit")) {
if ($self->session->setting->get("skipCommitComments")) {
WebGUI::VersionTag->getWorking($self->session)->requestCommit;
} else {
$self->session->http->setRedirect($self->getUrl("op=commitVersionTag;tagId=".WebGUI::VersionTag->getWorking($self->session)->getId));
}
}
return $self->getParent->www_manageAssets if ($self->session->form->process("proceed") eq "manageAssets");
return $self->getParent->www_view;
}

View file

@ -268,6 +268,9 @@ sub www_setContentPositions {
$self->addRevision({
contentPositions=>$self->session->form->process("map")
});
if ($self->session->setting->get("autoRequestCommit")) {
WebGUI::VersionTag->getWorking($self->session)->requestCommit;
}
return "Map set: ".$self->session->form->process("map");
}

View file

@ -63,6 +63,11 @@ A text label that will be displayed if toHtmlWithWrapper() is called. Defaults t
If set to 1 then a "None" option will appear in the list of workflows, which will store a null value in the field. Defaults to 0.
=head4 includeRealtime
Most workflow triggers can't handle realtime workflows, so we leave out realtime workflows unless they should
specifically be included.
=cut
sub definition {
@ -86,6 +91,9 @@ sub definition {
none=>{
defaulValue=>0
},
includeRealtime => {
defaultValue => 0
},
dbDataType => {
defaultValue => "VARCHAR(22) BINARY",
},
@ -103,7 +111,7 @@ Renders a template picker control.
sub toHtml {
my $self = shift;
my $workflowList = WebGUI::Workflow->getList($self->session, $self->get("type"));
my $workflowList = WebGUI::Workflow->getList($self->session, $self->get("type"), $self->get("includeRealtime"));
$workflowList->{""} = "None" if ($self->get("none"));
$self->set("options", $workflowList);
return $self->SUPER::toHtml();

View file

@ -99,6 +99,13 @@ sub process {
my @tags = ();
if ($working) {
$workingId = $working->getId;
my $commitUrl = "";
if ($session->setting->get("skipCommitComments")) {
$session->url->page("op=commitVersionTagConfirm;tagId=".$workingId);
}
else {
$session->url->page("op=commitVersionTag;tagId=".$workingId);
}
push(@tags, {
url=>$session->url->page("op=commitVersionTag;tagId=".$workingId),
title=>$i18n->get("commit my changes"),

View file

@ -77,7 +77,24 @@ sub definition {
defaultValue=>$session->setting->get("defaultVersionTagWorkflow"),
type=>"WebGUI::VersionTag",
label=>$i18n->get("default version tag workflow"),
hoverHelp=>$i18n->get('default version tag workflow help')
hoverHelp=>$i18n->get('default version tag workflow help'),
includeRealtime=>1,
});
push(@fields, {
tab=>"content",
fieldType=>"yesNo",
name=>"autoRequestCommit",
label=>$i18n->get("auto request commit"),
hoverHelp=>$i18n->get("auto request commit help"),
defaultValue=>$session->setting->get("autoRequestCommit")
});
push(@fields, {
tab=>"content",
fieldType=>"yesNo",
name=>"skipCommitComments",
label=>$i18n->get("skip commit comments"),
hoverHelp=>$i18n->get("skip commit comments"),
defaultValue=>$session->setting->get("skipCommitComments")
});
push(@fields, {
tab=>"content",
@ -164,10 +181,10 @@ sub definition {
push(@fields, {
tab=>"content",
fieldType=>"yesNo",
name=>"metaDataEnabled",
label=>$i18n->get("Enable Metadata"),
hoverHelp=>$i18n->get("Enable Metadata description"),
defaultValue=>$session->setting->get("metaDataEnabled")
name=>"metaDataEnabled",
label=>$i18n->get("Enable Metadata"),
hoverHelp=>$i18n->get("Enable Metadata description"),
defaultValue=>$session->setting->get("metaDataEnabled")
});
# user interface settings
push(@fields, {

View file

@ -116,7 +116,8 @@ sub www_editVersionTag {
if ($session->user->isInGroup("pbgroup000000000000016")) {
$f->workflow(
value=>$workflowId,
type=>"WebGUI::VersionTag"
type=>"WebGUI::VersionTag",
includeRealtime=>1,
);
$f->group(
value=>[$groupId],
@ -240,13 +241,17 @@ sub www_commitVersionTagConfirm {
if ($tagId) {
my $tag = WebGUI::VersionTag->new($session, $tagId);
if (defined $tag && $session->user->isInGroup($tag->get("groupToUse"))) {
$tag->set({comments=>$session->form->process("comments", "textarea")});
$tag->requestCommit;
my $i18n = WebGUI::International->new($session, "VersionTag");
my $ac = WebGUI::AdminConsole->new($session,"versions");
$tag->set({comments=>$session->form->process("comments", "textarea")});
eval { $tag->requestCommit; };
my $error = "";
if ($@) {
$error .= $i18n->get("bad commit")." ".$@;
}
my $ac = WebGUI::AdminConsole->new($session,"versions");
my $default = WebGUI::Asset->getDefault($session);
return $ac->render(
'<p>'.$i18n->get("commit accepted").'</p>'
$error.'<p>'.$i18n->get("commit accepted").'</p>'
.'<ul>
<li><a href="'.$default->getUrl.'">'.$i18n->get("back to home").'</a></li>
<li><a href="'.$default->getUrl("op=manageVersions").'">'.$i18n->get("manage versions").'</a></li>

View file

@ -199,22 +199,21 @@ sub www_editWorkflow {
label=>$i18n->get("is enabled"),
hoverHelp=>$i18n->get("is enabled help")
);
$f->yesNo(
name=>"isSingleton",
value=>$workflow->get("isSingleton"),
defaultValue=>0,
label=>$i18n->get("is singleton"),
hoverHelp=>$i18n->get("is singleton help")
);
$f->yesNo(
name=>"isSerial",
value=>$workflow->get("isSerial"),
defaultValue=>0,
label=>$i18n->get("is serial"),
hoverHelp=>$i18n->get("is serial help")
$f->selectBox(
name=>"mode",
options=>{
singleton=>$i18n->get("singleton"),
parallel=>$i18n->get("parallel"),
serial=>$i18n->get("serial"),
realtime=>$i18n->get("realtime"),
},
value=>$workflow->get("mode") || "parallel",
defaultValue=>"parallel",
label=>$i18n->get("mode"),
hoverHelp=>$i18n->get("mode help")
);
$f->submit;
my $steps = '<div style="clear:both"></div><table class="content">';
my $steps = '<table class="content">';
my $rs = $session->db->read("select activityId, title from WorkflowActivity where workflowId=? order by sequenceNumber",[$workflow->getId]);
while (my ($id, $title) = $rs->array) {
$steps .= '<tr><td>'
@ -224,7 +223,7 @@ sub www_editWorkflow {
.$session->icon->moveUp("op=promoteWorkflowActivity;workflowId=".$workflow->getId.";activityId=".$id)
.'</td><td>'.$title.'</td></tr>';
}
$steps .= '</table>';
$steps .= '</table><div style="clear: both;"></div>';
my $ac = WebGUI::AdminConsole->new($session,"workflow");
$ac->addSubmenuItem($session->url->page("op=addWorkflow"), $i18n->get("add a new workflow"));
$ac->addSubmenuItem($session->url->page("op=manageWorkflows"), $i18n->get("manage workflows"));
@ -247,8 +246,7 @@ sub www_editWorkflowSave {
my $workflow = WebGUI::Workflow->new($session, $session->form->param("workflowId"));
$workflow->set({
enabled => $session->form->get("enabled", "yesNo"),
isSingleton => $session->form->get("isSingleton", "yesNo"),
isSerial => $session->form->get("isSerial", "yesNo"),
mode => $session->form->get("mode"),
title => $session->form->get("title"),
description => $session->form->get("description", "textarea"),
});

View file

@ -15,6 +15,7 @@ package WebGUI::VersionTag;
=cut
use strict;
use Carp qw(carp);
use WebGUI::Asset;
use WebGUI::Workflow::Instance;
@ -383,7 +384,8 @@ sub new {
=head2 requestCommit ( )
Locks the version tag and then kicks off the approval/commit workflow for it.
Locks the version tag and then kicks off the approval/commit workflow for it. A carp is thrown if workflow is
realtime and fails.
=cut
@ -399,6 +401,19 @@ sub requestCommit {
$self->{_data}{committedBy} = $self->session->user->userId;
$self->{_data}{workflowInstanceId} = $instance->getId;
$self->session->db->setRow("assetVersionTag","tagId",$self->{_data});
# deal with realtime
if ($instance->getWorkflow->isRealtime) {
my $status = $instance->runAll;
if ($status eq "done") {
$instance->delete;
} else {
my $errorMessage = "Realtime workflow instance ".$instance->getId." returned status ".$status." where
'done' was expected";
$self->session->errorHandler->warn($errorMessage);
carp $errorMessage;
}
}
}

View file

@ -227,6 +227,8 @@ sub getActivities {
return \@activities;
}
#-------------------------------------------------------------------
=head2 getInstances ( )
Returns an array reference of the instance objects currently existing for this workflow.
@ -243,6 +245,8 @@ sub getInstances {
return \@instances;
}
#-------------------------------------------------------------------
=head2 getCrons ( )
Returns an array reference of the cron objects that trigger this workflow.
@ -274,7 +278,7 @@ sub getId {
#-------------------------------------------------------------------
=head2 getList ( session, [ type ] )
=head2 getList ( session, [ type, includeRealtime ] )
Returns a hash reference of workflowId/title pairs of all enabled workflows. This is a class method.
@ -286,18 +290,27 @@ A reference to the current session.
If specified this will limit the list to a certain type of workflow based upon the object type that the workflow is set up to handle.
=head3 includeRealtime
If set to 1 the list returned will include workflows with a mode of "realtime", otherwise it won't.
=cut
sub getList {
my $class = shift;
my $session = shift;
my $type = shift;
my $includeRealtime = shift;
my $sql = "select workflowId, title from Workflow where enabled=1";
my @params;
if ($type) {
$sql .= " and type=?";
$type = [$type];
push(@params, $type);
}
return $session->db->buildHashRef($sql, $type);
unless ($includeRealtime) {
$sql .= " and mode<>'realtime'";
}
return $session->db->buildHashRef($sql, \@params);
}
@ -324,6 +337,62 @@ sub getNextActivity {
}
#-------------------------------------------------------------------
=head2 isParallel ( )
Returns 1 if the mode is set to "parallel".
=cut
sub isParallel {
my $self = shift;
return ($self->get("mode") eq "parallel") ? 1 : 0;
}
#-------------------------------------------------------------------
=head2 isRealtime ( )
Returns 1 if the mode is set to "realtime".
=cut
sub isRealtime {
my $self = shift;
return ($self->get("mode") eq "realtime") ? 1 : 0;
}
#-------------------------------------------------------------------
=head2 isSerial ( )
Returns 1 if the mode is set to "serial".
=cut
sub isSerial {
my $self = shift;
return ($self->get("mode") eq "serial") ? 1 : 0;
}
#-------------------------------------------------------------------
=head2 isSingleton ( )
Returns 1 if the mode is set to "singleton".
=cut
sub isSingleton {
my $self = shift;
return ($self->get("mode") eq "singleton") ? 1 : 0;
}
#-------------------------------------------------------------------
=head2 new ( session, workflowId )
@ -431,34 +500,30 @@ A boolean indicating whether this workflow may be executed right now.
A string indicating the type of object this workflow will be operating on. Valid values are "None", or any object type, like "WebGUI::VersionTag".
=head4 isSingleton
=head4 mode
A boolean indicating whether this workflow should be run as a singleton. If it's a singleton, then only one instance of the workflow will be allowed to be created at a given time. So if you try to create a new instance of it, and one instance is already created, the create() method will return undef instead of a reference to the object.
=head4 isSerial
A boolean indicating whether this workflow should be run in serial mode. If it's run in serial, then only one instance of this workflow will be run at a given time, and all other instances of it will queue up.
A string containing one of "singleton", "parallel", "serial", or "realtime". Parallel is the default and should be
used in most situations. Singltons will allow only one instance of the workflow to be created at one time. New
instances will be destroyed immediately if a one instance of a singleton already exists. Serial workflows will run
instances sequentially in FIFO. Realtime workflows will run immediately without being handed off to Spectre for
governance.
=cut
sub set {
my $self = shift;
my $properties = shift;
if ($properties->{enabled} == 1) {
$self->{_data}{enabled} = 1;
} elsif ($properties->{enabled} == 0) {
$self->{_data}{enabled} = 0;
}
# depricated. replaced by mode.
if ($properties->{isSerial} == 1) {
$self->{_data}{isSerial} = 1;
} elsif ($properties->{isSerial} == 0) {
$self->{_data}{isSerial} = 0;
$properties->{mode} = "serial";
}
if ($properties->{isSingleton} == 1) {
$self->{_data}{isSingleton} = 1;
} elsif ($properties->{isSingleton} == 0) {
$self->{_data}{isSingleton} = 0;
$properties->{mode} = "singleton";
}
$self->{_data}{mode} = $properties->{mode} || "parallel";
$self->{_data}{enabled} = ($properties->{enabled} == 1) ? 1 : 0;
$self->{_data}{title} = $properties->{title} || $self->{_data}{title} || "Untitled";
$self->{_data}{description} = (exists $properties->{description}) ? $properties->{description} : $self->{_data}{description};
$self->{_data}{type} = $properties->{type} || $self->{_data}{type} || "None";

View file

@ -42,7 +42,8 @@ These methods are available from this class:
=head2 create ( session, properties )
Creates a new workflow instance and returns a reference to the object. Will return undef if the workflow specified is serial and an instance of it already exists.
Creates a new workflow instance and returns a reference to the object. Will return undef if the workflow specified
is singleton and an instance of it already exists.
=head3 session
@ -58,10 +59,17 @@ sub create {
my $class = shift;
my $session = shift;
my $properties = shift;
my ($isSingleton) = $session->db->quickArray("select isSingleton from Workflow where workflowId=?",[$properties->{workflowId}]);
my $params = (exists $properties->{parameters}) ? JSON::objToJson({parameters => $properties->{parameters}}, {pretty => 1, indent => 4, autoconv=>0, skipinvalid=>1}) : undef;
# do singleton check
my ($isSingleton) = $session->db->quickArray("select count(*) from Workflow where workflowId=? and
mode='singleton'",[$properties->{workflowId}]);
my $params = (exists $properties->{parameters})
? JSON::objToJson({parameters => $properties->{parameters}}, {pretty => 1, indent => 4, autoconv=>0, skipinvalid=>1})
: undef;
my ($count) = $session->db->quickArray("select count(*) from WorkflowInstance where workflowId=? and parameters=?",[$properties->{workflowId},$params]);
return undef if ($isSingleton && $count);
# create instance
my $instanceId = $session->db->setRow("WorkflowInstance","instanceId",{instanceId=>"new", runningSince=>time()});
my $self = $class->new($session, $instanceId);
$properties->{notifySpectre} = 1 unless ($properties->{notifySpectre} eq "0");
@ -119,8 +127,9 @@ Deconstructor.
=cut
sub DESTROY {
my $self = shift;
undef $self;
my $self = shift;
delete $self->{_workflow};
undef $self;
}
@ -153,12 +162,12 @@ Returns an array reference of all the instance objects defined in this system. A
sub getAllInstances {
my $class = shift;
my $session = shift;
my @instances = ();
my $rs = $session->db->read("SELECT instanceId FROM WorkflowInstance");
while (my ($instanceId) = $rs->array) {
push(@instances, WebGUI::Workflow::Instance->new($session, $instanceId));
}
return \@instances;
my @instances = ();
my $rs = $session->db->read("SELECT instanceId FROM WorkflowInstance");
while (my ($instanceId) = $rs->array) {
push(@instances, WebGUI::Workflow::Instance->new($session, $instanceId));
}
return \@instances;
}
@ -218,7 +227,10 @@ Returns a reference to the workflow object this instance is associated with.
sub getWorkflow {
my $self = shift;
return WebGUI::Workflow->new($self->session, $self->get("workflowId"));
unless (exists $self->{_workflow}) {
$self->{_workflow} = WebGUI::Workflow->new($self->session, $self->get("workflowId"));
}
return $self->{_workflow};
}
#-------------------------------------------------------------------
@ -272,7 +284,7 @@ sub run {
$self->set({lastStatus=>"disabled", notifySpectre=>0});
return "disabled";
}
if ($workflow->get("isSerial")) {
if ($workflow->isSerial) {
my ($firstId) = $self->session->db->quickArray(
"select instanceId from WorkflowInstance where workflowId=? order by runningSince",
[$workflow->getId]
@ -333,6 +345,23 @@ sub run {
return $status;
}
#-------------------------------------------------------------------
=head2 runAll ( )
Runs all activities in this workflow instance, and returns the last status code, which should be "done" unless
something bad happens.
=cut
sub runAll {
my $self = shift;
my $status = "complete";
while ($status eq "complete") {
$status = $self->run;
}
return $status;
}
#-------------------------------------------------------------------
@ -401,7 +430,7 @@ sub set {
$self->{_data}{currentActivityId} = (exists $properties->{currentActivityId}) ? $properties->{currentActivityId} : $self->{_data}{currentActivityId};
$self->{_data}{lastUpdate} = time();
$self->session->db->setRow("WorkflowInstance","instanceId",$self->{_data});
if ($properties->{notifySpectre}) {
if (!$self->getWorkflow->isRealtime && $properties->{notifySpectre}) {
my $spectre = WebGUI::Workflow::Spectre->new($self->session);
$spectre->notify("workflow/deleteInstance",$self->getId) unless ($properties->{newlyCreated});
$spectre->notify("workflow/addInstance", {cookieName=>$self->session->config->getCookieName, gateway=>$self->session->config->get("gateway"), sitename=>$self->session->config->get("sitename")->[0], instanceId=>$self->getId, priority=>$self->{_data}{priority}});

View file

@ -7,6 +7,12 @@ our $I18N = {
lastUpdated => 0
},
'bad commit' => {
message => q|Something bad happened while trying to commit your content. Please contact your system
administrator.|,
lastUpdated => 0
},
'back to home' => {
message => q|Back to home.|,
lastUpdated => 0

View file

@ -1,6 +1,28 @@
package WebGUI::i18n::English::WebGUI;
our $I18N = {
'skip commit comments' => {
message => q|Skip commit comments?|,
lastUpdated => 0,
},
'skip commit comments help' => {
message => q|Do you wish to be prompted to add comments to your content commits?|,
lastUpdated => 0,
},
'auto request commit' => {
message => q|Automatically request commit?|,
lastUpdated => 0,
},
'auto request commit help' => {
message => q|Would you like the system to automatically request commits for you so that you don't have to
remember to hit "Commit My Changes"? Note that when using this in conjunction with "Skip commit comments?"
and a realtime commit workflow it effectively hides the whole versioning and workflow process from users.|,
lastUpdated => 0,
},
'select' => {
message => q|Select|,
lastUpdated => 0,

View file

@ -49,27 +49,47 @@ our $I18N = {
lastUpdated => 0,
},
'is singleton help' => {
message => q|If yes is selected then only one instance of this workflow will be allowed to be created at one time. Generally speaking this would be a bad idea for approval workflows, but is probably a good idea for workflows that download emails from a remote server, to avoid getting duplicates.|,
context => q|the hover help for the is singleton field|,
lastUpdated => 0,
},
'singleton' => {
message => q|Singleton|,
},
'is singleton' => {
message => q|Is a singleton?|,
context => q|A question that asks the user whether this workflow may be instanciated multiple times concurrently or not.|,
lastUpdated => 0,
},
'serial' => {
message => q|Serial|,
},
'is serial help' => {
message => q|If yes is selected then only one instance of this workflow will be allowed to be run at one time, while new instances get queued up and wait for the running one to complete. This is generally bad for a workflow, but it can be good when multiple instances of workflow have to operate on the same data.|,
context => q|the hover help for the is serial field|,
lastUpdated => 0,
},
'parallel' => {
message => q|Parallel|,
},
'is serial' => {
message => q|Is serial?|,
context => q|A question that asks the user whether this workflow may be instanciated multiple times concurrently or not.|,
'realtime' => {
message => q|Realtime|,
},
'mode' => {
message => q|Mode|,
},
'mode help' => {
message => q|The mode of a workflow determines the precidence of when and how a workflow is run.
<p>Parallel workflows may run as many instances of the workflow as there are in existence. This is what you
want for most workflows. This mode is asynchronous.</p>
<p>Singleton means only one instance of this workflow will be allowed to be created at one time. Generally
speaking this would be a bad idea for approval workflows, but is probably a good idea for workflows that
download emails from a remote server, to avoid getting duplicates. This mode is asynchronous.</p>
<p>Serial means that you can create as many instances of a workflow that you like, but they will run
sequentially one after the other. Two instances of the same workflow will not run a the same time. This
is generally bad for approval processes, but it's good for maintenance functions or other things that need
to operate on the same data. Processing sequentially will ensure that the workflows don't overwrite each
other's data. This mode is asynchronous.</p>
<p>Realtime workflows run immediately and in parallel, meaning there can be more than one workflow of this
type in existence at one time and it will run at the same time as others. Realtime workflows are good for
publishing operations that don't have an approval needed and other functions that you want to ensure happen
immediately. This mode is synchronous. <b>NOTE:</b> Do not include any activities which may return a
waiting status, such as those that require human approval or an external event to occur. Doing so will
cause this workflow to run indefinitely and could lead to a crashed system. In addition, not all triggers
support realtime workflows. If they do not, then workflows with a mode of realtime won't show up in their
workflow select list.|,
context => q|the hover help for the mode field|,
lastUpdated => 0,
},