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 ( 6956e2...9a55f0 )
by
unknown
02:08
created

QueuedJobDescriptor::getCMSFields()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 40

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 40
rs 9.28
c 1
b 0
f 0
cc 3
nc 4
nop 0
1
<?php
2
3
namespace Symbiote\QueuedJobs\DataObjects;
4
5
use SilverStripe\Assets\Filesystem;
6
use SilverStripe\Core\Config\Config;
7
use SilverStripe\Core\Convert;
8
use SilverStripe\Forms\DropdownField;
9
use SilverStripe\Forms\LiteralField;
10
use SilverStripe\ORM\DataObject;
11
use SilverStripe\ORM\FieldType\DBField;
12
use SilverStripe\Security\Permission;
13
use Symbiote\QueuedJobs\Services\QueuedJob;
14
use Symbiote\QueuedJobs\Services\QueuedJobService;
15
16
/**
17
 * A QueuedJobDescriptor is the stored representation of a piece of work that could take a while to execute,
18
 * because of which it is desireable to not have it executing in parallel to other jobs.
19
 *
20
 * A queued job should always attempt to report how many potential dataobjects will be affected by being executed;
21
 * this will determine which queue it is placed within so that some shorter jobs can execute immediately without needing
22
 * to wait for a potentially long running job.
23
 *
24
 * @property string $JobTitle Name of job
25
 * @property string $Signature Unique identifier for this job instance
26
 * @property string $Implementation Classname of underlying job
27
 * @property string $StartAfter Don't start until this date, if set
28
 * @property string $JobStarted When this job was started
29
 * @property string $JobFinished When this job was finished
30
 * @property int $TotalSteps Number of steps
31
 * @property int $StepsProcessed Number of completed steps
32
 * @property int $LastProcessedCount Number at which StepsProcessed was last checked for stalled jobs
33
 * @property int $ResumeCounts Number of times this job has been resumed
34
 * @property string $SavedJobData serialised data for the job to use as storage
35
 * @property string $SavedJobMessages List of messages saved for this job
36
 * @property string $JobStatus Status of this job
37
 * @property string $JobType Type of job
38
 *
39
 * @method Member RunAs() Member to run this job as
40
 *
41
 * @author Marcus Nyeholt <[email protected]>
42
 * @license BSD http://silverstripe.org/bsd-license/
43
 */
44
class QueuedJobDescriptor extends DataObject
45
{
46
    /**
47
     * {@inheritDoc}
48
     * @var string
49
     */
50
    private static $table_name = 'QueuedJobDescriptor';
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $table_name 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...
51
52
    /**
53
     * @var array
54
     */
55
    private static $db = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $db 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...
56
        'JobTitle' => 'Varchar(255)',
57
        'Signature' => 'Varchar(64)',
58
        'Implementation' => 'Varchar(255)',
59
        'StartAfter' => 'DBDatetime',
60
        'JobStarted' => 'DBDatetime',
61
        'JobRestarted' => 'DBDatetime',
62
        'JobFinished' => 'DBDatetime',
63
        'TotalSteps' => 'Int',
64
        'StepsProcessed' => 'Int',
65
        'LastProcessedCount' => 'Int(-1)', // -1 means never checked, 0 means checked but no work is done
66
        'ResumeCounts' => 'Int',
67
        'SavedJobData' => 'Text',
68
        'SavedJobMessages' => 'Text',
69
        'JobStatus' => 'Varchar(16)',
70
        'JobType' => 'Varchar(16)',
71
    ];
72
73
    /**
74
     * @var array
75
     */
76
    private static $has_one = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
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...
77
        'RunAs' => 'SilverStripe\\Security\\Member',
78
    ];
79
80
    /**
81
     * @var array
82
     */
83
    private static $defaults = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $defaults 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...
84
        'JobStatus' => 'New',
85
        'ResumeCounts' => 0,
86
        'LastProcessedCount' => -1 // -1 means never checked, 0 means checked and none were processed
87
    ];
88
89
    /**
90
     * @var array
91
     */
92
    private static $indexes = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $indexes 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...
93
        'JobStatus' => true,
94
        'StartAfter' => true,
95
        'Signature' => true,
96
    ];
