1<?php 2 3 4namespace ComboStrap\Meta\Field; 5 6 7use ComboStrap\ExceptionNotFound; 8use ComboStrap\ExecutionContext; 9use ComboStrap\FileSystems; 10use ComboStrap\LogUtility; 11use ComboStrap\MarkupPath; 12use ComboStrap\Meta\Api\Metadata; 13use ComboStrap\Meta\Api\MetadataText; 14use ComboStrap\MetaManagerForm; 15use ComboStrap\TemplateEngine; 16use ComboStrap\Site; 17use ComboStrap\SlotSystem; 18use ComboStrap\Tag\BarTag; 19 20class PageTemplateName extends MetadataText 21{ 22 23 24 public const PROPERTY_NAME = "template"; 25 public const PROPERTY_NAME_OLD = "layout"; 26 public const HOLY_TEMPLATE_VALUE = "holy"; 27 public const MEDIUM_TEMPLATE_VALUE = "medium"; 28 public const LANDING_TEMPLATE_VALUE = "landing"; 29 public const INDEX_TEMPLATE_VALUE = "index"; 30 public const HAMBURGER_TEMPLATE_VALUE = "hamburger"; 31 public const BLANK_TEMPLATE_VALUE = "blank"; 32 33 /** 34 * Not public, used in test to overwrite it to {@link PageTemplateName::BLANK_TEMPLATE_VALUE} 35 * to speed up test 36 */ 37 const CONF_DEFAULT_NAME = "defaultLayoutName"; 38 39 40 /** 41 * App page 42 */ 43 const APP_PREFIX = "app-"; 44 const APP_EDIT = self::APP_PREFIX . ExecutionContext::EDIT_ACTION; 45 const APP_LOGIN = self::APP_PREFIX . ExecutionContext::LOGIN_ACTION; 46 const APP_SEARCH = self::APP_PREFIX . ExecutionContext::SEARCH_ACTION; 47 const APP_REGISTER = self::APP_PREFIX . ExecutionContext::REGISTER_ACTION; 48 const APP_RESEND_PWD = self::APP_PREFIX . ExecutionContext::RESEND_PWD_ACTION; 49 const APP_REVISIONS = self::APP_PREFIX . ExecutionContext::REVISIONS_ACTION; 50 const APP_DIFF = self::APP_PREFIX . ExecutionContext::DIFF_ACTION; 51 const APP_INDEX = self::APP_PREFIX . ExecutionContext::INDEX_ACTION; 52 const APP_PROFILE = self::APP_PREFIX . ExecutionContext::PROFILE_ACTION; 53 54 /** 55 * @deprecated for {@link self::MEDIUM_TEMPLATE_VALUE} 56 * changed to medium (median has too much mathematics connotation) 57 * medium: halfway between two extremes 58 */ 59 const MEDIAN_OLD_TEMPLATE = "median"; 60 const HOLY_MEDIUM_LAYOUT = "holy-medium"; 61 const INDEX_MEDIUM_LAYOUT = "index-medium"; 62 63 64 public static function createFromPage(MarkupPath $page): PageTemplateName 65 { 66 return (new PageTemplateName()) 67 ->setResource($page); 68 } 69 70 static public function getTab(): string 71 { 72 return MetaManagerForm::TAB_PAGE_VALUE; 73 } 74 75 static public function getDescription(): string 76 { 77 return "A template applies a layout on your page"; 78 } 79 80 static public function getLabel(): string 81 { 82 return "Template"; 83 } 84 85 public function getPossibleValues(): ?array 86 { 87 try { 88 $templateNames = []; 89 $directories = TemplateEngine::createFromContext() 90 ->getTemplateSearchDirectories(); 91 foreach ($directories as $directory) { 92 $files = FileSystems::getChildrenLeaf($directory); 93 foreach ($files as $file) { 94 $lastNameWithoutExtension = $file->getLastNameWithoutExtension(); 95 if (strpos($lastNameWithoutExtension, self::APP_PREFIX) === 0) { 96 continue; 97 } 98 if ($file->getExtension() === TemplateEngine::EXTENSION_HBS) { 99 100 $templateNames[] = $lastNameWithoutExtension; 101 } 102 } 103 } 104 sort($templateNames); 105 return $templateNames; 106 } catch (ExceptionNotFound $e) { 107 LogUtility::error("No template could be found", self::CANONICAL, $e); 108 return []; 109 } 110 } 111 112 113 static public function getName(): string 114 { 115 return self::PROPERTY_NAME; 116 } 117 118 static public function getPersistenceType(): string 119 { 120 return Metadata::PERSISTENT_METADATA; 121 } 122 123 static public function isMutable(): bool 124 { 125 return true; 126 } 127 128 /** 129 * @return string 130 */ 131 public function getDefaultValue(): string 132 { 133 /** 134 * @var MarkupPath $page 135 */ 136 $page = $this->getResource(); 137 138 /** 139 * Slot first 140 * because they are also root item page 141 */ 142 try { 143 switch ($page->getPathObject()->getLastNameWithoutExtension()) { 144 case SlotSystem::getSidebarName(): 145 case SlotSystem::getMainHeaderSlotName(): 146 case SlotSystem::getMainFooterSlotName(): 147 case SlotSystem::getMainSideSlotName(): 148 return self::INDEX_MEDIUM_LAYOUT; 149 case SlotSystem::getPageHeaderSlotName(): 150 case SlotSystem::getPageFooterSlotName(): 151 /** 152 * Header and footer contains bar 153 * {@link \syntax_plugin_combo_menubar menubar} or 154 * {@link \syntax_plugin_combo_bar} 155 * They therefore should not be constrained 156 * Landing page is perfect 157 */ 158 return self::LANDING_TEMPLATE_VALUE; 159 } 160 } catch (ExceptionNotFound $e) { 161 // No last name not installed 162 } 163 164 165 if ($page->isRootHomePage()) { 166 /** 167 * Ultimattely a {@link self::LANDING_TEMPLATE_VALUE} 168 * but for that the user needs to add {@link BarTag} 169 * 170 */ 171 return self::HAMBURGER_TEMPLATE_VALUE; 172 } 173 if ($page->isRootItemPage()) { 174 /** 175 * Home/Root item does not really belongs to the same 176 * namespace, we don't show therefore a sidebar 177 */ 178 return self::INDEX_MEDIUM_LAYOUT; 179 } 180 181 182 /** 183 * Default by namespace 184 * 185 * Calculate the possible template 186 * prefix in order 187 */ 188 try { 189 $parentNames = $page->getPathObject()->getParent()->getNames(); 190 $templatePrefixes = []; 191 $hierarchicalName = ''; 192 foreach ($parentNames as $name) { 193 if (empty($hierarchicalName)) { 194 $hierarchicalName .= $name; 195 } else { 196 $hierarchicalName .= "-$name"; 197 } 198 $templatePrefixes[] = $name; 199 if ($hierarchicalName !== $name) { 200 $templatePrefixes[] = $hierarchicalName; 201 } 202 } 203 $templatePrefixes = array_reverse($templatePrefixes); 204 } catch (ExceptionNotFound $e) { 205 // no parent, root 206 $templatePrefixes = []; 207 } 208 209 $pageTemplateEngine = TemplateEngine::createFromContext(); 210 211 212 /** 213 * Index pages 214 */ 215 if ($page->isIndexPage()) { 216 foreach ($templatePrefixes as $templatePrefix) { 217 $templateName = "$templatePrefix-index"; 218 if ($pageTemplateEngine->templateExists($templateName)) { 219 return $templateName; 220 } 221 } 222 return self::INDEX_TEMPLATE_VALUE; 223 } 224 225 /** 226 * Item page 227 */ 228 foreach ($templatePrefixes as $templatePrefix) { 229 $templateName = "$templatePrefix-item"; 230 if ($pageTemplateEngine->templateExists($templateName)) { 231 return $templateName; 232 } 233 } 234 235 return ExecutionContext::getActualOrCreateFromEnv()->getConfig()->getDefaultLayoutName(); 236 237 238 } 239 240 static public function getCanonical(): string 241 { 242 return self::PROPERTY_NAME; 243 } 244 245 246 /** 247 * @return string 248 */ 249 public function getValueOrDefault(): string 250 { 251 252 try { 253 $value = $this->getValue(); 254 if ($value === "") { 255 return $this->getDefaultValue(); 256 } 257 return $value; 258 } catch (ExceptionNotFound $e) { 259 return $this->getDefaultValue(); 260 } 261 262 263 } 264 265 /** @noinspection PhpMissingReturnTypeInspection */ 266 public function buildFromReadStore() 267 { 268 269 $metaDataStore = $this->getReadStore(); 270 $value = $metaDataStore->getFromName(self::PROPERTY_NAME); 271 if ($value === null) { 272 $value = $metaDataStore->getFromName(self::PROPERTY_NAME_OLD); 273 } 274 if ($value === self::MEDIAN_OLD_TEMPLATE) { 275 $value = self::MEDIUM_TEMPLATE_VALUE; 276 } 277 parent::setFromStoreValueWithoutException($value); 278 return $this; 279 } 280 281 public function sendToWriteStore(): Metadata 282 { 283 284 parent::sendToWriteStore(); 285 $writeStore = $this->getWriteStore(); 286 $value = $writeStore->getFromName(self::PROPERTY_NAME_OLD); 287 if ($value !== null) { 288 // delete the old value 289 $writeStore->setFromPersistentName(self::PROPERTY_NAME_OLD, null); 290 } 291 292 return $this; 293 } 294 295 public static function getOldPersistentNames(): array 296 { 297 return [self::PROPERTY_NAME_OLD]; 298 } 299 300 301 static public function isOnForm(): bool 302 { 303 return true; 304 } 305} 306