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 ( 99ec42...de979d )
by Robbie
9s
created

WorkflowApplicable::updateCMSActions()   D

Complexity

Conditions 18
Paths 35

Size

Total Lines 93
Code Lines 53

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 93
rs 4.7996
c 0
b 0
f 0
cc 18
eloc 53
nc 35
nop 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Security\Permission;
22
use SilverStripe\Security\Security;
23
use Symbiote\AdvancedWorkflow\DataObjects\WorkflowDefinition;
24
use Symbiote\AdvancedWorkflow\DataObjects\WorkflowInstance;
25
use Symbiote\AdvancedWorkflow\Services\WorkflowService;
26
use Symbiote\QueuedJobs\Service\AbstractQueuedJob;
27
28
/**
29
 * DataObjects that have the WorkflowApplicable extension can have a
30
 * workflow definition applied to them. At some point, the workflow definition is then
31
 * triggered.
32
 *
33
 * @author  [email protected]
34
 * @license BSD License (http://silverstripe.org/bsd-license/)
35
 * @package advancedworkflow
36
 */
37
class WorkflowApplicable extends DataExtension
38
{
39
    private static $has_one = [
0 ignored issues
show
Unused Code introduced by
The property $has_one is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
40
        'WorkflowDefinition' => WorkflowDefinition::class,
41
    ];
42
43
    private static $many_many = [
0 ignored issues
show
Unused Code introduced by
The property $many_many is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
44
        'AdditionalWorkflowDefinitions' => WorkflowDefinition::class
45
    ];
46
47
    private static $dependencies = [
0 ignored issues
show
Unused Code introduced by
The property $dependencies is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
48
        'workflowService' => '%$' . WorkflowService::class,
49
    ];
50
51
    /**
52
     *
53
     * Used to flag to this extension if there's a WorkflowPublishTargetJob running.
54
     * @var boolean
55
     */
56
    public $isPublishJobRunning = false;
57
58
    /**
59
     *
60
     * @param boolean $truth
61
     */
62
    public function setIsPublishJobRunning($truth)
63
    {
64
        $this->isPublishJobRunning = $truth;
65
    }
66
67
    /**
68
     *
69
     * @return boolean
70
     */
71
    public function getIsPublishJobRunning()
72
    {
73
        return $this->isPublishJobRunning;
74
    }
75
76
    /**
77
     *
78
     * @see {@link $this->isPublishJobRunning}
79
     * @return boolean
80
     */
81
    public function isPublishJobRunning()
82
    {
83
        $propIsSet = $this->getIsPublishJobRunning() ? true : false;
84
        return class_exists(AbstractQueuedJob::class) && $propIsSet;
85
    }
86
87
    /**
88
     * @var WorkflowService
89
     */
90
    public $workflowService;
91
92
    /**
93
     *
94
     * A cache var for the current workflow instance
95
     *
96
     * @var WorkflowInstance
97
     */
98
    protected $currentInstance;
99
100
    public function updateSettingsFields(FieldList $fields)
101
    {
102
        $this->updateFields($fields);
103
    }
104
105
    public function updateCMSFields(FieldList $fields)
106
    {
107
        if (!$this->owner->hasMethod('getSettingsFields')) {
108
            $this->updateFields($fields);
109
        }
110
111
        // Instantiate a hidden form field to pass the triggered workflow definition through,
112
        // allowing a dynamic form action.
113
114
        $fields->push(HiddenField::create(
115
            'TriggeredWorkflowID'
116
        ));
117
    }
118
119
    public function updateFields(FieldList $fields)
120
    {
121
        if (!$this->owner->ID) {
122
            return $fields;
123
        }
124
125
        $tab = $fields->fieldByName('Root') ? $fields->findOrMakeTab('Root.Workflow') : $fields;
126
127
        if (Permission::check('APPLY_WORKFLOW')) {
128
            $definition = new DropdownField(
129
                'WorkflowDefinitionID',
130
                _t('WorkflowApplicable.DEFINITION', 'Applied Workflow')
131
            );
132
            $definitions = $this->workflowService->getDefinitions()->map()->toArray();
133
            $definition->setSource($definitions);
134
            $definition->setEmptyString(_t('WorkflowApplicable.INHERIT', 'Inherit from parent'));
135
            $tab->push($definition);
136
137
            // Allow an optional selection of additional workflow definitions.
138
139
            if ($this->owner->WorkflowDefinitionID) {
140
                $fields->removeByName('AdditionalWorkflowDefinitions');
141
                unset($definitions[$this->owner->WorkflowDefinitionID]);
142
                $tab->push($additional = ListboxField::create(
143
                    'AdditionalWorkflowDefinitions',
144
                    _t('WorkflowApplicable.ADDITIONAL_WORKFLOW_DEFINITIONS', 'Additional Workflows')
145
                ));
146
                $additional->setSource($definitions);
147
            }
148
        }
149
150
        // Display the effective workflow definition.
151
152
        if ($effective = $this->getWorkflowInstance()) {
153
            $title = $effective->Definition()->Title;
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<Symbiote\Advanced...cts\WorkflowDefinition>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
154
            $tab->push(ReadonlyField::create(
155
                'EffectiveWorkflow',
156
                _t('WorkflowApplicable.EFFECTIVE_WORKFLOW', 'Effective Workflow'),
157
                $title
158
            ));
159
        }
160
161
        if ($this->owner->ID) {
162
            $config = new GridFieldConfig_Base();
163
            $config->addComponent(new GridFieldEditButton());
164
            $config->addComponent(new GridFieldDetailForm());
165
166
            $insts = $this->owner->WorkflowInstances();
167
            $log = new GridField(
168
                'WorkflowLog',
169
                _t('WorkflowApplicable.WORKFLOWLOG', 'Workflow Log'),
170
                $insts,
171
                $config
172
            );
173
174
            $tab->push($log);
175
        }
176
    }
177
178
    public function updateCMSActions(FieldList $actions)
179
    {
180
        $active = $this->workflowService->getWorkflowFor($this->owner);
181
        $c = Controller::curr();
182
        if ($c && $c->hasExtension(AdvancedWorkflowExtension::class)) {
183
            if ($active) {
184
                if ($this->canEditWorkflow()) {
185
                    $workflowOptions = new Tab(
186
                        'WorkflowOptions',
187
                        _t(
188
                            'SiteTree.WorkflowOptions',
189
                            'Workflow options',
190
                            'Expands a view for workflow specific buttons'
191
                        )
192
                    );
193
194
                    $menu = $actions->fieldByName('ActionMenus');
195
                    if (!$menu) {
196
                        // create the menu for adding to any arbitrary non-sitetree object
197
                        $menu = $this->createActionMenu();
198
                        $actions->push($menu);
199
                    }
200
201
                    if (!$actions->fieldByName('ActionMenus.WorkflowOptions')) {
202
                        $menu->push($workflowOptions);
203
                    }
204
205
                    $transitions = $active->CurrentAction()->getValidTransitions();
206
207
                    foreach ($transitions as $transition) {
208
                        if ($transition->canExecute($active)) {
209
                            $action = FormAction::create('updateworkflow-' . $transition->ID, $transition->Title)
210
                                ->setAttribute('data-transitionid', $transition->ID);
211
                            $workflowOptions->push($action);
212
                        }
213
                    }
214
215
                    // $action = FormAction::create('updateworkflow', $active->CurrentAction() ?
0 ignored issues
show
Unused Code Comprehensibility introduced by
53% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
216
                    // $active->CurrentAction()->Title :
0 ignored issues
show
Unused Code Comprehensibility introduced by
60% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
217
                    // _t('WorkflowApplicable.UPDATE_WORKFLOW', 'Update Workflow'))
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
218
                    //  ->setAttribute('data-icon', 'navigation');
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
219
220
                    // $actions->fieldByName('MajorActions') ?
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
221
                    // $actions->fieldByName('MajorActions')->push($action) :
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
222
                    // $actions->push($action);
0 ignored issues
show
Unused Code Comprehensibility introduced by
75% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
223
                }
224
            } else {
225
                // Instantiate the workflow definition initial actions.
226
                $definitions = $this->workflowService->getDefinitionsFor($this->owner);
227
                if ($definitions) {
228
                    $menu = $actions->fieldByName('ActionMenus');
229
                    if (is_null($menu)) {
230
                        // Instantiate a new action menu for any data objects.
231
232
                        $menu = $this->createActionMenu();
233
                        $actions->push($menu);
234
                    }
235
                    $tab = Tab::create(
236
                        'AdditionalWorkflows'
237
                    );
238
                    $addedFirst = false;
239
                    foreach ($definitions as $definition) {
240
                        if ($definition->getInitialAction() && $this->owner->canEdit()) {
241
                            $action = FormAction::create(
242
                                "startworkflow-{$definition->ID}",
243
                                $definition->InitialActionButtonText ?
244
                                    $definition->InitialActionButtonText :
245
                                    $definition->getInitialAction()->Title
246
                            )
247
                                ->addExtraClass('start-workflow')
248
                                ->setAttribute('data-workflow', $definition->ID)
249
                                ->addExtraClass('btn-primary');
250
251
                            // The first element is the main workflow definition,
252
                            // and will be displayed as a major action.
253
                            if (!$addedFirst) {
254
                                $addedFirst = true;
255
                                $action->setAttribute('data-icon', 'navigation');
256
                                $majorActions = $actions->fieldByName('MajorActions');
257
                                $majorActions ? $majorActions->push($action) : $actions->push($action);
258
                            } else {
259
                                $tab->push($action);
260
                            }
261
                        }
262
                    }
263
                    // Only display menu if actions pushed to it
264
                    if ($tab->Fields()->exists()) {
265
                        $menu->insertBefore($tab, 'MoreOptions');
266
                    }
267
                }
268
            }
269
        }
270
    }
271
272
    protected function createActionMenu()
273
    {
274
        $rootTabSet = new TabSet('ActionMenus');
275
        $rootTabSet->addExtraClass('ss-ui-action-tabset action-menus');
276
        return $rootTabSet;
277
    }
278
279
    /**
280
     * Included in CMS-generated email templates for a NotifyUsersWorkflowAction.
281
     * Returns an absolute link to the CMS UI for a Page object
282
     *
283
     * @return string|null
284
     */
285
    public function AbsoluteEditLink()
286
    {
287
        $CMSEditLink = null;
288
289
        if ($this->owner instanceof CMSPreviewable) {
290
            $CMSEditLink = $this->owner->CMSEditLink();
291
        } elseif ($this->owner->hasMethod('WorkflowLink')) {
292
            $CMSEditLink = $this->owner->WorkflowLink();
293
        }
294
295
        if ($CMSEditLink === null) {
296
            return null;
297
        }
298
299
        return Controller::join_links(Director::absoluteBaseURL(), $CMSEditLink);
300
    }
301
302
    /**
303
     * Included in CMS-generated email templates for a NotifyUsersWorkflowAction.
304
     * Allows users to select a link in an email for direct access to the transition-selection dropdown in the CMS UI.
305
     *
306
     * @return string
307
     */
308
    public function LinkToPendingItems()
309
    {
310
        $urlBase = Director::absoluteBaseURL();
311
        $urlFrag = 'admin/workflows/WorkflowDefinition/EditForm/field';
312
        $urlInst = $this->getWorkflowInstance();
313
        return Controller::join_links($urlBase, $urlFrag, 'PendingObjects', 'item', $urlInst->ID, 'edit');
314
    }
315
316
    /**
317
     * After a workflow item is written, we notify the
318
     * workflow so that it can take action if needbe
319
     */
320
    public function onAfterWrite()
321
    {
322
        $instance = $this->getWorkflowInstance();
323
        if ($instance && $instance->CurrentActionID) {
0 ignored issues
show
Documentation introduced by
The property CurrentActionID does not exist on object<Symbiote\Advanced...jects\WorkflowInstance>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
324
            $action = $instance->CurrentAction()->BaseAction()->targetUpdated($instance);
0 ignored issues
show
Documentation Bug introduced by
The method BaseAction does not exist on object<Symbiote\Advanced...WorkflowActionInstance>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
Unused Code introduced by
$action is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
325
        }
326
    }
327
328
    public function WorkflowInstances()
329
    {
330
        return WorkflowInstance::get()->filter([
331
            'TargetClass' => $this->owner->baseClass(),
332
            'TargetID' => $this->owner->ID
333
        ]);
334
    }
335
336
    /**
337
     * Gets the current instance of workflow
338
     *
339
     * @return WorkflowInstance
340
     */
341
    public function getWorkflowInstance()
342
    {
343
        if (!$this->currentInstance) {
344
            $this->currentInstance = $this->workflowService->getWorkflowFor($this->owner);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->workflowService->...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...
345
        }
346
347
        return $this->currentInstance;
348
    }
349
350
351
    /**
352
     * Gets the history of a workflow instance
353
     *
354
     * @return DataObjectSet
355
     */
356
    public function getWorkflowHistory($limit = null)
357
    {
358
        return $this->workflowService->getWorkflowHistoryFor($this->owner, $limit);
359
    }
360
361
    /**
362
     * Check all recent WorkflowActionIntances and return the most recent one with a Comment
363
     *
364
     * @param int $limit
365
     * @return WorkflowActionInstance|null
366
     */
367
    public function RecentWorkflowComment($limit = 10)
368
    {
369
        if ($actions = $this->getWorkflowHistory($limit)) {
370
            foreach ($actions as $action) {
371
                if ($action->Comment != '') {
372
                    return $action;
373
                }
374
            }
375
        }
376
    }
377
378
    /**
379
     * Content can never be directly publishable if there's a workflow applied.
380
     *
381
     * If there's an active instance, then it 'might' be publishable
382
     */
383
    public function canPublish()
384
    {
385
        // Override any default behaviour, to allow queuedjobs to complete
386
        if ($this->isPublishJobRunning()) {
387
            return true;
388
        }
389
390
        if ($active = $this->getWorkflowInstance()) {
391
            $publish = $active->canPublishTarget($this->owner);
0 ignored issues
show
Unused Code introduced by
The call to WorkflowInstance::canPublishTarget() has too many arguments starting with $this->owner.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
392
            if (!is_null($publish)) {
393
                return $publish;
394
            }
395
        }
396
397
        // use definition to determine if publishing directly is allowed
398
        $definition = $this->workflowService->getDefinitionFor($this->owner);
399
400
        if ($definition) {
401
            if (!Security::getCurrentUser()) {
402
                return false;
403
            }
404
            $member = Security::getCurrentUser();
405
406
            $canPublish = $definition->canWorkflowPublish($member, $this->owner);
407
408
            return $canPublish;
409
        }
410
    }
411
412
    /**
413
     * Can only edit content that's NOT in another person's content changeset
414
     */
415
    public function canEdit($member)
416
    {
417
        // Override any default behaviour, to allow queuedjobs to complete
418
        if ($this->isPublishJobRunning()) {
419
            return true;
420
        }
421
422
        if ($active = $this->getWorkflowInstance()) {
423
            return $active->canEditTarget($this->owner);
0 ignored issues
show
Unused Code introduced by
The call to WorkflowInstance::canEditTarget() has too many arguments starting with $this->owner.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
424
        }
425
    }
426
427
    /**
428
     * Can a user edit the current workflow attached to this item?
429
     */
430
    public function canEditWorkflow()
431
    {
432
        $active = $this->getWorkflowInstance();
433
        if ($active) {
434
            return $active->canEdit();
435
        }
436
        return false;
437
    }
438
}
439