Skip to content

Commit

Permalink
Merge pull request #201 from nyeholt/master
Browse files Browse the repository at this point in the history
API Support for multiple workflow definitions
  • Loading branch information
nyeholt committed Dec 11, 2014
2 parents eb9f653 + dd56566 commit 6c1ec3c
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 66 deletions.
7 changes: 6 additions & 1 deletion code/dataobjects/WorkflowDefinition.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ class WorkflowDefinition extends DataObject {
'Template' => 'Varchar',
'TemplateVersion' => 'Varchar',
'RemindDays' => 'Int',
'Sort' => 'Int'
'Sort' => 'Int',
'InitialActionButtonText' => 'Varchar'
);

private static $default_sort = 'Sort';
Expand Down Expand Up @@ -173,6 +174,10 @@ public function getCMSFields() {

$fields->addFieldToTab('Root.Main', new TextField('Title', $this->fieldLabel('Title')));
$fields->addFieldToTab('Root.Main', new TextareaField('Description', $this->fieldLabel('Description')));
$fields->addFieldToTab('Root.Main', TextField::create(
'InitialActionButtonText',
_t('WorkflowDefinition.INITIAL_ACTION_BUTTON_TEXT', 'Initial Action Button Text')
));
if($this->ID) {
$fields->addFieldToTab('Root.Main', new CheckboxSetField('Users', _t('WorkflowDefinition.USERS', 'Users'), $cmsUsers));
$fields->addFieldToTab('Root.Main', new TreeMultiselectField('Groups', _t('WorkflowDefinition.GROUPS', 'Groups'), 'Group'));
Expand Down
7 changes: 3 additions & 4 deletions code/dataobjects/WorkflowInstance.php
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ protected function userHasAccess($member) {
$inWorkflowGroupOrUserTables = ($member->inGroups($this->Groups()) || $this->Users()->find('ID', $member->ID));
// This method is used in more than just the ModelAdmin. Check for the current controller to determine where canView() expectations differ
if($this->getTarget() && Controller::curr()->getAction() == 'index' && !$inWorkflowGroupOrUserTables) {
if($this->getVersionedConnection($this->getTarget()->ID,$member->ID,$this->DefinitionID)) {
if($this->getVersionedConnection($this->getTarget()->ID, $member->ID)) {
return true;
}
return false;
Expand Down Expand Up @@ -608,13 +608,12 @@ public function doFrontEndAction(array $data, Form $form, SS_HTTPRequest $reques
*
* @param number $recordID
* @param number $userID
* @param number $definitionID
* @param number $wasPublished
* @return boolean
*/
public function getVersionedConnection($recordID,$userID,$definitionID,$wasPublished=0) {
public function getVersionedConnection($recordID, $userID, $wasPublished = 0) {
// Turn this into an array and run through implode()
$filter = "\"AuthorID\" = '".$userID."' AND \"RecordID\" = '".$recordID."' AND \"WorkflowDefinitionID\" = '".$definitionID."' AND \"WasPublished\" = '".$wasPublished."'";
$filter = "RecordID = {$recordID} AND AuthorID = {$userID} AND WasPublished = {$wasPublished}";
$query = new SQLQuery();
$query->setFrom('"SiteTree_versions"')->setSelect('COUNT("ID")')->setWhere($filter);
$query->firstRow();
Expand Down
9 changes: 5 additions & 4 deletions code/extensions/AdvancedWorkflowExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ class AdvancedWorkflowExtension extends LeftAndMainExtension {

private static $allowed_actions = array(
'updateworkflow',
'startworkflow'
);

public function startworkflow($data, $form, $request) {
$item = $form->getRecord();
$workflowID = isset($data['TriggeredWorkflowID']) ? intval($data['TriggeredWorkflowID']) : 0;

if (!$item || !$item->canEdit()) {
return;
Expand All @@ -24,8 +26,8 @@ public function startworkflow($data, $form, $request) {
$this->saveAsDraftWithAction($form, $item);

$svc = singleton('WorkflowService');
$svc->startWorkflow($item);
$svc->startWorkflow($item, $workflowID);

$negotiator = method_exists($this->owner, 'getResponseNegotiator') ? $this->owner->getResponseNegotiator() : Controller::curr()->getResponseNegotiator();
return $negotiator->respond($this->owner->getRequest());
}
Expand All @@ -37,8 +39,7 @@ public function startworkflow($data, $form, $request) {
* @param Form $form
*/
public function updateEditForm(Form $form) {
Requirements::javascript('advancedworkflow/javascript/advancedworkflow-cms.js');

Requirements::javascript(ADVANCED_WORKFLOW_DIR . '/javascript/advanced-workflow-cms.js');
$svc = singleton('WorkflowService');
$p = $form->getRecord();
$active = $svc->getWorkflowFor($p);
Expand Down
105 changes: 70 additions & 35 deletions code/extensions/WorkflowApplicable.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ class WorkflowApplicable extends DataExtension {
'WorkflowDefinition' => 'WorkflowDefinition',
);

private static $many_many = array(
'AdditionalWorkflowDefinitions' => 'WorkflowDefinition'
);

private static $dependencies = array(
'workflowService' => '%$WorkflowService',
);
Expand Down Expand Up @@ -70,31 +74,51 @@ public function updateSettingsFields(FieldList $fields) {

public function updateCMSFields(FieldList $fields) {
if(!$this->owner->hasMethod('getSettingsFields')) $this->updateFields($fields);

// Instantiate a hidden form field to pass the triggered workflow definition through, allowing a dynamic form action.

$fields->push(HiddenField::create(
'TriggeredWorkflowID'
));
}

public function updateFields(FieldList $fields) {
if (!$this->owner->ID) {
return $fields;
}
$effective = $this->workflowService->getDefinitionFor($this->owner);

$tab = $fields->fieldByName('Root') ? $fields->findOrMakeTab('Root.Workflow') : $fields;

if(Permission::check('APPLY_WORKFLOW')) {
$definition = new DropdownField('WorkflowDefinitionID', _t('WorkflowApplicable.DEFINITION', 'Applied Workflow'));
$definition->setSource($this->workflowService->getDefinitions()->map());
$definitions = $this->workflowService->getDefinitions()->map()->toArray();
$definition->setSource($definitions);
$definition->setEmptyString(_t('WorkflowApplicable.INHERIT', 'Inherit from parent'));

$tab->push($definition);

// $fields->addFieldToTab($tab, $definition);

// Allow an optional selection of additional workflow definitions.

if($this->owner->WorkflowDefinitionID) {
unset($definitions[$this->owner->WorkflowDefinitionID]);
$tab->push($additional = ListboxField::create(
'AdditionalWorkflowDefinitions',
_t('WorkflowApplicable.ADDITIONAL_WORKFLOW_DEFINITIONS', 'Additional Workflows')
));
$additional->setSource($definitions);
$additional->setMultiple(true);
}
}

$tab->push(new ReadonlyField(
'EffectiveWorkflow',
_t('WorkflowApplicable.EFFECTIVE_WORKFLOW', 'Effective Workflow'),
$effective ? $effective->Title : _t('WorkflowApplicable.NONE', '(none)')
));
// Display the effective workflow definition.

if($effective = $this->getWorkflowInstance()) {
$title = $effective->Definition()->Title;
$tab->push(ReadonlyField::create(
'EffectiveWorkflow',
_t('WorkflowApplicable.EFFECTIVE_WORKFLOW', 'Effective Workflow'),
$title
));
}

if($this->owner->ID) {
$config = new GridFieldConfig_Base();
Expand All @@ -112,7 +136,6 @@ public function updateCMSActions(FieldList $actions) {
$active = $this->workflowService->getWorkflowFor($this->owner);
$c = Controller::curr();
if ($c && $c->hasExtension('AdvancedWorkflowExtension')) {

if ($active) {
if ($this->canEditWorkflow()) {
$workflowOptions = new Tab(
Expand Down Expand Up @@ -144,12 +167,43 @@ public function updateCMSActions(FieldList $actions) {
// $actions->fieldByName('MajorActions') ? $actions->fieldByName('MajorActions')->push($action) : $actions->push($action);
}
} else {
$effective = $this->workflowService->getDefinitionFor($this->owner);
if ($effective && $effective->getInitialAction()) {
$action = FormAction::create('startworkflow', $effective->getInitialAction()->Title)
->setAttribute('data-icon', 'navigation');
$actions->fieldByName('MajorActions') ? $actions->fieldByName('MajorActions')->push($action) : $actions->push($action);
// Instantiate the workflow definition initial actions.
$definitions = $this->workflowService->getDefinitionsFor($this->owner);
if($definitions) {
$menu = $actions->fieldByName('ActionMenus');
if(is_null($menu)) {

// Instantiate a new action menu for any data objects.

$menu = $this->createActionMenus();
$actions->push($menu);
}
$tab = Tab::create(
'AdditionalWorkflows'
);
$menu->insertBefore($tab, 'MoreOptions');
$addedFirst = false;
foreach($definitions as $definition) {
if($definition->getInitialAction()) {
$action = FormAction::create(
"startworkflow-{$definition->ID}",
$definition->InitialActionButtonText ? $definition->InitialActionButtonText : $definition->getInitialAction()->Title
)->addExtraClass('start-workflow')->setAttribute('data-workflow', $definition->ID);

// The first element is the main workflow definition, and will be displayed as a major action.

if(!$addedFirst) {
$addedFirst = true;
$action->setAttribute('data-icon', 'navigation');
$majorActions = $actions->fieldByName('MajorActions');
$majorActions ? $majorActions->push($action) : $actions->push($action);
} else {
$tab->push($action);
}
}
}
}

}
}
}
Expand All @@ -160,25 +214,6 @@ protected function createActionMenu() {
return $rootTabSet;
}

public function updateFrontendActions($actions){
$active = $this->workflowService->getWorkflowFor($this->owner);

if ($active) {
if ($this->canEditWorkflow()) {
$action = FormAction::create('updateworkflow', _t('WorkflowApplicable.UPDATE_WORKFLOW', 'Update Workflow'));
$actions->fieldByName('MajorActions') ? $actions->fieldByName('MajorActions')->push($action) : $actions->push($action);
}
} else {
$effective = $this->workflowService->getDefinitionFor($this->owner);
if ($effective) {
// we can add an action for starting off the workflow at least
$initial = $effective->getInitialAction();
$action = FormAction::create('startworkflow', $initial->Title);
$actions->fieldByName('MajorActions') ? $actions->fieldByName('MajorActions')->push($action) : $actions->push($action);
}
}
}

/**
* Included in CMS-generated email templates for a NotifyUsersWorkflowAction.
* Returns an absolute link to the CMS UI for a Page object
Expand Down
65 changes: 63 additions & 2 deletions code/services/WorkflowService.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,55 @@ public function getDefinitionFor(DataObject $dataObject) {
return null;
}

/**
* Retrieves a workflow definition by ID for a data object.
*
* @param data object
* @param integer
* @return workflow definition
*/

public function getDefinitionByID($object, $workflowID) {

// Make sure the correct extensions have been applied to the data object.

$workflow = null;
if($object->hasExtension('WorkflowApplicable') || $object->hasExtension('FileWorkflowApplicable')) {

// Validate the workflow ID against the data object.

if(($object->WorkflowDefinitionID == $workflowID) || ($workflow = $object->AdditionalWorkflowDefinitions()->byID($workflowID))) {
if(is_null($workflow)) {
$workflow = DataObject::get_by_id('WorkflowDefinition', $workflowID);
}
}
}
return $workflow ? $workflow : null;
}

/**
* Retrieves and collates the workflow definitions for a data object, where the first element will be the main workflow definition.
*
* @param data object
* @return array
*/

public function getDefinitionsFor($object) {

// Retrieve the main workflow definition.

$default = $this->getDefinitionFor($object);
if($default) {

// Merge the additional workflow definitions.

return array_merge(array(
$default
), $object->AdditionalWorkflowDefinitions()->toArray());
}
return null;
}

/**
* Gets the workflow for the given item
*
Expand Down Expand Up @@ -165,13 +214,25 @@ public function executeTransition(DataObject $target, $transitionId) {
*
* @param DataObject $object
*/
public function startWorkflow(DataObject $object) {
public function startWorkflow(DataObject $object, $workflowID = null) {
$existing = $this->getWorkflowFor($object);
if ($existing) {
throw new ExistingWorkflowException(_t('WorkflowService.EXISTING_WORKFLOW_ERROR', "That object already has a workflow running"));
}

$definition = $this->getDefinitionFor($object);
$definition = null;
if($workflowID) {

// Retrieve the workflow definition that has been triggered.

$definition = $this->getDefinitionByID($object, $workflowID);
}
if(is_null($definition)) {

// Fall back to the main workflow definition.

$definition = $this->getDefinitionFor($object);
}

if ($definition) {
$instance = new WorkflowInstance();
Expand Down
38 changes: 38 additions & 0 deletions javascript/advanced-workflow-cms.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
;(function($) {
$(function() {

$.entwine('ss', function($) {

$('.cms-edit-form .Actions #ActionMenus_WorkflowOptions .action').entwine({
onclick: function(e) {
var transitionId = $(this).attr('data-transitionid');
var buttonName = $(this).attr('name');

buttonName = buttonName.replace(/-\d+/, '');
$(this).attr('name', buttonName);

$('input[name=TransitionID]').val(transitionId);

this._super(e);
}
});

$('.cms-edit-form .Actions .action.start-workflow').entwine({
onmouseup: function(e) {

// Populate the hidden form field with the selected workflow definition.

var action = $(this);
$('input[name=TriggeredWorkflowID]').val(action.data('workflow'));

// Update the element name to exclude the ID, therefore submitting correctly.

var name = action.attr('name');
action.attr('name', name.replace(/-\d+/, ''));
this._super(e);
}
});
});

});
})(jQuery);
20 changes: 0 additions & 20 deletions javascript/advancedworkflow-cms.js

This file was deleted.

0 comments on commit 6c1ec3c

Please sign in to comment.