97
98
    /**
99
     * @var array
100
     */
101
    private static $casting = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $casting 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...
102
        'Messages' => 'HTMLText',
103
    ];
104
105
    /**
106
     * @var array
107
     */
108
    private static $searchable_fields = [
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $searchable_fields 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...
109
        'JobTitle',
110
    ];
111
112
    /**
113
     * @var string
114
     */
115
    private static $default_sort = 'Created DESC';
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
Unused Code introduced by
The property $default_sort 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...
116
117
    public function requireDefaultRecords()
118
    {
119
        parent::requireDefaultRecords();
120
        $this->getJobDir();
121
    }
122
123
    /**
124
     * @return array
125
     */
126
    public function summaryFields()
127
    {
128
        return [
129
            'JobTitle' => _t(__CLASS__ . '.TABLE_TITLE', 'Title'),
130
            'Created' => _t(__CLASS__ . '.TABLE_ADDE', 'Added'),
131
            'JobStarted' => _t(__CLASS__ . '.TABLE_STARTED', 'Started'),
132
//          'JobRestarted' => _t(__CLASS__ . '.TABLE_RESUMED', 'Resumed'),
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...
133
            'StartAfter' => _t(__CLASS__ . '.TABLE_START_AFTER', 'Start After'),
134
            'JobType' => _t(__CLASS__ . '.JOB_TYPE', 'Job Type'),
135
            'JobStatus' => _t(__CLASS__ . '.TABLE_STATUS', 'Status'),
136
            'LastMessage' => _t(__CLASS__ . '.TABLE_MESSAGES', 'Message'),
137
            'StepsProcessed' => _t(__CLASS__ . '.TABLE_NUM_PROCESSED', 'Done'),
138
            'TotalSteps' => _t(__CLASS__ . '.TABLE_TOTAL', 'Total'),
139
        ];
140
    }
141
142
    /**
143
     * Pause this job, but only if it is waiting, running, or init
144
     *
145
     * @param bool $force Pause this job even if it's not waiting, running, or init
146
     *
147
     * @return bool Return true if this job was paused
148
     */
149
    public function pause($force = false)
150
    {
151
        if ($force || in_array(
152
            $this->JobStatus,
153
            [QueuedJob::STATUS_WAIT, QueuedJob::STATUS_RUN, QueuedJob::STATUS_INIT]
154
        )) {
155
            $this->JobStatus = QueuedJob::STATUS_PAUSED;
156
            $this->write();
157
            return true;
158
        }
159
        return false;
160
    }
161
162
    /**
163
     * Resume this job and schedules it for execution
164
     *
165
     * @param bool $force Resume this job even if it's not paused or broken
166
     *
167
     * @return bool Return true if this job was resumed
168
     */
169
    public function resume($force = false)
170
    {
171
        if ($force || in_array($this->JobStatus, [QueuedJob::STATUS_PAUSED, QueuedJob::STATUS_BROKEN])) {
172
            $this->JobStatus = QueuedJob::STATUS_WAIT;
173
            $this->ResumeCounts++;
174
            $this->write();
175
            singleton(QueuedJobService::class)->startJob($this);
176
            return true;
177
        }
178
        return false;
179
    }
180
181
    /**
182
     * Restarts this job via a forced resume
183
     */
184
    public function restart()
185
    {
186
        $this->resume(true);
187
    }
188
189
    /**
190
     * Called to indicate that the job is ready to be run on the queue. This is done either as the result of
191
     * creating the job and adding it, or when resuming.
192
     */
193
    public function activateOnQueue()
194
    {
195
        // if it's an immediate job, lets cache it to disk to be picked up later
196
        if ($this->JobType == QueuedJob::IMMEDIATE
197
            && !Config::inst()->get(QueuedJobService::class, 'use_shutdown_function')
198
        ) {
199
            touch($this->getJobDir() . '/queuedjob-' . $this->ID);
200
        }
201
    }
202
203
    /**
204
     * Gets the path to the queuedjob cache directory
205
     *
206
     * @return string
207
     */
208
    protected function getJobDir()
