1<?php
2/**
3 * Matomo - free/libre analytics platform
4 *
5 * @link https://matomo.org
6 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
7 */
8namespace Piwik\Plugins\TagManager\Model;
9
10use Piwik\Container\StaticContainer;
11use Piwik\Piwik;
12use Piwik\Plugins\TagManager\Context\BaseContext;
13use Piwik\Plugins\TagManager\Dao\ContainerReleaseDao;
14use Piwik\Plugins\TagManager\Dao\ContainersDao;
15use Piwik\Plugins\TagManager\Context\ContextProvider;
16use Piwik\Plugins\TagManager\Dao\ContainerVersionsDao;
17use Exception;
18use Piwik\Plugins\TagManager\Input\Description;
19use Piwik\Plugins\TagManager\Input\IdSite;
20use Piwik\Plugins\TagManager\Input\Name;
21use Piwik\Plugins\TagManager\Model\Container\ContainerIdGenerator;
22
23
24class Container extends BaseModel
25{
26
27    /**
28     * @var ContainersDao
29     */
30    private $dao;
31
32    /**
33     * @var ContextProvider
34     */
35    private $contextProvider;
36
37    /**
38     * @var ContainerVersionsDao
39     */
40    private $versionsDao;
41
42    /**
43     * @var ContainerReleaseDao
44     */
45    private $releasesDao;
46
47    /**
48     * @var ContainerIdGenerator
49     */
50    private $containerIdGenerator;
51
52    /**
53     * @var Environment
54     */
55    private $environment;
56
57    public function __construct(ContainersDao $containersDao, ContainerVersionsDao $containerVersionsDao, ContainerReleaseDao $containerPublishesDao, ContextProvider $contextProvider, ContainerIdGenerator $containerIdGenerator, Environment $environment)
58    {
59        $this->dao = $containersDao;
60        $this->versionsDao = $containerVersionsDao;
61        $this->releasesDao = $containerPublishesDao;
62        $this->contextProvider = $contextProvider;
63        $this->containerIdGenerator = $containerIdGenerator;
64        $this->environment = $environment;
65    }
66
67    public function getNumContainersTotal()
68    {
69        return $this->dao->getNumContainersTotal();
70    }
71
72    public function getNumContainersInSite($idSite)
73    {
74        return $this->dao->getNumContainersInSite($idSite);
75    }
76
77    public function enablePreviewMode($idSite, $idContainer, $idContainerVersion, $releaseLogin)
78    {
79        $idContainerRelease = $this->publishVersion($idSite, $idContainer, $idContainerVersion, Environment::ENVIRONMENT_PREVIEW, $releaseLogin);
80        $this->generateContainer($idSite, $idContainer);
81        return $idContainerRelease;
82    }
83
84    public function disablePreviewMode($idSite, $idContainer)
85    {
86        $date = $this->getCurrentDateTime();
87        $this->releasesDao->deleteAllVersionsForRelease($idSite, $idContainer, Environment::ENVIRONMENT_PREVIEW, $date);
88        $this->generateContainer($idSite, $idContainer);
89    }
90
91    public function generateContainerIfHasPreviewRelease($idSite, $idContainer)
92    {
93        $container = $this->getContainer($idSite, $idContainer);
94
95        if (!empty($container['releases'])) {
96            foreach ($container['releases'] as $release) {
97                if ($release['environment'] === Environment::ENVIRONMENT_PREVIEW) {
98
99                    // we only want to regenerate the containers if it has a preview enabled
100                    $context = $this->contextProvider->getContext($container['context']);
101                    if ($context) {
102                        return $context->generate($container);
103                    }
104                }
105            }
106        }
107    }
108
109    public function hasPreviewRelease($idSite, $idContainer)
110    {
111        $release = $this->releasesDao->getReleaseForContainerVersion($idSite, $idContainer, Environment::ENVIRONMENT_PREVIEW);
112
113        return !empty($release);
114    }
115
116    public function generateContainer($idSite, $idContainer)
117    {
118        $container = $this->getContainer($idSite, $idContainer);
119
120        if (!empty($container)) {
121            $context = $this->contextProvider->getContext($container['context']);
122            if ($context) {
123                return $context->generate($container);
124            }
125        }
126    }
127
128    public function getContainerInstallInstructions($idSite, $idContainer, $environment)
129    {
130        $this->checkContainerExists($idSite, $idContainer);
131        $this->environment->checkIsValidEnvironment($environment);
132
133        $container = $this->dao->getContainer($idSite, $idContainer);
134
135        if (!empty($container)) {
136            $context = $this->contextProvider->getContext($container['context']);
137            if ($context) {
138                return $context->getInstallInstructions($container, $environment);
139            }
140        }
141    }
142
143    private function validateContainer($idSite, $name, $description)
144    {
145        $site = new IdSite($idSite);
146        $site->check();
147
148        $name = new Name($name);
149        $name->check();
150
151        $description = new Description($description);
152        $description->check();
153    }
154
155    public function addContainer($idSite, $context, $name, $description)
156    {
157        $this->validateContainer($idSite, $name, $description);
158        $this->contextProvider->checkIsValidContext($context);
159
160        $createdDate = $this->getCurrentDateTime();
161
162        $idContainer = $this->containerIdGenerator->generateId();
163
164        $this->dao->createContainer($idSite, $idContainer, $context, $name, $description, $createdDate);
165
166        $this->versionsDao->createDraftVersion($idSite, $idContainer, $createdDate);
167
168        $this->generateContainer($idSite, $idContainer);
169
170        return $idContainer;
171    }
172
173    public function updateContainer($idSite, $idContainer, $name, $description)
174    {
175        $this->validateContainer($idSite, $name, $description);
176
177        $columns = array(
178            'name' => $name,
179            'description' => $description
180        );
181        $this->updateContainerColumns($idSite, $idContainer, $columns);
182        $this->generateContainer($idSite, $idContainer);
183    }
184
185    public function checkContainerExists($idSite, $idContainer)
186    {
187        $container = $this->dao->getContainer($idSite, $idContainer);
188
189        if (empty($container)) {
190            throw new Exception(Piwik::translate('TagManager_ErrorContainerDoesNotExist', $idContainer));
191        }
192    }
193
194    public function getContainers($idSite)
195    {
196        $containers = $this->dao->getContainersForSite($idSite);
197        return $this->enrichContainers($containers);
198    }
199
200    public function deleteContainer($idSite, $idContainer)
201    {
202        $deletedDate = $this->getCurrentDateTime();
203        $this->dao->deleteContainer($idSite, $idContainer, $deletedDate);
204
205        // we remove them to no longer expose any information to any user/visitor, as a Matomo user would assume the
206        // data has been removed
207        BaseContext::removeAllContainerFiles($idContainer);
208    }
209
210    public function getContainer($idSite, $idContainer)
211    {
212        $container = $this->dao->getContainer($idSite, $idContainer);
213
214        return $this->enrichContainer($container);
215    }
216
217    public function checkContainerVersionExists($idSite, $idContainer, $idContainerVersion)
218    {
219        $this->checkContainerExists($idSite, $idContainer);
220
221        $version = $this->versionsDao->getVersion($idSite, $idContainer, $idContainerVersion);
222
223        if (empty($version)) {
224            throw new Exception(Piwik::translate('TagManager_ErrorContainerVersionDoesNotExist'));
225        }
226    }
227
228    public function createContainerVersion($idSite, $idContainer, $idContainerVersion, $name, $description)
229    {
230        $this->checkContainerVersionExists($idSite, $idContainer, $idContainerVersion);
231        $this->validateContainerVersion($idSite, $name, $description);
232        $createdDate = $this->getCurrentDateTime();
233
234        // create a new version
235        $newIdContainerVersion = $this->versionsDao->createVersion($idSite, $idContainer, $name, $description, $createdDate);
236
237        // we need to use staticContainer and not in constructor as they would require each other in the constructor and result in a loop
238        $export = $this->getExport();
239        $exported = $export->exportContainerVersion($idSite, $idContainer, $idContainerVersion);
240        $import = StaticContainer::get('Piwik\Plugins\TagManager\API\Import');
241        $import->importContainerVersion($exported, $idSite, $idContainer, $newIdContainerVersion);
242
243        return $newIdContainerVersion;
244    }
245
246    public function updateContainerVersion($idSite, $idContainer, $idContainerVersion, $name, $description)
247    {
248        $this->validateContainerVersion($idSite, $name, $description);
249
250        $columns = array(
251            'name' => $name,
252            'description' => $description,
253            'updated_date' => $this->getCurrentDateTime()
254        );
255        $this->versionsDao->updateContainerColumns($idSite, $idContainer, $idContainerVersion, $columns);
256        $this->generateContainer($idSite, $idContainer);
257    }
258
259    public function deleteContainerVersion($idSite, $idContainer, $idContainerVersion)
260    {
261        $version = $this->getContainerVersion($idSite, $idContainer, $idContainerVersion);
262        if (!empty($version)) {
263            if (empty($version['revision'])) {
264                throw new Exception(Piwik::translate('TagManager_ErrorVersionCannotBeDeleted', Piwik::translate('TagManager_Draft')));
265            }
266            if (!empty($version['releases'])) {
267                throw new Exception(Piwik::translate('TagManager_ErrorVersionCannotBeDeletedAsPublished'));
268            }
269            $this->versionsDao->deleteVersion($idSite, $idContainerVersion, $this->getCurrentDateTime());
270        }
271    }
272
273    /**
274     * @param $idSite
275     * @param $idContainer
276     * @param $idContainerVersion
277     * @return array|bool
278     */
279    public function getContainerVersion($idSite, $idContainer, $idContainerVersion)
280    {
281        $version = $this->versionsDao->getVersion($idSite, $idContainer, $idContainerVersion);
282
283        return $this->enrichContainerVersion($version);
284    }
285
286    public function getContainerVersions($idSite, $idContainer)
287    {
288        $versions = $this->versionsDao->getVersionsOfContainer($idSite, $idContainer);
289
290        return $this->enrichContainerVersions($versions);
291    }
292
293    public function checkContainerReleaseExists($idSite, $idContainer, $environment)
294    {
295        $this->checkContainerExists($idSite, $idContainer);
296
297        $release = $this->releasesDao->getReleaseForContainerVersion($idSite, $idContainer, $environment);
298
299        if (empty($release)) {
300            throw new Exception(Piwik::translate('TagManager_ErrorContainerReleaseDoesNotExist'));
301        }
302    }
303
304    public function publishVersion($idSite, $idContainer, $idContainerVersion, $environment, $releaseLogin)
305    {
306        $this->checkContainerVersionExists($idSite, $idContainer, $idContainerVersion);
307        // there is on purpose no validation for environment name as it may be used with eg preview etc.
308
309        $publishDate = $this->getCurrentDateTime();
310        $idContainerRelease = $this->releasesDao->releaseVersion($idSite, $idContainer, $idContainerVersion, $environment, $releaseLogin, $publishDate);
311        $this->generateContainer($idSite, $idContainer);
312        return $idContainerRelease;
313    }
314
315    private function validateContainerVersion($idSite, $versionName, $versionDescription)
316    {
317        $site = new IdSite($idSite);
318        $site->check();
319
320        $name = new Name($versionName);
321        $name->check();
322
323        $description = new Description($versionDescription);
324        $description->check();
325    }
326
327    public function getAllReleasedContainers()
328    {
329        $containers = $this->releasesDao->getAllReleasedContainers();
330        foreach ($containers as $index => $container) {
331            $containers[$index]['idsite'] = (int) $container['idsite'];
332        }
333        return $containers;
334    }
335
336    public function getActiveContainersInfo()
337    {
338        $containers = $this->dao->getActiveContainersInfo();
339        foreach ($containers as $index => $container) {
340            $containers[$index]['idsite'] = (int) $container['idsite'];
341        }
342        return $containers;
343    }
344
345    private function updateContainerColumns($idSite, $idContainer, $columns)
346    {
347        if (!isset($columns['updated_date'])) {
348            $columns['updated_date'] = $this->getCurrentDateTime();
349        }
350        $this->dao->updateContainerColumns($idSite, $idContainer, $columns);
351    }
352
353    /**
354     * @return \Piwik\Plugins\TagManager\API\Export
355     */
356    private function getExport()
357    {
358        return StaticContainer::get('Piwik\Plugins\TagManager\API\Export');
359    }
360
361    private function enrichContainers($containers)
362    {
363        if (empty($containers)) {
364            return array();
365        }
366
367        foreach ($containers as $index => $container) {
368            $containers[$index] = $this->enrichContainer($container);
369        }
370
371        return $containers;
372    }
373
374    private function enrichContainer($container)
375    {
376        if (empty($container)) {
377            return $container;
378        }
379
380        $container['created_date_pretty'] = $this->formatDate($container['created_date'], $container['idsite']);
381        $container['updated_date_pretty'] = $this->formatDate($container['updated_date'], $container['idsite']);
382        unset($container['deleted_date']);
383        $container['versions'] = $this->versionsDao->getVersionsOfContainer($container['idsite'], $container['idcontainer']);
384        $container['versions'] = $this->enrichContainerVersions($container['versions'], false);
385        $container['releases'] = $this->releasesDao->getReleasesOfContainer($container['idsite'], $container['idcontainer']);
386        $container['releases'] = $this->enrichContainerReleases($container['releases']);
387        $container['draft'] = $this->versionsDao->getDraftVersion($container['idsite'], $container['idcontainer']);
388        $container['draft'] = $this->enrichContainerVersion($container['draft'], false);
389        $container = $this->mixinSetVersionsAndReleases($container);
390
391        return $container;
392    }
393
394    private function mixinSetVersionsAndReleases($container)
395    {
396        foreach ($container['versions'] as $index => $version) {
397            $container['versions'][$index]['environments'] = array();
398        }
399
400        foreach ($container['releases'] as &$release) {
401            $release['version_name'] = '';
402            foreach ($container['versions'] as &$version) {
403                if ($release['idcontainerversion'] === $version['idcontainerversion']) {
404                    $release['version_name'] = $version['name'];
405                    $version['environments'][] = $release['environment'];
406                }
407            }
408        }
409        return $container;
410    }
411
412    private function enrichContainerVersions($containerVersions, $fetchReleases = true)
413    {
414        if (empty($containerVersions)) {
415            return array();
416        }
417
418        foreach ($containerVersions as $index => $containerVersion) {
419            $containerVersions[$index] = $this->enrichContainerVersion($containerVersion, $fetchReleases);
420        }
421
422        return $containerVersions;
423    }
424
425    private function enrichContainerVersion($containerVersion, $fetchReleases = true)
426    {
427        if (empty($containerVersion)) {
428            return $containerVersion;
429        }
430
431        $containerVersion['created_date_pretty'] = $this->formatDate($containerVersion['created_date'], $containerVersion['idsite']);
432        $containerVersion['updated_date_pretty'] = $this->formatDate($containerVersion['updated_date'], $containerVersion['idsite']);
433        unset($containerVersion['deleted_date']);
434        if ($fetchReleases) {
435            $containerVersion['releases'] = $this->releasesDao->getReleasesForContainerVersion($containerVersion['idsite'], $containerVersion['idcontainer'], $containerVersion['idcontainerversion']);
436            $containerVersion['releases'] = $this->enrichContainerReleases($containerVersion['releases']);
437        }
438
439        return $containerVersion;
440    }
441
442    private function enrichContainerReleases($containerReleases)
443    {
444        if (empty($containerReleases)) {
445            return array();
446        }
447
448        foreach ($containerReleases as $index => $containerRelease) {
449            $containerReleases[$index] = $this->enrichContainerRelease($containerRelease);
450        }
451
452        return $containerReleases;
453    }
454
455    private function enrichContainerRelease($containerRelease)
456    {
457        if (empty($containerRelease)) {
458            return $containerRelease;
459        }
460
461        $containerRelease['release_date_pretty'] = $this->formatDate($containerRelease['release_date'], $containerRelease['idsite']);
462        unset($containerRelease['deleted_date']);
463
464        return $containerRelease;
465    }
466}
467
468