1<?php
2
3
4namespace ComboStrap;
5
6
7/**
8 * This class represents a page layout slots
9 */
10class TemplateSlot
11{
12    public const SLOT_IDS = [
13        self::PAGE_SIDE_ID,
14        self::PAGE_HEADER_ID,
15        self::PAGE_MAIN_ID,
16        self::PAGE_FOOTER_ID,
17        self::MAIN_HEADER_ID,
18        self::MAIN_CONTENT_ID,
19        self::MAIN_SIDE_ID,
20        self::MAIN_FOOTER_ID
21    ];
22    public const PAGE_HEADER_ID = "page-header";
23    public const PAGE_FOOTER_ID = "page-footer";
24    public const MAIN_SIDE_ID = "main-side";
25    public const PAGE_SIDE_ID = "page-side";
26    public const MAIN_CONTENT_ID = "main-content";
27    public const MAIN_FOOTER_ID = "main-footer";
28    public const MAIN_HEADER_ID = "main-header";
29    public const PAGE_MAIN_ID = "page-main";
30    public const CONF_PAGE_MAIN_SIDEKICK_NAME_DEFAULT = Site::SLOT_MAIN_SIDE_NAME;
31    public const CONF_PAGE_HEADER_NAME = "combo-conf-008";
32    public const CONF_PAGE_FOOTER_NAME = "combo-conf-009";
33    public const CONF_PAGE_HEADER_NAME_DEFAULT = "slot_header";
34    public const CONF_PAGE_FOOTER_NAME_DEFAULT = "slot_footer";
35    public const CONF_PAGE_MAIN_SIDEKICK_NAME = "sidekickSlotPageName";
36    const MAIN_TOC_ID = "main-toc";
37    const SLOT_MAIN_HEADER_PATH_NAME = "slot_main_header";
38    const SLOT_MAIN_FOOTER_PATH_NAME = "slot_main_footer";
39
40
41    /**
42     * @var WikiPath - the context path of this slot
43     */
44    private WikiPath $contextPath;
45    /**
46     * @var FetcherMarkup - the fetcher if this is a slot and has a page fragment source
47     */
48    private FetcherMarkup $fetcherFragment;
49    private string $elementId;
50
51
52    public function __construct(string $elementId, WikiPath $contextPath)
53    {
54
55        $this->elementId = $elementId;
56        if (!in_array($elementId, self::SLOT_IDS)) {
57            throw new ExceptionRuntimeInternal("$elementId is not a valid slot id. Valid ids are (" . ArrayUtility::formatAsString(self::SLOT_IDS) . ").");
58        }
59        $this->contextPath = $contextPath;
60
61
62    }
63
64    public static function createFromElementId(string $elementId, WikiPath $contextPath = null): TemplateSlot
65    {
66
67        if ($contextPath === null) {
68            $contextPath = ExecutionContext::getActualOrCreateFromEnv()
69                ->getConfig()
70                ->getDefaultContextPath();
71        }
72        return new TemplateSlot($elementId, $contextPath);
73
74    }
75
76    public static function createFromPathName($pathNameWithoutExtension): TemplateSlot
77    {
78        return self::createFromElementId(self::getElementIdFromPathName($pathNameWithoutExtension));
79    }
80
81    private static function getElementIdFromPathName($pathNameWithoutExtension): string
82    {
83        if ($pathNameWithoutExtension === SlotSystem::getPageHeaderSlotName()) {
84            return self::PAGE_HEADER_ID;
85        }
86
87        if ($pathNameWithoutExtension === SlotSystem::getPageFooterSlotName()) {
88            return self::PAGE_FOOTER_ID;
89        }
90        if ($pathNameWithoutExtension === SlotSystem::getSidebarName()) {
91            return self::PAGE_SIDE_ID;
92        }
93
94        if ($pathNameWithoutExtension === SlotSystem::getMainSideSlotName()) {
95            return self::MAIN_SIDE_ID;
96        }
97        if ($pathNameWithoutExtension === self::SLOT_MAIN_HEADER_PATH_NAME) {
98            return self::MAIN_HEADER_ID;
99        }
100        if ($pathNameWithoutExtension === self::SLOT_MAIN_FOOTER_PATH_NAME) {
101            return self::MAIN_FOOTER_ID;
102        }
103        throw new ExceptionRuntimeInternal("Internal: The markup name ($pathNameWithoutExtension) was unexpected, it's not a slot");
104
105    }
106
107
108    public
109    static function getPathNameFromElementId($elementId)
110    {
111        switch ($elementId) {
112            case self::PAGE_HEADER_ID:
113                return SlotSystem::getPageHeaderSlotName();
114            case self::PAGE_FOOTER_ID:
115                return SlotSystem::getPageFooterSlotName();
116            case self::MAIN_CONTENT_ID:
117                throw new ExceptionRuntimeInternal("Main content area is not a slot and does not have any last slot name");
118            case self::PAGE_SIDE_ID:
119                return SlotSystem::getSidebarName();
120            case self::MAIN_SIDE_ID:
121                return SlotSystem::getMainSideSlotName();
122            case self::MAIN_HEADER_ID:
123                return self::SLOT_MAIN_HEADER_PATH_NAME;
124            case self::MAIN_FOOTER_ID:
125                return self::SLOT_MAIN_FOOTER_PATH_NAME;
126            default:
127                throw new ExceptionRuntimeInternal("Internal: The element ($elementId) was unexpected, it's not a slot");
128        }
129
130    }
131
132    /**
133     *
134     * @return WikiPath
135     */
136    public
137     function getDefaultSlotContentPath(): WikiPath
138    {
139        return WikiPath::createComboResource(":slot:{$this->getElementId()}.md");
140    }
141
142
143    /**
144     */
145    public
146    function getLastFileNameForFragment()
147    {
148        $elementId = $this->getElementId();
149        return self::getPathNameFromElementId($elementId);
150    }
151
152
153    public
154    function __toString()
155    {
156        return "Slot {$this->getElementId()} for {$this->contextPath}";
157    }
158
159
160    /**
161     * @throws ExceptionNotFound - if the area is not a slot or there is no path found
162     */
163    private
164    function getFragmentPath()
165    {
166
167        // Main content
168        $requestedPath = $this->contextPath;
169        if ($this->getElementId() === self::MAIN_CONTENT_ID) {
170            return $requestedPath;
171        }
172        // Slot
173        $contextExtension = $requestedPath->getExtension();
174        try {
175            return FileSystems::closest($requestedPath, $this->getLastFileNameForFragment() . '.' . $contextExtension);
176        } catch (ExceptionNotFound $e) {
177            foreach (WikiPath::ALL_MARKUP_EXTENSIONS as $markupExtension) {
178                if ($markupExtension == $contextExtension) {
179                    continue;
180                }
181                try {
182                    return FileSystems::closest($requestedPath, $this->getLastFileNameForFragment() . '.' . $contextExtension);
183                } catch (ExceptionNotFound $e) {
184                    // not found, we let it go to the default if needed
185                }
186            }
187        }
188
189
190        return $this->getDefaultSlotContentPath();
191
192
193
194    }
195
196
197    /**
198     * @throws ExceptionNotFound if the page/markup fragment was not found (a container element does not have any also)
199     */
200    public
201    function getMarkupFetcher(): FetcherMarkup
202    {
203        if (isset($this->fetcherFragment)) {
204            return $this->fetcherFragment;
205        }
206        /**
207         * Rebuild the fragment if any
208         */
209        $fragmentPath = $this->getFragmentPath();
210        $contextPath = $this->contextPath;
211        try {
212            $this->fetcherFragment = FetcherMarkup::createXhtmlMarkupFetcherFromPath($fragmentPath, $contextPath);
213        } catch (ExceptionNotExists $e) {
214            throw new ExceptionNotFound("The fragment path ($fragmentPath) was no found");
215        }
216        return $this->fetcherFragment;
217    }
218
219    public
220    function getElementId(): string
221    {
222        return $this->elementId;
223    }
224
225    public function getPathName()
226    {
227        return self::getPathNameFromElementId($this->getElementId());
228    }
229
230
231}
232