209
    {
210
        // make sure our temp dir is in place. This is what will be inotify watched
211
        $jobDir = Config::inst()->get(QueuedJobService::class, 'cache_dir');
212
        if ($jobDir{0} != '/') {
213
            $jobDir = TEMP_FOLDER . '/' . $jobDir;
214
        }
215
216
        if (!is_dir($jobDir)) {
217
            Filesystem::makeFolder($jobDir);
218
        }
219
        return $jobDir;
220
    }
221
222
    public function execute()
223
    {
224
        $service = singleton(QueuedJobService::class);
225
        $service->runJob($this->ID);
226
    }
227
228
    /**
229
     * Called when the job has completed and we want to cleanup anything the descriptor has lying around
230
     * in caches or the like.
231
     */
232
    public function cleanupJob()
233
    {
234
        // remove the job's temp file if it exists
235
        $tmpFile = $this->getJobDir() . '/queuedjob-' . $this->ID;
236
        if (file_exists($tmpFile)) {
237
            unlink($tmpFile);
238
        }
239
    }
240
241
    public function onBeforeDelete()
242
    {
243
        parent::onBeforeDelete();
244
        $this->cleanupJob();
245
    }
246
247
    /**
248
     * Get all job messages as an HTML unordered list.
249
     *
250
     * @return string|void
251
     */
252
    public function getMessages()
253
    {
254
        if (strlen($this->SavedJobMessages)) {
255
            $messages = @unserialize($this->SavedJobMessages);
256
            if (!empty($messages)) {
257
                return DBField::create_field('HTMLText', '<ul><li>' . nl2br(implode('</li><li>', Convert::raw2xml($messages))) . '</li></ul>');
258
            }
259
            return '';
260
        }
261
    }
262
263
    /**
264
     * Get the last job message as a raw string
265
     *
266
     * @return string|void
267
     */
268
    public function getLastMessage()
269
    {
270
        if (strlen($this->SavedJobMessages)) {
271
            $msgs = @unserialize($this->SavedJobMessages);
272
            if (is_array($msgs) && sizeof($msgs)) {
273
                $msg = array_pop($msgs);
274
                return $msg;
275
            }
276
        }
277
    }
278
279
    /**
280
     * @return string
281
     */
282
    public function getTitle()
283
    {
284
        return $this->JobTitle;
285
    }
286
287
    /**
288
     * @return FieldList
289
     */
290
    public function getCMSFields()
291
    {
292
        $fields = parent::getCMSFields();
293
        $fields->replaceField(
294
            'JobType',
295
            new DropdownField('JobType', $this->fieldLabel('JobType'), [
296
                QueuedJob::IMMEDIATE => 'Immediate',
297
                QueuedJob::QUEUED => 'Queued',
298
                QueuedJob::LARGE => 'Large',
299
            ])
300
        );
301
        $statuses = [
302
            QueuedJob::STATUS_NEW,
303
            QueuedJob::STATUS_INIT,
304
            QueuedJob::STATUS_RUN,
305
            QueuedJob::STATUS_WAIT,
306
            QueuedJob::STATUS_COMPLETE,
307
            QueuedJob::STATUS_PAUSED,
308
            QueuedJob::STATUS_CANCELLED,
309
            QueuedJob::STATUS_BROKEN,
310
        ];
311
        $fields->replaceField(
312
            'JobStatus',
313
            DropdownField::create('JobStatus', $this->fieldLabel('JobStatus'), array_combine($statuses, $statuses))
314
        );
315
316
        $fields->removeByName('SavedJobData');
317
        $fields->removeByName('SavedJobMessages');
318
319
        if (strlen($this->SavedJobMessages)) {
320
            $fields->addFieldToTab('Root.Messages', new LiteralField('Messages', $this->getMessages()));
0 ignored issues
show
Bug introduced by
It seems like $this->getMessages() targeting Symbiote\QueuedJobs\Data...scriptor::getMessages() can also be of type null or object<SilverStripe\ORM\FieldType\DBField>; however, SilverStripe\Forms\LiteralField::__construct() does only seem to accept string|object<SilverStripe\Forms\FormField>, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
321
        }
322
323
        if (Permission::check('ADMIN')) {
324
            return $fields;
325
        }
326
327
        // Readonly CMS view is a lot more useful for debugging than no view at all
328
        return $fields->makeReadonly();
329
    }
330
}
331