GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Push — master ( bf6f51...19227d )
by Robbie
56:02
created

src/Extensions/WorkflowApplicable.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Symbiote\AdvancedWorkflow\Extensions;
4
5
use SilverStripe\Control\Controller;
6
use SilverStripe\Control\Director;
7
use SilverStripe\Forms\DropdownField;
8
use SilverStripe\Forms\FieldList;
9
use SilverStripe\Forms\FormAction;
10
use SilverStripe\Forms\GridField\GridField;
11
use SilverStripe\Forms\GridField\GridFieldConfig_Base;
12
use SilverStripe\Forms\GridField\GridFieldDetailForm;
13
use SilverStripe\Forms\GridField\GridFieldEditButton;
14
use SilverStripe\Forms\HiddenField;
15
use SilverStripe\Forms\ListboxField;
16
use SilverStripe\Forms\ReadonlyField;
17
use SilverStripe\Forms\Tab;
18
use SilverStripe\Forms\TabSet;
19
use SilverStripe\ORM\CMSPreviewable;
20
use SilverStripe\ORM\DataExtension;
21
use SilverStripe\ORM\DataList;
22
use SilverStripe\Security\Permission;
23
use SilverStripe\Security\Security;
24
use Symbiote\AdvancedWorkflow\DataObjects\WorkflowActionInstance;
25
use Symbiote\AdvancedWorkflow\DataObjects\WorkflowDefinition;
26
use Symbiote\AdvancedWorkflow\DataObjects\WorkflowInstance;
27
use Symbiote\AdvancedWorkflow\Services\WorkflowService;
28
use Symbiote\QueuedJobs\Services\AbstractQueuedJob;
29
30
/**
31
 * DataObjects that have the WorkflowApplicable extension can have a
32
 * workflow definition applied to them. At some point, the workflow definition is then
33
 * triggered.
34
 *
35
 * @author  [email protected]
36
 * @license BSD License (http://silverstripe.org/bsd-license/)
37
 * @package advancedworkflow
38
 */
