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