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 ( ad63e1...95a104 )
by Robbie
15s
created

QueuedJobDescriptor::getJobTypeString()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 2
nc 2
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...
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...
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...
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...
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...
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...
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...
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...
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
            'JobFinished' => _t(__CLASS__ . '.TABLE_FINISHED', 'Finished'),
133
//          'JobRestarted' => _t(__CLASS__ . '.TABLE_RESUMED', 'Resumed'),
134
            'StartAfter' => _t(__CLASS__ . '.TABLE_START_AFTER', 'Start After'),
135
            'JobTypeString' => _t(__CLASS__ . '.JOB_TYPE', 'Job Type'),
136
            'JobStatus' => _t(__CLASS__ . '.TABLE_STATUS', 'Status'),
137
            'LastMessage' => _t(__CLASS__ . '.TABLE_MESSAGES', 'Message'),
138
            'StepsProcessed' => _t(__CLASS__ . '.TABLE_NUM_PROCESSED', 'Done'),
139
            'TotalSteps' => _t(__CLASS__ . '.TABLE_TOTAL', 'Total'),
140
        ];
141
    }
142
143
    /**
144
     * Pause this job, but only if it is waiting, running, or init
145
     *
146
     * @param bool $force Pause this job even if it's not waiting, running, or init
147
     *
148
     * @return bool Return true if this job was paused
149
     */
150
    public function pause($force = false)
151
    {
152
        if ($force || in_array(
153
            $this->JobStatus,
154
            [QueuedJob::STATUS_WAIT, QueuedJob::STATUS_RUN, QueuedJob::STATUS_INIT]
155
        )) {
156
            $this->JobStatus = QueuedJob::STATUS_PAUSED;
157
            $this->write();
158
            return true;
159
        }
160
        return false;
161
    }
162
163
    /**
164
     * Resume this job and schedules it for execution
165
     *
166
     * @param bool $force Resume this job even if it's not paused or broken
167
     *
168
     * @return bool Return true if this job was resumed
169
     */
170
    public function resume($force = false)
171
    {
172
        if ($force || in_array($this->JobStatus, [QueuedJob::STATUS_PAUSED, QueuedJob::STATUS_BROKEN])) {
173
            $this->JobStatus = QueuedJob::STATUS_WAIT;
174
            $this->ResumeCounts++;
175
            $this->write();
176
            singleton(QueuedJobService::class)->startJob($this);
177
            return true;
178
        }
179
        return false;
180
    }
181
182
    /**
183
     * Restarts this job via a forced resume
184
     */
185
    public function restart()
186
    {
187
        $this->resume(true);
188
    }
189
190
    /**
191
     * Called to indicate that the job is ready to be run on the queue. This is done either as the result of
192
     * creating the job and adding it, or when resuming.
193
     */
194
    public function activateOnQueue()
195
    {
196
        // if it's an immediate job, lets cache it to disk to be picked up later
197
        if ($this->JobType == QueuedJob::IMMEDIATE
198
            && !Config::inst()->get(QueuedJobService::class, 'use_shutdown_function')
199
        ) {
200
            touch($this->getJobDir() . '/queuedjob-' . $this->ID);
201
        }
202
    }
203
204
    /**
205
     * Gets the path to the queuedjob cache directory
206
     *
207
     * @return string
208
     */
209
    protected function getJobDir()
210
    {
211
        // make sure our temp dir is in place. This is what will be inotify watched
212
        $jobDir = Config::inst()->get(QueuedJobService::class, 'cache_dir');
213
        if ($jobDir{0} != '/') {
214
            $jobDir = TEMP_FOLDER . '/' . $jobDir;
215
        }
216
217
        if (!is_dir($jobDir)) {
218
            Filesystem::makeFolder($jobDir);
219
        }
220
        return $jobDir;
221
    }
222
223
    public function execute()
224
    {
225
        $service = singleton(QueuedJobService::class);
226
        $service->runJob($this->ID);
227
    }
