1<?php
2
3
4namespace ComboStrap;
5
6
7class PageImages extends MetadataTabular
8{
9
10
11    const CANONICAL = "page:image";
12    public const PROPERTY_NAME = 'images';
13    public const PERSISTENT_NAME = 'images';
14    public const FIRST_IMAGE_META_RELATION = "firstimage";
15    public const CONF_DISABLE_FIRST_IMAGE_AS_PAGE_IMAGE = "disableFirstImageAsPageImage";
16
17    /**
18     * The name should be plural, this one was not
19     */
20    const OLD_PROPERTY_NAME = "image";
21
22
23    /**
24     * PageImages constructor.
25     */
26    public function __construct()
27    {
28        parent::__construct();
29    }
30
31
32    public static function createForPage(Page $page): PageImages
33    {
34        return (new PageImages())
35            ->setResource($page);
36
37    }
38
39    public static function create(): Metadata
40    {
41        return new PageImages();
42    }
43
44    /**
45     * Google accepts several images dimension and ratios
46     * for the same image
47     * We may get an array then
48     */
49    public function getValueAsPageImagesOrDefault(): array
50    {
51
52        $pageImages = $this->getValueAsPageImages();
53        if ($pageImages !== null) {
54            return $pageImages;
55        }
56        try {
57            $defaultPageImage = $this->getDefaultImage();
58        } catch (ExceptionCombo $e) {
59            LogUtility::msg("Error while getting the default page image for the page {$this->getResource()}. The image was not used. Error: {$e->getMessage()}");
60            return [];
61        }
62        if ($defaultPageImage === null) {
63            return [];
64        }
65        try {
66            return [
67                PageImage::create($defaultPageImage, $this->getResource())
68            ];
69        } catch (ExceptionCombo $e) {
70            LogUtility::msg("Error while creating the default page image ($defaultPageImage) for the page {$this->getResource()}. The image was not used. Error: {$e->getMessage()}");
71            return [];
72        }
73
74    }
75
76    /**
77     * @return array
78     */
79    private function toMetadataArray(): ?array
80    {
81        if ($this->pageImages === null) {
82            return null;
83        }
84        $pageImagesMeta = [];
85        ksort($this->pageImages);
86        foreach ($this->pageImages as $pageImage) {
87            $absolutePath = $pageImage->getImage()->getPath()->toAbsolutePath()->toString();
88            $pageImagesMeta[$absolutePath] = [
89                PageImagePath::PERSISTENT_NAME => $absolutePath
90            ];
91            if ($pageImage->getUsages() !== null && $pageImage->getUsages() !== $pageImage->getDefaultUsage()) {
92                $pageImagesMeta[$absolutePath][PageImageUsage::PERSISTENT_NAME] = implode(", ", $pageImage->getUsages());
93            }
94        };
95        return array_values($pageImagesMeta);
96    }
97
98    /**
99     * @param $persistentValue
100     * @return PageImage[]
101     * @throws ExceptionCombo
102     */
103    public function toPageImageArray($persistentValue): array
104    {
105
106        if ($persistentValue === null) {
107            return [];
108        }
109
110        /**
111         * @var Page $page ;
112         */
113        $page = $this->getResource();
114
115        if (is_array($persistentValue)) {
116            $images = [];
117            foreach ($persistentValue as $key => $value) {
118                $usage = null;
119                if (is_numeric($key)) {
120                    if (is_array($value)) {
121                        if (isset($value[PageImageUsage::PERSISTENT_NAME])) {
122                            $usage = $value[PageImageUsage::PERSISTENT_NAME];
123                            if (is_string($usage)) {
124                                $usage = explode(",", $usage);
125                            }
126                        }
127                        $imagePath = $value[PageImagePath::PERSISTENT_NAME];
128                    } else {
129                        $imagePath = $value;
130                    }
131                } else {
132                    $imagePath = $key;
133                    if (is_array($value) && isset($value[PageImageUsage::PERSISTENT_NAME])) {
134                        $usage = $value[PageImageUsage::PERSISTENT_NAME];
135                        if (!is_array($usage)) {
136                            $usage = explode(",", $usage);;
137                        }
138                    }
139                }
140                DokuPath::addRootSeparatorIfNotPresent($imagePath);
141                $pageImage = PageImage::create($imagePath, $page);
142                if ($usage !== null) {
143                    $pageImage->setUsages($usage);
144                }
145                $images[$imagePath] = $pageImage;
146
147            }
148            return $images;
149        } else {
150            /**
151             * A single path image
152             */
153            DokuPath::addRootSeparatorIfNotPresent($persistentValue);
154            $images = [$persistentValue => PageImage::create($persistentValue, $page)];
155        }
156
157        return $images;
158
159    }
160
161
162    /**
163     * @throws ExceptionCombo
164     */
165    public function setFromStoreValue($value): Metadata
166    {
167        $this->buildFromStoreValue($value);
168        $this->checkImageExistence();
169        return $this;
170    }
171
172
173    public function getCanonical(): string
174    {
175        return self::CANONICAL;
176    }
177
178    static public function getName(): string
179    {
180        return self::PROPERTY_NAME;
181    }
182
183    static public function getPersistentName(): string
184    {
185        return self::PERSISTENT_NAME;
186    }
187
188    /**
189     * @throws ExceptionCombo
190     */
191    public function toStoreValue(): ?array
192    {
193        $this->buildCheck();
194        $this->checkImageExistence();
195        return parent::toStoreValue();
196    }
197
198    public function toStoreDefaultValue()
199    {
200        return null;
201    }
202
203    public function getPersistenceType(): string
204    {
205        return MetadataDokuWikiStore::PERSISTENT_METADATA;
206    }
207
208
209    /**
210     * @return PageImage[]
211     */
212    public function getValueAsPageImages(): ?array
213    {
214        $this->buildCheck();
215
216        $rows = parent::getValue();
217        if ($rows === null) {
218            return null;
219        }
220        $pageImages = [];
221        foreach ($rows as $row) {
222            /**
223             * @var PageImagePath $pageImagePath
224             */
225            $pageImagePath = $row[PageImagePath::getPersistentName()];
226            try {
227                $pageImage = PageImage::create($pageImagePath->getValue(), $this->getResource());
228            } catch (ExceptionCombo $e) {
229                LogUtility::msg("Error while creating the page image ($pageImagePath) for the page {$this->getResource()}. The image was not used. Error: {$e->getMessage()}");
230                continue;
231            }
232            /**
233             * @var PageImageUsage $pageImageUsage
234             */
235            $pageImageUsage = $row[PageImageUsage::getPersistentName()];
236            if ($pageImageUsage !== null) {
237                try {
238                    $pageImage->setUsages($pageImageUsage->getValue());
239                } catch (ExceptionCombo $e) {
240                    LogUtility::msg("Bad Usage value. Should not happen on get");
241                }
242            }
243            $pageImages[] = $pageImage;
244        }
245        return $pageImages;
246    }
247
248    /**
249     * @throws ExceptionCombo
250     */
251    public function addImage(string $wikiImagePath, $usages = null): PageImages
252    {
253
254        $pageImagePath = PageImagePath::createFromParent($this)
255            ->setFromStoreValue($wikiImagePath);
256        $row[PageImagePath::getPersistentName()] = $pageImagePath;
257        if ($usages !== null) {
258            $pageImageUsage = PageImageUsage::createFromParent($this)
259                ->setFromStoreValue($usages);
260            $row[PageImageUsage::getPersistentName()] = $pageImageUsage;
261        }
262        $this->rows[] = $row;
263
264        /**
265         * What fucked up is fucked up
266         * The runtime {@link Doku_Renderer_metadata::_recordMediaUsage()}
267         * ```
268         * meta['relation']['media'][$src] = $exist
269         * ```
270         * is only set when parsing to add page to the index
271         */
272        return $this;
273    }
274
275
276    public function getTab(): string
277    {
278        return MetaManagerForm::TAB_IMAGE_VALUE;
279    }
280
281
282    public function getDescription(): string
283    {
284        return "The illustrative images of the page";
285    }
286
287    public function getLabel(): string
288    {
289        return "Page Images";
290    }
291
292
293    public function getMutable(): bool
294    {
295        return true;
296    }
297
298    /**
299     *
300     * We check the existence of the image also when persisting,
301     * not when building
302     * because when moving a media, the images does not exist any more
303     *
304     * We can then build the the pageimages with non-existing images
305     * but we can't save
306     *
307     * @throws ExceptionCombo
308     */
309    private function checkImageExistence()
310    {
311        if ($this->pageImages !== null) {
312            foreach ($this->pageImages as $pageImage) {
313                if (!$pageImage->getImage()->exists()) {
314                    throw new ExceptionCombo("The image ({$pageImage->getImage()}) does not exist", $this->getCanonical());
315                }
316            }
317        }
318    }
319
320
321    /**
322     * @throws ExceptionCombo
323     */
324    public
325    function getDefaultImage(): ?Image
326    {
327        if (!PluginUtility::getConfValue(self::CONF_DISABLE_FIRST_IMAGE_AS_PAGE_IMAGE)) {
328            return $this->getFirstImage();
329        }
330        return null;
331    }
332
333    /**
334     * @return ImageRaster|ImageSvg|null - the first image of the page
335     * @throws ExceptionCombo
336     */
337    public function getFirstImage()
338    {
339
340        $store = $this->getReadStore();
341        if (!($store instanceof MetadataDokuWikiStore)) {
342            return null;
343        }
344        /**
345         * Our first image metadata
346         * We can't overwrite the {@link \Doku_Renderer_metadata::$firstimage first image}
347         * We put it then in directly under the root
348         */
349        $firstImageId = $store->getCurrentFromName(PageImages::FIRST_IMAGE_META_RELATION);
350
351        /**
352         * Dokuwiki first image metadata
353         */
354        if (empty($firstImageId)) {
355            $relation = $store->getCurrentFromName('relation');
356            if (!isset($relation[PageImages::FIRST_IMAGE_META_RELATION])) {
357                return null;
358            }
359
360            $firstImageId = $relation[PageImages::FIRST_IMAGE_META_RELATION];
361            if (empty($firstImageId)) {
362                return null;
363            }
364        }
365
366        /**
367         * Image Id check
368         */
369        if (media_isexternal($firstImageId)) {
370            return null;
371        }
372        return Image::createImageFromId($firstImageId);
373
374    }
375
376
377    function getChildrenClass(): array
378    {
379        return [PageImagePath::class, PageImageUsage::class];
380    }
381
382    public function getUidClass(): string
383    {
384        return PageImagePath::class;
385    }
386
387
388    public function getDefaultValue(): ?array
389    {
390
391        $defaultImage = $this->getDefaultImage();
392        $pageImagePath = null;
393        if ($defaultImage !== null) {
394            $pageImagePath = PageImagePath::createFromParent($this)
395                ->buildFromStoreValue($defaultImage->getPath()->toString());
396        }
397        return [
398            [
399                PageImagePath::getPersistentName() => $pageImagePath,
400                PageImageUsage::getPersistentName() => PageImageUsage::createFromParent($this)->buildFromStoreValue([PageImageUsage::DEFAULT])
401            ]
402        ];
403
404    }
405
406    public static function getOldPersistentNames(): array
407    {
408        return [self::OLD_PROPERTY_NAME];
409    }
410
411
412}
413