39
class WorkflowApplicable extends DataExtension
40
{
41
    private static $has_one = [
42
        'WorkflowDefinition' => WorkflowDefinition::class,
43
    ];
44
45
    private static $many_many = [
46
        'AdditionalWorkflowDefinitions' => WorkflowDefinition::class
47
    ];
48
49
    private static $dependencies = [
50
        'workflowService' => '%$' . WorkflowService::class,
51
    ];
52
53
    /**
54
     *
55
     * Used to flag to this extension if there's a WorkflowPublishTargetJob running.
56
     * @var boolean
57
     */
58
    public $isPublishJobRunning = false;
59
60
    /**
61
     *
62
     * @param boolean $truth
63
     */
64
    public function setIsPublishJobRunning($truth)
65
    {
66
        $this->isPublishJobRunning = $truth;
67
    }
68
69
    /**
70
     *
71
     * @return boolean
72
     */
73
    public function getIsPublishJobRunning()
74
    {
75
        return $this->isPublishJobRunning;
76
    }
77
78
    /**
79
     *
80
     * @see {@link $this->isPublishJobRunning}
81
     * @return boolean
82
     */
83
    public function isPublishJobRunning()
84
    {
85
        $propIsSet = (bool) $this->getIsPublishJobRunning();
86
        return class_exists(AbstractQueuedJob::class) && $propIsSet;
87
    }
88
89
    /**
90
     * @var WorkflowService
91
     */
92
    public $workflowService;
93
94
    /**
95
     *
96
     * A cache var for the current workflow instance
97
     *
98
     * @var WorkflowInstance
99
     */
100
    protected $currentInstance;
101
102
    public function updateSettingsFields(FieldList $fields)
103
    {
104
        $this->updateFields($fields);
105
    }
106
107
    public function updateCMSFields(FieldList $fields)
108
    {
109
        if (!$this->owner->hasMethod('getSettingsFields')) {
110
            $this->updateFields($fields);
111
        }
112
113
        // Instantiate a hidden form field to pass the triggered workflow definition through,
114
        // allowing a dynamic form action.
115
116
        $fields->push(HiddenField::create(
117
            'TriggeredWorkflowID'
118
        ));
119
    }
120
121
    public function updateFields(FieldList $fields)
122
    {
123
        if (!$this->owner->ID) {
124
            return $fields;
125
        }
126
127
        $tab = $fields->fieldByName('Root') ? $fields->findOrMakeTab('Root.Workflow') : $fields;
128
129
        if (Permission::check('APPLY_WORKFLOW')) {
130
            $definition = new DropdownField(
131
                'WorkflowDefinitionID',
132
                _t('WorkflowApplicable.DEFINITION', 'Applied Workflow')
133
            );
134
            $definitions = $this->getWorkflowService()->getDefinitions()->map()->toArray();
135
            $definition->setSource($definitions);
136
            $definition->setEmptyString(_t('WorkflowApplicable.INHERIT', 'Inherit from parent'));
137
            $tab->push($definition);
138
139
            // Allow an optional selection of additional workflow definitions.
140
141
            if ($this->owner->WorkflowDefinitionID) {
142
                $fields->removeByName('AdditionalWorkflowDefinitions');
143
                unset($definitions[$this->owner->WorkflowDefinitionID]);
144
                $tab->push($additional = ListboxField::create(
145
                    'AdditionalWorkflowDefinitions',
146
                    _t('WorkflowApplicable.ADDITIONAL_WORKFLOW_DEFINITIONS', 'Additional Workflows')
147
                ));
148
                $additional->setSource($definitions);
149
            }
150
        }
151
152
        // Display the effective workflow definition.
153
154
        if ($effective = $this->getWorkflowInstance()) {
155
            $title = $effective->Definition()->Title;
156
            $tab->push(ReadonlyField::create(
157
                'EffectiveWorkflow',
158
                _t('WorkflowApplicable.EFFECTIVE_WORKFLOW', 'Effective Workflow'),
159
                $title
160
            ));
161
        }
162
163
        if ($this->owner->ID) {
164
            $config = new GridFieldConfig_Base();
165
            $config->addComponent(new GridFieldEditButton());
166
            $config->addComponent(new GridFieldDetailForm());
167
168
            $insts = $this->owner->WorkflowInstances();
169
            $log = new GridField(
170
                'WorkflowLog',
171
                _t('WorkflowApplicable.WORKFLOWLOG', 'Workflow Log'),
172
                $insts,
173
                $config
174
            );
175
176
            $tab->push($log);
177
        }
178
    }
179
180
    public function updateCMSActions(FieldList $actions)
181
    {
182
        $active = $this->getWorkflowService()->getWorkflowFor($this->owner);
183
        $c = Controller::curr();
184
        if ($c && $c->hasExtension(AdvancedWorkflowExtension::class)) {
185
            if ($active) {
186
                if ($this->canEditWorkflow()) {
187
                    $workflowOptions = new Tab(
188
                        'WorkflowOptions',
189
                        _t(
190
                            'SiteTree.WorkflowOptions',
191
                            'Workflow options',
192
                            'Expands a view for workflow specific buttons'
193
                        )
194
                    );
195
196
                    $menu = $actions->fieldByName('ActionMenus');
197
                    if (!$menu) {
198
                        // create the menu for adding to any arbitrary non-sitetree object
199
                        $menu = $this->createActionMenu();
200
                        $actions->push($menu);
201
                    }
202
203
                    if (!$actions->fieldByName('ActionMenus.WorkflowOptions')) {
204
                        $menu->push($workflowOptions);
205
                    }
206
207
                    $transitions = $active->CurrentAction()->getValidTransitions();
208
209
                    foreach ($transitions as $transition) {
210
                        if ($transition->canExecute($active)) {
211
                            $action = FormAction::create('updateworkflow-' . $transition->ID, $transition->Title)
212
                                ->setAttribute('data-transitionid', $transition->ID);
213
                            $workflowOptions->push($action);
214
                        }
215
                    }
216
217
                    // $action = FormAction::create('updateworkflow', $active->CurrentAction() ?
218
                    // $active->CurrentAction()->Title :
219
                    // _t('WorkflowApplicable.UPDATE_WORKFLOW', 'Update Workflow'))
220
                    //  ->setAttribute('data-icon', 'navigation');
221
222
                    // $actions->fieldByName('MajorActions') ?
223
                    // $actions->fieldByName('MajorActions')->push($action) :
224
                    // $actions->push($action);
225
                }
226
            } else {
227
                // Instantiate the workflow definition initial actions.
228
                $definitions = $this->getWorkflowService()->getDefinitionsFor($this->owner);
229
                if ($definitions) {
230
                    $menu = $actions->fieldByName('ActionMenus');
231
                    if (is_null($menu)) {
232
                        // Instantiate a new action menu for any data objects.
233
234
                        $menu = $this->createActionMenu();
235
                        $actions->push($menu);
236
                    }
237
                    $tab = Tab::create(
238
                        'AdditionalWorkflows'
239
                    );
240
                    $addedFirst = false;
241
                    foreach ($definitions as $definition) {
242
                        if ($definition->getInitialAction() && $this->owner->canEdit()) {
243
                            $action = FormAction::create(
244
                                "startworkflow-{$definition->ID}",
245
                                $definition->InitialActionButtonText ?
246
                                    $definition->InitialActionButtonText :
247
                                    $definition->getInitialAction()->Title
248
                            )
249
                                ->addExtraClass('start-workflow')
250
                                ->setAttribute('data-workflow', $definition->ID)
251
                                ->addExtraClass('btn-primary');
252
253
                            // The first element is the main workflow definition,
254
                            // and will be displayed as a major action.
255
                            if (!$addedFirst) {
256
                                $addedFirst = true;
257
                                $action->setAttribute('data-icon', 'navigation');
258
                                $majorActions = $actions->fieldByName('MajorActions');
259
                                $majorActions ? $majorActions->push($action) : $actions->push($action);
260
                            } else {
261
                                $tab->push($action);
262
                            }
263
                        }
264
                    }
265
                    // Only display menu if actions pushed to it
266
                    if ($tab->Fields()->exists()) {
267
                        $menu->insertBefore($tab, 'MoreOptions');
268
                    }
269
                }
270
            }
271
        }
272
    }
273
274
    protected function createActionMenu()
275
    {
276
        $rootTabSet = new TabSet('ActionMenus');
277
        $rootTabSet->addExtraClass('ss-ui-action-tabset action-menus');
278
        return $rootTabSet;
279
    }
280
281
    /**
282
     * Included in CMS-generated email templates for a NotifyUsersWorkflowAction.
283
     * Returns an absolute link to the CMS UI for a Page object
284
     *
285
     * @return string|null
286
     */
287
    public function AbsoluteEditLink()
288
    {
289
        $CMSEditLink = null;
290
291
        if ($this->owner instanceof CMSPreviewable) {
292
            $CMSEditLink = $this->owner->CMSEditLink();
293
        } elseif ($this->owner->hasMethod('WorkflowLink')) {
294
            $CMSEditLink = $this->owner->WorkflowLink();
295
        }
296
297
        if ($CMSEditLink === null) {
298
            return null;
299
        }
300
301
        return Controller::join_links(Director::absoluteBaseURL(), $CMSEditLink);
302
    }
303
304
    /**
305
     * Included in CMS-generated email templates for a NotifyUsersWorkflowAction.
306
     * Allows users to select a link in an email for direct access to the transition-selection dropdown in the CMS UI.
307
     *
308
     * @return string
309
     */
310
    public function LinkToPendingItems()
311
    {
312
        $urlBase = Director::absoluteBaseURL();
313
        $urlFrag = 'admin/workflows/WorkflowDefinition/EditForm/field';
314
        $urlInst = $this->getWorkflowInstance();
315
        return Controller::join_links($urlBase, $urlFrag, 'PendingObjects', 'item', $urlInst->ID, 'edit');
316
    }
317
318
    /**
319
     * After a workflow item is written, we notify the
320
     * workflow so that it can take action if needbe
321
     */
322
    public function onAfterWrite()
323
    {
324
        $instance = $this->getWorkflowInstance();
325
        if ($instance && $instance->CurrentActionID) {
326
            $action = $instance->CurrentAction()->BaseAction()->targetUpdated($instance);
327
        }
328
    }
329
330
    public function WorkflowInstances()
331
    {
332
        return WorkflowInstance::get()->filter([
333
            'TargetClass' => $this->owner->baseClass(),
334
            'TargetID' => $this->owner->ID
335
        ]);
336
    }
337
338
    /**
339
     * Gets the current instance of workflow
340
     *
341
     * @return WorkflowInstance
342
     */
343
    public function getWorkflowInstance()
344
    {
345
        if (!$this->currentInstance) {
346
            $this->currentInstance = $this->getWorkflowService()->getWorkflowFor($this->owner);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->getWorkflowServic...rkflowFor($this->owner) can also be of type object<SilverStripe\ORM\DataObject>. However, the property $currentInstance is declared as type object<Symbiote\Advanced...jects\WorkflowInstance>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
347
        }
348
349
        return $this->currentInstance;
350
    }
351
352
353
    /**
354
     * Gets the history of a workflow instance
355
     *
356
     * @return DataList
357
     */
358
    public function getWorkflowHistory($limit = null)
359
    {
360
        return $this->getWorkflowService()->getWorkflowHistoryFor($this->owner, $limit);
361
    }
362
363
    /**
364
     * Check all recent WorkflowActionIntances and return the most recent one with a Comment
365
     *
366
     * @param int $limit
367
     * @return WorkflowActionInstance|null
368
     */
369
    public function RecentWorkflowComment($limit = 10)
370
    {
371
        if ($actions = $this->getWorkflowHistory($limit)) {
372
            foreach ($actions as $action) {
373
                if ($action->Comment != '') {
374
                    return $action;
375
                }
376
            }
377
        }
378
    }
379
380
    /**
381
     * Content can never be directly publishable if there's a workflow applied.
382
     *
383
     * If there's an active instance, then it 'might' be publishable
384
     */
385
    public function canPublish()
386
    {
387
        // Override any default behaviour, to allow queuedjobs to complete
388
        if ($this->isPublishJobRunning()) {
389
            return true;
390
        }
391
392
        if ($active = $this->getWorkflowInstance()) {
393
            $publish = $active->canPublishTarget($this->owner);
394
            if (!is_null($publish)) {
395
                return $publish;
396
            }
397
        }
398
399
        // use definition to determine if publishing directly is allowed
400
        $definition = $this->getWorkflowService()->getDefinitionFor($this->owner);
401
402
        if ($definition) {
403
            if (!Security::getCurrentUser()) {
404
                return false;
405
            }
406
            $member = Security::getCurrentUser();
407
408
            $canPublish = $definition->canWorkflowPublish($member, $this->owner);
409
410
            return $canPublish;
411
        }
412
    }
413
414
    /**
415
     * Can only edit content that's NOT in another person's content changeset
416
     *
417
     * @return bool
418
     */
419
    public function canEdit($member)
420
    {
421
        // Override any default behaviour, to allow queuedjobs to complete
422
        if ($this->isPublishJobRunning()) {
423
            return true;
424
        }
425
426
        if ($active = $this->getWorkflowInstance()) {
427
            return $active->canEditTarget();
428
        }
429
    }
430
431
    /**
432
     * Can a user edit the current workflow attached to this item?
433
     *
434
     * @return bool
435
     */
436
    public function canEditWorkflow()
437
    {
438
        $active = $this->getWorkflowInstance();
439
        if ($active) {
440
            return $active->canEdit();
441
        }
442
        return false;
443
    }
444
445
    /**
446
     * @param WorkflowService $workflowService
447
     * @return $this
448
     */
449
    public function setWorkflowService(WorkflowService $workflowService)
450
    {
451
        $this->workflowService = $workflowService;
452
        return $this;
453
    }
454
455
    /**
456
     * @return WorkflowService
457
     */
458
    public function getWorkflowService()
459
    {
460
        return $this->workflowService;
461
    }
462
}
463