Issues (27)

src/Extension/Engine/SiteTreePublishingEngine.php (2 issues)

1
<?php
2
3
namespace SilverStripe\StaticPublishQueue\Extension\Engine;
4
5
use SilverStripe\CMS\Model\SiteTree;
6
use SilverStripe\CMS\Model\SiteTreeExtension;
7
use SilverStripe\Core\Environment;
8
use SilverStripe\Core\Injector\Injector;
9
use SilverStripe\Core\Resettable;
10
use SilverStripe\ORM\ValidationException;
11
use SilverStripe\StaticPublishQueue\Contract\StaticPublishingTrigger;
12
use SilverStripe\StaticPublishQueue\Extension\Publishable\PublishableSiteTree;
13
use SilverStripe\StaticPublishQueue\Job\DeleteStaticCacheJob;
14
use SilverStripe\StaticPublishQueue\Job\GenerateStaticCacheJob;
15
use SilverStripe\StaticPublishQueue\Service\UrlBundleInterface;
16
use Symbiote\QueuedJobs\Services\QueuedJobService;
17
18
/**
19
 * This extension couples to the StaticallyPublishable and StaticPublishingTrigger implementations
20
 * on the SiteTree objects and makes sure the actual change to SiteTree is triggered/enqueued.
21
 *
22
 * Provides the following information as a context to StaticPublishingTrigger:
23
 * * action - name of the executed action: publish or unpublish
24
 *
25
 * @see PublishableSiteTree
26
 */
27
class SiteTreePublishingEngine extends SiteTreeExtension implements Resettable
28
{
29
    /**
30
     * Queued job service injection property
31
     * Used for unit tests only to cover edge cases where Injector doesn't cover
32
     *
33
     * @var QueuedJobService|null
34
     */
35
    protected static $queueService = null;
36
37
    /**
38
     * Queues the urls to be flushed into the queue.
39
     *
40
     * @var array
41
     */
42
    private $toUpdate = [];
43
44
    /**
45
     * Queues the urls to be deleted as part of a next flush operation.
46
     *
47
     * @var array
48
     */
49
    private $toDelete = [];
50
51
    public static function reset(): void
52
    {
53
        static::$queueService = null;
54
    }
55
56
    /**
57
     * Force inject queue service
58
     * Used for unit tests only to cover edge cases where Injector doesn't cover
59
     *
60
     *
61
     * @param QueuedJobService $service
62
     */
63
    public static function setQueueService(QueuedJobService $service): void
64
    {
65
        static::$queueService = $service;
66
    }
67
68
    /**
69
     * @return array
70
     */
71
    public function getToUpdate()
72
    {
73
        return $this->toUpdate;
74
    }
75
76
    /**
77
     * @return array
78
     */
79
    public function getToDelete()
80
    {
81
        return $this->toDelete;
82
    }
83
84
    /**
85
     * @param array $toUpdate
86
     * @return $this
87
     */
88
    public function setToUpdate($toUpdate)
89
    {
90
        $this->toUpdate = $toUpdate;
91
        return $this;
92
    }
93
94
    /**
95
     * @param array $toDelete
96
     * @return $this
97
     */
98
    public function setToDelete($toDelete)
99
    {
100
        $this->toDelete = $toDelete;
101
        return $this;
102
    }
103
104
    /**
105
     * @param SiteTree|SiteTreePublishingEngine|null $original
106
     */
107
    public function onAfterPublishRecursive(&$original)
108
    {
109
        // If the site tree has been "reorganised" (ie: the parentID has changed)
110
        // then this is the equivalent of an un-publish and publish as far as the
111
        // static publisher is concerned
112
        if ($original && (
113
            // Intentionally using loose comparison as ParentID may contain either string or integer
114
            $original->ParentID != $this->getOwner()->ParentID
0 ignored issues
show
Bug Best Practice introduced by
The property ParentID does not exist on SilverStripe\StaticPubli...iteTreePublishingEngine. Did you maybe forget to declare it?
Loading history...
115
                || $original->URLSegment !== $this->getOwner()->URLSegment
0 ignored issues
show
Bug Best Practice introduced by
The property URLSegment does not exist on SilverStripe\StaticPubli...iteTreePublishingEngine. Did you maybe forget to declare it?
Loading history...
116
            )
117
        ) {
118
            $context = [
119
                'action' => 'unpublish',
120
            ];
121
            $original->collectChanges($context);
122
            $original->flushChanges();
123
        }
124
        $context = [
125
            'action' => 'publish',
126
        ];
127
        $this->collectChanges($context);
128
        $this->flushChanges();
129
    }
130
131
    public function onBeforeUnpublish()
132
    {
133
        $context = [
134
            'action' => 'unpublish',
135
        ];
136
        $this->collectChanges($context);
137
    }
138
139
    public function onAfterUnpublish()
140
    {
141
        $this->flushChanges();
142
    }
143
144
    /**
145
     * Collect all changes for the given context.
146
     *
147
     * @param array $context
148
     */
149
    public function collectChanges($context)
150
    {
151
        Environment::increaseMemoryLimitTo();
152
        Environment::increaseTimeLimitTo();
153
154
        if ($this->getOwner()->hasExtension(PublishableSiteTree::class)
155
            || $this->getOwner() instanceof StaticPublishingTrigger
156
        ) {
157
            $toUpdate = $this->getOwner()->objectsToUpdate($context);
158
            $this->setToUpdate($toUpdate);
159
160
            $toDelete = $this->getOwner()->objectsToDelete($context);
161
            $this->setToDelete($toDelete);
162
        }
163
    }
164
165
    /**
166
     * Execute URL deletions, enqueue URL updates.
167
     */
168
    public function flushChanges()
169
    {
170
        $queueService = static::$queueService ?? QueuedJobService::singleton();
171
172
        if (!empty($this->toUpdate)) {
173
            /** @var UrlBundleInterface $urlService */
174
            $urlService = Injector::inst()->create(UrlBundleInterface::class);
175
176
            foreach ($this->toUpdate as $item) {
177
                $urls = $item->urlsToCache();
178
                ksort($urls);
179
                $urls = array_keys($urls);
180
                $urlService->addUrls($urls);
181
            }
182
183
            $jobs = $urlService->getJobsForUrls(GenerateStaticCacheJob::class, 'Building URLs', $this->owner);
184
185
            foreach ($jobs as $job) {
186
                $queueService->queueJob($job);
187
            }
188
189
            $this->toUpdate = [];
190
        }
191
192
        if (!empty($this->toDelete)) {
193
            /** @var UrlBundleInterface $urlService */
194
            $urlService = Injector::inst()->create(UrlBundleInterface::class);
195
196
            foreach ($this->toDelete as $item) {
197
                $urls = $item->urlsToCache();
198
                ksort($urls);
199
                $urls = array_keys($urls);
200
                $urlService->addUrls($urls);
201
            }
202
203
            $jobs = $urlService->getJobsForUrls(DeleteStaticCacheJob::class, 'Purging URLs', $this->owner);
204
205
            foreach ($jobs as $job) {
206
                $queueService->queueJob($job);
207
            }
208
209
            $this->toDelete = [];
210
        }
211
    }
212
}
213