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.

QueuedJobDescriptor::activateOnQueue()   A
last analyzed

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