228
229
    /**
230
     * Called when the job has completed and we want to cleanup anything the descriptor has lying around
231
     * in caches or the like.
232
     */
233
    public function cleanupJob()
234
    {
235
        // remove the job's temp file if it exists
236
        $tmpFile = $this->getJobDir() . '/queuedjob-' . $this->ID;
237
        if (file_exists($tmpFile)) {
238
            unlink($tmpFile);
239
        }
240
    }
241
242
    public function onBeforeDelete()
243
    {
244
        parent::onBeforeDelete();
245
        $this->cleanupJob();
246
    }
247
248
    /**
249
     * Get all job messages as an HTML unordered list.
250
     *
251
     * @return string|void
252
     */
253
    public function getMessages()
254
    {
255
        if (strlen($this->SavedJobMessages)) {
256
            $messages = @unserialize($this->SavedJobMessages);
257
            if (!empty($messages)) {
258
                return DBField::create_field('HTMLText', '<ul><li>' . nl2br(implode('</li><li>', Convert::raw2xml($messages))) . '</li></ul>');
259
            }
260
            return '';
261
        }
262
    }
263
264
    /**
265
     * Get the last job message as a raw string
266
     *
267
     * @return string|void
268
     */
269
    public function getLastMessage()
270
    {
271
        if (strlen($this->SavedJobMessages)) {
272
            $msgs = @unserialize($this->SavedJobMessages);
273
            if (is_array($msgs) && sizeof($msgs)) {
274
                $msg = array_pop($msgs);
275
                return $msg;
276
            }
277
        }
278
    }
279
280
    /**
281
     * @return string
282
     */
283
    public function getTitle()
284
    {
285
        return $this->JobTitle;
286
    }
287
288
    /**
289
     * Return a string representation of the numeric JobType
290
     * @return string
291
     */
292
    public function getJobTypeString()
293
    {
294
        $map = $this->getJobTypeValues();
295
        return isset($map[$this->JobType]) ? $map[$this->JobType] : '(Unknown)';
296
    }
297
298
299
    /**
300
     * Return a map of numeric JobType values to localisable string representations.
301
     * @return array
302
     */
303
    public function getJobTypeValues()
304
    {
305
        return [
306
            QueuedJob::IMMEDIATE => _t(__CLASS__ . '.TYPE_IMMEDIATE', 'Immediate'),
307
            QueuedJob::QUEUED => _t(__CLASS__ . '.TYPE_QUEUED', 'Queued'),
308
            QueuedJob::LARGE => _t(__CLASS__ . '.TYPE_LARGE', 'Large'),
309
        ];
310
    }
311
312
    /**
313
     * @return FieldList
314
     */
315
    public function getCMSFields()
316
    {
317
        $fields = parent::getCMSFields();
318
        $fields->replaceField(
319
            'JobType',
320
            new DropdownField('JobType', $this->fieldLabel('JobType'), $this->getJobTypeValues())
321
        );
322
        $statuses = [
323
            QueuedJob::STATUS_NEW,
324
            QueuedJob::STATUS_INIT,
325
            QueuedJob::STATUS_RUN,
326
            QueuedJob::STATUS_WAIT,
327
            QueuedJob::STATUS_COMPLETE,
328
            QueuedJob::STATUS_PAUSED,
329
            QueuedJob::STATUS_CANCELLED,
330
            QueuedJob::STATUS_BROKEN,
331
        ];
332
        $fields->replaceField(
333
            'JobStatus',
334
            DropdownField::create('JobStatus', $this->fieldLabel('JobStatus'), array_combine($statuses, $statuses))
335
        );
336
337
        $fields->removeByName('SavedJobData');
338
        $fields->removeByName('SavedJobMessages');
339
340
        if (strlen($this->SavedJobMessages)) {
341
            $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...
342
        }
343
344
        if (Permission::check('ADMIN')) {
345
            return $fields;
346
        }
347
348
        // Readonly CMS view is a lot more useful for debugging than no view at all
349
        return $fields->makeReadonly();
350
    }
351
}
352