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
Pull Request — master (#387)
by Simone
01:55
created

WorkflowApplicable::isShareLinkAction()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
cc 3
nc 2
nop 0
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 SilverStripe\ShareDraftContent\Extensions\ShareDraftContentControllerExtension;
25
use Symbiote\AdvancedWorkflow\DataObjects\WorkflowActionInstance;
26
use Symbiote\AdvancedWorkflow\DataObjects\WorkflowDefinition;
27
use Symbiote\AdvancedWorkflow\DataObjects\WorkflowInstance;
28
use Symbiote\AdvancedWorkflow\Services\WorkflowService;
29
use Symbiote\QueuedJobs\Services\AbstractQueuedJob;
30
31
/**
32
 * DataObjects that have the WorkflowApplicable extension can have a
33
 * workflow definition applied to them. At some point, the workflow definition is then
34
 * triggered.
35
 *
36
 * @author  [email protected]
37
 * @license BSD License (http://silverstripe.org/bsd-license/)
38
 * @package advancedworkflow
39
 */
40
class WorkflowApplicable extends DataExtension
41
{
42
    private static $has_one = [
43
        'WorkflowDefinition' => WorkflowDefinition::class,
44
    ];
45
46
    private static $many_many = [
47
        'AdditionalWorkflowDefinitions' => WorkflowDefinition::class
48
    ];
49
50
    private static $dependencies = [
51
        'workflowService' => '%$' . WorkflowService::class,
52
    ];
53
54
    /**
55
     *
56
     * Used to flag to this extension if there's a WorkflowPublishTargetJob running.
57
     * @var boolean
58
     */
59
    public $isPublishJobRunning = false;
60
61
    /**
62
     *
63
     * @param boolean $truth
64
     */
65
    public function setIsPublishJobRunning($truth)
66
    {
67
        $this->isPublishJobRunning = $truth;
68
    }
69
70
    /**
71
     *
72
     * @return boolean
73
     */
74
    public function getIsPublishJobRunning()
75
    {
76
        return $this->isPublishJobRunning;
77
    }
78
79
    /**
80
     *
81
     * @see {@link $this->isPublishJobRunning}
82
     * @return boolean
83
     */
84
    public function isPublishJobRunning()
85
    {
86
        $propIsSet = (bool) $this->getIsPublishJobRunning();
87
        return class_exists(AbstractQueuedJob::class) && $propIsSet;
88
    }
89
90
    /**
91
     * @var WorkflowService
92
     */
93
    public $workflowService;
94
95
    /**
96
     *
97
     * A cache var for the current workflow instance
98
     *
99
     * @var WorkflowInstance
100
     */
101
    protected $currentInstance;
102
103
    public function updateSettingsFields(FieldList $fields)
104
    {
105
        $this->updateFields($fields);
106
    }
107
108
    public function updateCMSFields(FieldList $fields)
109
    {
110
        if (!$this->owner->hasMethod('getSettingsFields')) {
111
            $this->updateFields($fields);
112
        }
113
114
        // Instantiate a hidden form field to pass the triggered workflow definition through,
115
        // allowing a dynamic form action.
116
117
        $fields->push(HiddenField::create(
118
            'TriggeredWorkflowID'
119
        ));
120
    }
121
122
    public function updateFields(FieldList $fields)
123
    {
124
        if (!$this->owner->ID) {
125
            return $fields;
126
        }
127
128
        $tab = $fields->fieldByName('Root') ? $fields->findOrMakeTab('Root.Workflow') : $fields;
129
130
        if (Permission::check('APPLY_WORKFLOW')) {
131
            $definition = new DropdownField(
132
                'WorkflowDefinitionID',
133
                _t('WorkflowApplicable.DEFINITION', 'Applied Workflow')
134
            );
135
            $definitions = $this->getWorkflowService()->getDefinitions()->map()->toArray();
136
            $definition->setSource($definitions);
137
            $definition->setEmptyString(_t('WorkflowApplicable.INHERIT', 'Inherit from parent'));
138
            $tab->push($definition);
139
140
            // Allow an optional selection of additional workflow definitions.
141
142
            if ($this->owner->WorkflowDefinitionID) {
143
                $fields->removeByName('AdditionalWorkflowDefinitions');
144
                unset($definitions[$this->owner->WorkflowDefinitionID]);
145
                $tab->push($additional = ListboxField::create(
146
                    'AdditionalWorkflowDefinitions',
147
                    _t('WorkflowApplicable.ADDITIONAL_WORKFLOW_DEFINITIONS', 'Additional Workflows')
148
                ));
149
                $additional->setSource($definitions);
150
            }
151
        }
152
153
        // Display the effective workflow definition.
154
155
        if ($effective = $this->getWorkflowInstance()) {
156
            $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...
157
            $tab->push(ReadonlyField::create(
158
                'EffectiveWorkflow',
159
                _t('WorkflowApplicable.EFFECTIVE_WORKFLOW', 'Effective Workflow'),
160
                $title
161
            ));
162
        }
163
164
        if ($this->owner->ID) {
165
            $config = new GridFieldConfig_Base();
166
            $config->addComponent(new GridFieldEditButton());
167
            $config->addComponent(new GridFieldDetailForm());
168
169
            $insts = $this->owner->WorkflowInstances();
170
            $log = new GridField(
171
                'WorkflowLog',
172
                _t('WorkflowApplicable.WORKFLOWLOG', 'Workflow Log'),
173
                $insts,
174
                $config
175
            );
176
177
            $tab->push($log);
178
        }
179
    }
180
181
    public function updateCMSActions(FieldList $actions)
182
    {
183
        $active = $this->getWorkflowService()->getWorkflowFor($this->owner);
184
        $c = Controller::curr();
185
        if ($c && $c->hasExtension(AdvancedWorkflowExtension::class) && !$this->owner->isArchived()) {
186
            if ($active) {
187
                if ($this->canEditWorkflow()) {
188
                    $workflowOptions = new Tab(
189
                        'WorkflowOptions',
190
                        _t(
191
                            'SiteTree.WorkflowOptions',
192
                            'Workflow options',
193
                            'Expands a view for workflow specific buttons'
194
                        )
195
                    );
196
197
                    $menu = $actions->fieldByName('ActionMenus');
198
                    if (!$menu) {
199
                        // create the menu for adding to any arbitrary non-sitetree object
200
                        $menu = $this->createActionMenu();
201
                        $actions->push($menu);
202
                    }
203
204
                    if (!$actions->fieldByName('ActionMenus.WorkflowOptions')) {
205
                        $menu->push($workflowOptions);
206
                    }
207
208
                    $transitions = $active->CurrentAction()->getValidTransitions();
209
210
                    foreach ($transitions as $transition) {
211
                        if ($transition->canExecute($active)) {
212
                            $action = FormAction::create('updateworkflow-' . $transition->ID, $transition->Title)
213
                                ->setAttribute('data-transitionid', $transition->ID);
214
                            $workflowOptions->push($action);
215
                        }
216
                    }
217
218
                    // $action = FormAction::create('updateworkflow', $active->CurrentAction() ?
219
                    // $active->CurrentAction()->Title :
220
                    // _t('WorkflowApplicable.UPDATE_WORKFLOW', 'Update Workflow'))
221
                    //  ->setAttribute('data-icon', 'navigation');
222
223
                    // $actions->fieldByName('MajorActions') ?
224
                    // $actions->fieldByName('MajorActions')->push($action) :
225
                    // $actions->push($action);
226
                }
227
            } else {
228
                // Instantiate the workflow definition initial actions.
229
                $definitions = $this->getWorkflowService()->getDefinitionsFor($this->owner);
230
                if ($definitions) {
231
                    $menu = $actions->fieldByName('ActionMenus');
232
                    if (is_null($menu)) {
233
                        // Instantiate a new action menu for any data objects.
234
235
                        $menu = $this->createActionMenu();
236
                        $actions->push($menu);
237
                    }
238
                    $tab = Tab::create(
239
                        'AdditionalWorkflows'
240
                    );
241
                    $addedFirst = false;
242
                    foreach ($definitions as $definition) {
243
                        if ($definition->getInitialAction() && $this->owner->canEdit()) {
244
                            $action = FormAction::create(
245
                                "startworkflow-{$definition->ID}",
246
                                $definition->InitialActionButtonText ?
247
                                    $definition->InitialActionButtonText :
248
                                    $definition->getInitialAction()->Title
249
                            )
250
                                ->addExtraClass('start-workflow')
251
                                ->setAttribute('data-workflow', $definition->ID)
252
                                ->addExtraClass('btn-primary');
253
254
                            // The first element is the main workflow definition,
255
                            // and will be displayed as a major action.
256
                            if (!$addedFirst) {
257
                                $addedFirst = true;
258
                                $action->setAttribute('data-icon', 'navigation');
259
                                $majorActions = $actions->fieldByName('MajorActions');
260
                                $majorActions ? $majorActions->push($action) : $actions->push($action);
261
                            } else {
262
                                $tab->push($action);
263
                            }
264
                        }
265
                    }
266
                    // Only display menu if actions pushed to it
267
                    if ($tab->Fields()->exists()) {
268
                        $menu->insertBefore($tab, 'MoreOptions');
269
                    }
270
                }
271
            }
272
        }
273
    }
274
275
    protected function createActionMenu()
276
    {
277
        $rootTabSet = new TabSet('ActionMenus');
278
        $rootTabSet->addExtraClass('ss-ui-action-tabset action-menus');
279
        return $rootTabSet;
280
    }
281
282
    /**
283
     * Included in CMS-generated email templates for a NotifyUsersWorkflowAction.
284
     * Returns an absolute link to the CMS UI for a Page object
285
     *
286
     * @return string|null
287
     */
288
    public function AbsoluteEditLink()
289
    {
290
        $CMSEditLink = null;
291
292
        if ($this->owner instanceof CMSPreviewable) {
293
            $CMSEditLink = $this->owner->CMSEditLink();
294
        } elseif ($this->owner->hasMethod('WorkflowLink')) {
295
            $CMSEditLink = $this->owner->WorkflowLink();
296
        }
297
298
        if ($CMSEditLink === null) {
299
            return null;
300
        }
301
302
        return Controller::join_links(Director::absoluteBaseURL(), $CMSEditLink);
303
    }
304
305
    /**
306
     * Included in CMS-generated email templates for a NotifyUsersWorkflowAction.
307
     * Allows users to select a link in an email for direct access to the transition-selection dropdown in the CMS UI.
308
     *
309
     * @return string
310
     */
311
    public function LinkToPendingItems()
312
    {
313
        $urlBase = Director::absoluteBaseURL();
314
        $urlFrag = 'admin/workflows/WorkflowDefinition/EditForm/field';
315
        $urlInst = $this->getWorkflowInstance();
316
        return Controller::join_links($urlBase, $urlFrag, 'PendingObjects', 'item', $urlInst->ID, 'edit');
317
    }
318
319
    /**
320
     * After a workflow item is written, we notify the
321
     * workflow so that it can take action if needbe
322
     */
323
    public function onAfterWrite()
324
    {
325
        $instance = $this->getWorkflowInstance();
326
        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...
327
            $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...
328
        }
329
    }
330
331
    public function WorkflowInstances()
332
    {
333
        return WorkflowInstance::get()->filter([
334
            'TargetClass' => $this->owner->baseClass(),
335
            'TargetID' => $this->owner->ID
336
        ]);
337
    }
338
339
    /**
340
     * Gets the current instance of workflow
341
     *
342
     * @return WorkflowInstance
343
     */
344
    public function getWorkflowInstance()
345
    {
346
        if (!$this->currentInstance) {
347
            $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...
348
        }
349
350
        return $this->currentInstance;
351
    }
352
353
354
    /**
355
     * Gets the history of a workflow instance
356
     *
357
     * @return DataList
358
     */
359
    public function getWorkflowHistory($limit = null)
360
    {
361
        return $this->getWorkflowService()->getWorkflowHistoryFor($this->owner, $limit);
362
    }
363
364
    /**
365
     * Check all recent WorkflowActionIntances and return the most recent one with a Comment
366
     *
367
     * @param int $limit
368
     * @return WorkflowActionInstance|null
369
     */
370
    public function RecentWorkflowComment($limit = 10)
371
    {
372
        if ($actions = $this->getWorkflowHistory($limit)) {
373
            foreach ($actions as $action) {
374
                if ($action->Comment != '') {
375
                    return $action;
376
                }
377
            }
378
        }
379
    }
380
381
    /**
382
     * Content can never be directly publishable if there's a workflow applied.
383
     *
384
     * If there's an active instance, then it 'might' be publishable
385
     */
386
    public function canPublish()
387
    {
388
        // Override any default behaviour, to allow queuedjobs to complete
389
        if ($this->isPublishJobRunning()) {
390
            return true;
391
        }
392
393
        if ($active = $this->getWorkflowInstance()) {
394
            $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...
395
            if (!is_null($publish)) {
396
                return $publish;
397
            }
398
        }
399
400
        // use definition to determine if publishing directly is allowed
401
        $definition = $this->getWorkflowService()->getDefinitionFor($this->owner);
402
403
        if ($definition) {
404
            if (!Security::getCurrentUser()) {
405
                return false;
406
            }
407
            $member = Security::getCurrentUser();
408
409
            $canPublish = $definition->canWorkflowPublish($member, $this->owner);
410
411
            return $canPublish;
412
        }
413
    }
414
415
    /**
416
     * Can only edit content that's NOT in another person's content changeset
417
     *
418
     * @return bool
419
     */
420
    public function canEdit($member)
421
    {
422
        // Override any default behaviour, to allow queuedjobs to complete
423
        if ($this->isPublishJobRunning()) {
424
            return true;
425
        }
426
427
        // Override any default behaviour, to allow the draft link generation
428
        if ($this->isShareLinkAction()) {
429
            return true;
430
        }
431
432
        if ($active = $this->getWorkflowInstance()) {
433
            return $active->canEditTarget();
434
        }
435
    }
436
437
    /**
438
     * Can a user edit the current workflow attached to this item?
439
     *
440
     * @return bool
441
     */
442
    public function canEditWorkflow()
443
    {
444
        $active = $this->getWorkflowInstance();
445
        if ($active) {
446
            return $active->canEdit();
447
        }
448
        return false;
449
    }
450
451
    /**
452
     * @param WorkflowService $workflowService
453
     * @return $this
454
     */
455
    public function setWorkflowService(WorkflowService $workflowService)
456
    {
457
        $this->workflowService = $workflowService;
458
        return $this;
459
    }
460
461
    /**
462
     * @return WorkflowService
463
     */
464
    public function getWorkflowService()
465
    {
466
        return $this->workflowService;
467
    }
468
469
    /**
470
     * @return bool
471
     */
472
    public function isShareLinkAction()
473
    {
474
        $currentController = Controller::curr();
475
476
        if ($currentController->hasExtension(ShareDraftContentControllerExtension::class) && $currentController->getAction() == 'MakeShareDraftLink') {
477
            return true;
478
        }
479
480
        return false;
481
    }
482
}
483