~xibo-maintainers/xibo/tempel

« back to all changes in this revision

Viewing changes to lib/Factory/RequiredFileFactory.php

  • Committer: GitHub
  • Author(s): Dan Garner
  • Date: 2016-11-16 11:51:16 UTC
  • Revision ID: git-v1:1cdd39222ff9fdd3fcc0bb8924cc706a24b18905
Bugfix/1.8.0 pack1 (#227)

* Library Tidy inaccessible
* Enabled notifications tidy task by default
* Implementation of "parent settings" so that TwitterMetro can use the API key/secret from Twitter.
* Fix for StatsArchive task infinite loop
* Fix for StatsArchive task processing ahead of now
* Flip clock to support countdown from a particular date, and daily countdown. xibosignage/xibo#936
* Fix date parsing when a timezone has been set
* Only save campaign if changes were made.
* Fix display notifications for removing dups
* XMR priv socket sleep to 100 ms
* Fix removal of Twig sources on release archive build. xibosignage/xibo#940
* Improve campaign layout assign/unassign xibosignage/xibo#941
* Tidy up unnecessary save in MediaInventory. Better logging for errors with sendfile from XMDS.
* Fix display status icon xibosignage/xibo#943
* Updated caching logic for required files
* Rewrite the nonce cache (required file) so that it uses a key per file instead of one storage object.
* Rework required files again - move back into the DB, but optimize nonce creation to ease contention.
* Further improvements to DB activity, basic connection management and stats reporting. Also deadlock protection statement.
* XMDS not updating bytes requested.
* More logging for retry deadlock loop
* Correct bandwidth logging. Fix deadlock loop.
* Improvements to media download (proper async). Fix for module downloads being copied rather than moved.
* Swagger doc fixes
* Convert XTR into a select and run single process script. Should fix it for windows.

Show diffs side-by-side

added added

removed removed

Lines of Context:
8
8
 
9
9
namespace Xibo\Factory;
10
10
 
11
 
 
12
 
use Stash\Interfaces\PoolInterface;
13
11
use Xibo\Entity\RequiredFile;
14
12
use Xibo\Exception\NotFoundException;
15
13
use Xibo\Service\LogServiceInterface;
22
20
 */
23
21
class RequiredFileFactory extends BaseFactory
24
22
{
25
 
    private $directory = null;
26
 
    private $files = [];
27
 
    private $displayId = 0;
28
 
 
29
 
    /** @var  PoolInterface */
30
 
    private $pool;
 
23
    private $statement = null;
31
24
 
32
25
    /**
33
26
     * Construct a factory
34
27
     * @param StorageServiceInterface $store
35
28
     * @param LogServiceInterface $log
36
29
     * @param SanitizerServiceInterface $sanitizerService
37
 
     * @param PoolInterface $pool
38
30
     */
39
 
    public function __construct($store, $log, $sanitizerService, $pool)
 
31
    public function __construct($store, $log, $sanitizerService)
40
32
    {
41
33
        $this->setCommonDependencies($store, $log, $sanitizerService);
42
 
        $this->pool = $pool;
43
34
    }
44
35
 
45
36
    /**
47
38
     */
48
39
    public function createEmpty()
49
40
    {
50
 
        return new RequiredFile($this->getStore(), $this->getLog(), $this);
51
 
    }
52
 
 
53
 
    /**
54
 
     * @param string $nonce
55
 
     * @return RequiredFile
56
 
     * @throws NotFoundException
57
 
     */
58
 
    public function getByNonce($nonce)
59
 
    {
60
 
        $this->getLog()->debug('Required file by Nonce: ' . $nonce);
61
 
 
62
 
        if ($this->directory === null) {
63
 
            $item = $this->pool->getItem('inventory/directory');
64
 
 
65
 
            if ($item->isHit()) {
66
 
                $this->directory = json_decode($item->get(), true);
67
 
            } else {
68
 
                throw new NotFoundException();
69
 
            }
70
 
        }
71
 
 
72
 
        if (count($this->directory) <= 0) {
73
 
            $this->getLog()->debug('Empty inventory directory');
74
 
            throw new NotFoundException();
75
 
        }
76
 
 
77
 
        if (array_key_exists($nonce, $this->directory)) {
78
 
            // Get the nonce out of the relevent display inventory
79
 
            $displayId = $this->directory[$nonce];
80
 
 
81
 
            $this->getLog()->debug('Nonce resolves to displayId ' . $displayId);
82
 
 
83
 
            return $this->getByDisplayAndNonce($displayId, $nonce);
84
 
        } else {
85
 
            $this->getLog()->debug('Nonce ' . $nonce . ' does not exist in directory.');
86
 
            throw new NotFoundException();
87
 
        }
88
 
    }
89
 
 
90
 
    /**
91
 
     * Get Required files for display
92
 
     * @param int $displayId
93
 
     */
94
 
    public function setDisplay($displayId)
95
 
    {
96
 
        if ($this->displayId === $displayId)
97
 
            return;
98
 
 
99
 
        // Persist what we currently have
100
 
        $this->persist();
101
 
 
102
 
        // Load fresh display
103
 
        $this->files = [
104
 
            '_total' => 0,
105
 
            '_totalSize' => 0,
106
 
            '_totalComplete' => 0,
107
 
            '_totalSizeComplete' => 0,
108
 
            'layout' => [],
109
 
            'media' => [],
110
 
            'widget' => [],
111
 
            'nonce' => []
112
 
        ];
113
 
 
114
 
        $item = $this->pool->getItem('inventory/' . $displayId);
115
 
 
116
 
        if ($item->isHit()) {
117
 
            $files = json_decode($item->get(), true);
118
 
 
119
 
            foreach ($files as $key => $element) {
120
 
 
121
 
                $file = $this->createEmpty()->hydrate($element);
122
 
 
123
 
                $this->files['_total'] = $this->files['_total'] + 1;
124
 
                $this->files['_totalSize'] = $this->files['_totalSize'] + $file->size;
125
 
 
126
 
                if ($file->complete == 1) {
127
 
                    $this->files['_totalComplete'] = $this->files['_totalComplete'] + 1;
128
 
                    $this->files['_totalSizeComplete'] = $this->files['_totalSizeComplete'] + $file->size;
129
 
                }
130
 
 
131
 
                // Add the required file to the appropriate array
132
 
                $this->addFileToLookupKey($file);
133
 
                $this->addFileToStore($file);
134
 
            }
135
 
        }
136
 
 
137
 
        //$this->getLog()->debug('Cache: ' . json_encode($this->files));
138
 
 
139
 
        // Store the current displayId
140
 
        $this->displayId = $displayId;
141
 
    }
142
 
 
143
 
    /**
144
 
     * @param RequiredFile $file
145
 
     */
146
 
    private function addFileToStore($file)
147
 
    {
148
 
        $this->files['nonce'][$file->nonce] = $file;
149
 
    }
150
 
 
151
 
    /**
152
 
     * @param RequiredFile $file
153
 
     */
154
 
    private function addFileToLookupKey($file)
155
 
    {
156
 
        // Add the required file to the appropriate array
157
 
        if ($file->layoutId != 0 && $file->regionId != 0 && $file->mediaId != 0) {
158
 
            $this->files['widget'][$file->mediaId] = $file->nonce;
159
 
        } else if ($file->mediaId != 0) {
160
 
            $this->files['media'][$file->mediaId] = $file->nonce;
161
 
        } else if ($file->layoutId != 0) {
162
 
            $this->files['layout'][$file->layoutId] = $file->nonce;
163
 
        }
164
 
    }
165
 
 
166
 
    /**
167
 
     * @param RequiredFile $file
168
 
     * @param string $nonce
169
 
     */
170
 
    public function addOrReplace($file, $nonce)
171
 
    {
172
 
        if ($this->displayId != $file->displayId)
173
 
            $this->setDisplay($file->displayId);
174
 
 
175
 
        // Given the required file we've been provided, find that in our current cache and replace the nonce and the pointer to it
176
 
        if ($nonce !== '') {
177
 
            // We are an existing required file, which needs removing and then adding.
178
 
            $this->remove($file, $nonce);
179
 
        }
180
 
 
181
 
        // pop it in the current array, according to its nature
182
 
        $this->addFileToStore($file);
183
 
 
184
 
        // Add the required file to the appropriate array
185
 
        $this->addFileToLookupKey($file);
186
 
    }
187
 
 
188
 
    /**
189
 
     * Removes a required file from the cache
190
 
     * @param RequiredFile $file
191
 
     * @param string $nonce
192
 
     */
193
 
    private function remove($file, $nonce)
194
 
    {
195
 
        // Remove from the cache
196
 
        if ($file->layoutId != 0 && $file->regionId != 0 && $file->mediaId != 0) {
197
 
            unset($this->files['widget'][$file->mediaId]);
198
 
        } else if ($file->mediaId != 0) {
199
 
            unset($this->files['media'][$file->mediaId]);
200
 
        } else if ($file->layoutId != 0) {
201
 
            unset($this->files['layout'][$file->layoutId]);
202
 
        }
203
 
 
204
 
        unset($this->files['nonce'][$nonce]);
205
 
    }
206
 
 
207
 
    /**
208
 
     * @return int
209
 
     */
210
 
    public function getTotalCount()
211
 
    {
212
 
        return $this->files['_total'];
213
 
    }
214
 
 
215
 
    /**
216
 
     * @return mixed
217
 
     */
218
 
    public function getCompleteCount()
219
 
    {
220
 
        return $this->files['_totalComplete'];
221
 
    }
222
 
 
223
 
    /**
224
 
     * @return mixed
225
 
     */
226
 
    public function getTotalSize()
227
 
    {
228
 
        return $this->files['_totalSize'];
229
 
    }
230
 
 
231
 
    /**
232
 
     * @return mixed
233
 
     */
234
 
    public function getCompleteSize()
235
 
    {
236
 
        return $this->files['_totalSizeComplete'];
237
 
    }
238
 
 
239
 
    /**
240
 
     * @return array
241
 
     */
242
 
    public function getLayoutIds()
243
 
    {
244
 
        return array_keys($this->files['layout']);
245
 
    }
246
 
 
247
 
    /**
248
 
     * @return array
249
 
     */
250
 
    public function getMediaIds()
251
 
    {
252
 
        return array_keys($this->files['media']);
253
 
    }
254
 
 
255
 
    /**
256
 
     * @return array
257
 
     */
258
 
    public function getWidgetIds()
259
 
    {
260
 
        return array_keys($this->files['widget']);
261
 
    }
262
 
 
263
 
    /**
264
 
     * @param int $displayId
265
 
     * @param string $nonce
266
 
     * @return RequiredFile
267
 
     * @throws NotFoundException
268
 
     */
269
 
    public function getByDisplayAndNonce($displayId, $nonce)
270
 
    {
271
 
        if ($this->displayId != $displayId)
272
 
            $this->setDisplay($displayId);
273
 
 
274
 
        if (!isset($this->files['nonce'][$nonce]))
275
 
            throw new NotFoundException('Nonce not in directory required files.');
276
 
 
277
 
        $rf = $this->files['nonce'][$nonce];
278
 
 
279
 
        if ($rf == null)
280
 
            throw new NotFoundException();
281
 
 
282
 
        return $rf;
 
41
        return new RequiredFile($this->getStore(), $this->getLog());
 
42
    }
 
43
 
 
44
    /**
 
45
     * @param array $params
 
46
     * @return RequiredFile[]
 
47
     */
 
48
    private function query($params)
 
49
    {
 
50
        $files = [];
 
51
 
 
52
        if ($this->statement === null) {
 
53
            $this->statement = $this->getStore()->getConnection()->prepare('
 
54
              SELECT * 
 
55
                FROM `requiredfile` 
 
56
               WHERE `displayId` = :displayId
 
57
                  AND `type` = :type 
 
58
                  AND `itemId` = :itemId
 
59
              ');
 
60
        }
 
61
 
 
62
        $this->statement->execute($params);
 
63
 
 
64
        foreach ($this->statement->fetchAll(\PDO::FETCH_ASSOC) as $item) {
 
65
            $files[] = $this->createEmpty()->hydrate($item);
 
66
        }
 
67
 
 
68
        return $files;
283
69
    }
284
70
 
285
71
    /**
290
76
     */
291
77
    public function getByDisplayAndLayout($displayId, $layoutId)
292
78
    {
293
 
        if ($this->displayId != $displayId)
294
 
            $this->setDisplay($displayId);
295
 
 
296
 
        if (!isset($this->files['layout'][$layoutId]))
297
 
            throw new NotFoundException();
298
 
 
299
 
        $rf = $this->files['nonce'][$this->files['layout'][$layoutId]];
300
 
 
301
 
        if ($rf == null)
302
 
            throw new NotFoundException();
303
 
 
304
 
        return $rf;
 
79
        $result = $this->query(['displayId' => $displayId, 'type' => 'L', 'itemId' => $layoutId]);
 
80
 
 
81
        if (count($result) <= 0)
 
82
            throw new NotFoundException(__('Required file not found for Display and Layout Combination'));
 
83
 
 
84
        return $result[0];
305
85
    }
306
86
 
307
87
    /**
312
92
     */
313
93
    public function getByDisplayAndMedia($displayId, $mediaId)
314
94
    {
315
 
        if ($this->displayId != $displayId)
316
 
            $this->setDisplay($displayId);
317
 
 
318
 
        if (!isset($this->files['media'][$mediaId]))
319
 
            throw new NotFoundException();
320
 
 
321
 
        $file = $this->files['nonce'][$this->files['media'][$mediaId]];
322
 
 
323
 
        if ($file == null)
324
 
            throw new NotFoundException();
325
 
 
326
 
        return $file;
 
95
        $result = $this->query(['displayId' => $displayId, 'type' => 'M', 'itemId' => $mediaId]);
 
96
 
 
97
        if (count($result) <= 0)
 
98
            throw new NotFoundException(__('Required file not found for Display and Media Combination'));
 
99
 
 
100
        return $result[0];
327
101
    }
328
102
 
329
103
    /**
334
108
     */
335
109
    public function getByDisplayAndWidget($displayId, $widgetId)
336
110
    {
337
 
        if ($this->displayId != $displayId)
338
 
            $this->setDisplay($displayId);
339
 
 
340
 
        if (!isset($this->files['widget'][$widgetId]))
341
 
            throw new NotFoundException();
342
 
 
343
 
        $file = $this->files['nonce'][$this->files['widget'][$widgetId]];
344
 
 
345
 
        if ($file == null)
346
 
            throw new NotFoundException();
347
 
 
348
 
        return $file;
 
111
        $result = $this->query(['displayId' => $displayId, 'type' => 'W', 'itemId' => $widgetId]);
 
112
 
 
113
        if (count($result) <= 0)
 
114
            throw new NotFoundException(__('Required file not found for Display and Layout Widget'));
 
115
 
 
116
        return $result[0];
349
117
    }
350
118
 
351
119
    /**
359
127
    public function createForLayout($displayId, $layoutId, $size, $path)
360
128
    {
361
129
        try {
362
 
            $nonce = $this->getByDisplayAndLayout($displayId, $layoutId);
 
130
            $requiredFile = $this->getByDisplayAndLayout($displayId, $layoutId);
363
131
        }
364
132
        catch (NotFoundException $e) {
365
 
            $nonce = $this->createEmpty();
 
133
            $requiredFile = $this->createEmpty();
366
134
        }
367
135
 
368
 
        $nonce->displayId = $displayId;
369
 
        $nonce->layoutId = $layoutId;
370
 
        $nonce->size = $size;
371
 
        $nonce->storedAs = $path;
372
 
        return $nonce;
 
136
        $requiredFile->displayId = $displayId;
 
137
        $requiredFile->type = 'L';
 
138
        $requiredFile->itemId = $layoutId;
 
139
        $requiredFile->size = $size;
 
140
        $requiredFile->path = $path;
 
141
        return $requiredFile;
373
142
    }
374
143
 
375
144
    /**
376
145
     * Create for Get Resource
377
146
     * @param $displayId
378
 
     * @param $layoutId
379
 
     * @param $regionId
380
 
     * @param $mediaId
 
147
     * @param $widgetId
381
148
     * @return RequiredFile
382
149
     */
383
 
    public function createForGetResource($displayId, $layoutId, $regionId, $mediaId)
 
150
    public function createForGetResource($displayId, $widgetId)
384
151
    {
385
152
        try {
386
 
            $nonce = $this->getByDisplayAndWidget($displayId, $mediaId);
 
153
            $requiredFile = $this->getByDisplayAndWidget($displayId, $widgetId);
387
154
        }
388
155
        catch (NotFoundException $e) {
389
 
            $nonce = $this->createEmpty();
 
156
            $requiredFile = $this->createEmpty();
390
157
        }
391
158
 
392
 
        $nonce->displayId = $displayId;
393
 
        $nonce->layoutId = $layoutId;
394
 
        $nonce->regionId = $regionId;
395
 
        $nonce->mediaId = $mediaId;
396
 
        return $nonce;
 
159
        $requiredFile->displayId = $displayId;
 
160
        $requiredFile->type = 'W';
 
161
        $requiredFile->itemId = $widgetId;
 
162
        return $requiredFile;
397
163
    }
398
164
 
399
165
    /**
407
173
    public function createForMedia($displayId, $mediaId, $size, $path)
408
174
    {
409
175
        try {
410
 
            $nonce = $this->getByDisplayAndMedia($displayId, $mediaId);
 
176
            $requiredFile = $this->getByDisplayAndMedia($displayId, $mediaId);
411
177
        }
412
178
        catch (NotFoundException $e) {
413
 
            $nonce = $this->createEmpty();
414
 
        }
415
 
 
416
 
        $nonce->displayId = $displayId;
417
 
        $nonce->mediaId = $mediaId;
418
 
        $nonce->size = $size;
419
 
        $nonce->storedAs = $path;
420
 
        return $nonce;
421
 
    }
422
 
 
423
 
    /**
424
 
     * Expire all nonces
425
 
     * @param $displayId
426
 
     */
427
 
    public function expireAll($displayId)
428
 
    {
429
 
        $this->setDisplay($displayId);
430
 
 
431
 
        // Go through each nonce and set it to a short expiry
432
 
        foreach ($this->files['nonce'] as $file) {
433
 
            /** @var RequiredFile $file */
434
 
            $file->expireSoon();
435
 
        }
436
 
    }
437
 
 
438
 
    /**
439
 
     * Persist the current pool to the cache
440
 
     */
441
 
    public function persist()
442
 
    {
443
 
        if ($this->displayId == null)
444
 
            return;
445
 
 
446
 
        $directoryItem = $this->pool->getItem('inventory/directory');
447
 
 
448
 
        if ($directoryItem->isHit()) {
449
 
            $directory = json_decode($directoryItem->get(), true);
450
 
        } else {
451
 
            $directory = [];
452
 
        }
453
 
 
454
 
        // Combine our nonce directory with the existing global directory
455
 
        foreach ($this->files['nonce'] as $key => $value) {
456
 
            /** @var RequiredFile $value */
457
 
            if ($value->isExpired()) {
458
 
                unset($directory[$key]);
459
 
                $this->remove($value, $value->nonce);
460
 
            } else {
461
 
                $directory[$key] = $this->displayId;
462
 
            }
463
 
        }
464
 
 
465
 
        // Overwrite the pool cache
466
 
        $item = $this->pool->getItem('inventory/' . $this->displayId);
467
 
        $item->set(json_encode($this->files['nonce']));
468
 
        $item->expiresAfter(new \DateInterval('P2M'));
469
 
 
470
 
        // Set the directory
471
 
        $directoryItem->set(json_encode($directory));
472
 
        $directoryItem->expiresAfter(new \DateInterval('P2M'));
473
 
 
474
 
        $this->pool->saveDeferred($item);
475
 
        $this->pool->saveDeferred($directoryItem);
 
179
            $requiredFile = $this->createEmpty();
 
180
        }
 
181
 
 
182
        $requiredFile->displayId = $displayId;
 
183
        $requiredFile->type = 'M';
 
184
        $requiredFile->itemId = $mediaId;
 
185
        $requiredFile->size = $size;
 
186
        $requiredFile->path = $path;
 
187
        return $requiredFile;
476
188
    }
477
189
}
 
 
b'\\ No newline at end of file'