1<?php 2 3 4use ComboStrap\ExceptionNotFound; 5use ComboStrap\MarkupPath; 6use ComboStrap\Site; 7 8/** 9 * Add all canonical HTML metadata 10 * 11 * In 1.14. we keep the name of the class with canonical to be able to update 12 * Above 1.15, in a release branch, you can just modify it 13 */ 14class action_plugin_combo_metacanonical extends DokuWiki_Action_Plugin 15{ 16 17 18 const APPLE_MOBILE_WEB_APP_TITLE_META = "apple-mobile-web-app-title"; 19 const APPLICATION_NAME_META = "application-name"; 20 21 /** 22 * @throws ExceptionNotFound 23 */ 24 public static function getContextPageForHeadHtmlMeta(): MarkupPath 25 { 26 27 try { 28 $page = MarkupPath::createFromRequestedPage(); 29 } catch (ExceptionNotFound $e) { 30 // $_SERVER['SCRIPT_NAME']== "/lib/exe/mediamanager.php" 31 // $ID is null 32 // Admin call for instance 33 throw new ExceptionNotFound("No requested page"); 34 } 35 36 /** 37 * No metadata for slot page 38 */ 39 if ($page->isSlot()) { 40 throw new ExceptionNotFound("Secondary slot"); 41 } 42 43 return $page; 44 } 45 46 public function register(Doku_Event_Handler $controller) 47 { 48 /** 49 * https://www.dokuwiki.org/devel:event:tpl_metaheader_output 50 */ 51 $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'htmlHeadMetadataProcessing', array()); 52 53 54 } 55 56 57 function htmlHeadMetadataProcessing($event) 58 { 59 60 try { 61 $page = self::getContextPageForHeadHtmlMeta(); 62 } catch (ExceptionNotFound $e) { 63 return; 64 } 65 66 67 /** 68 * Add the canonical metadata value 69 */ 70 $this->canonicalHeadMetadata($event, $page); 71 /** 72 * Add the app name value 73 */ 74 $this->appNameMetadata($event, $page); 75 76 } 77 78 /** 79 * Dokuwiki has already a canonical methodology 80 * https://www.dokuwiki.org/canonical 81 * 82 */ 83 private function canonicalHeadMetadata($event, MarkupPath $page) 84 { 85 86 /** 87 * Where do we pick the canonical URL 88 * Canonical from meta 89 * 90 * FYI: The creation of the link was extracted from 91 * {@link wl()} that call {@link idfilter()} that performs just a replacement 92 * Calling the wl function will not work because 93 * {@link wl()} use the constant DOKU_URL that is set before any test via getBaseURL(true) 94 */ 95 $canonicalUrl = $page->getAbsoluteCanonicalUrl(); 96 97 /** 98 * Replace the meta entry 99 * 100 * First search the key of the meta array 101 */ 102 $canonicalKey = ""; 103 $canonicalRelArray = array("rel" => "canonical", "href" => $canonicalUrl); 104 foreach ($event->data['link'] as $key => $link) { 105 if ($link["rel"] == "canonical") { 106 $canonicalKey = $key; 107 } 108 } 109 if ($canonicalKey != "") { 110 // Update 111 $event->data['link'][$canonicalKey] = $canonicalRelArray; 112 } else { 113 // Add 114 $event->data['link'][] = $canonicalRelArray; 115 } 116 117 /** 118 * Add the Og canonical meta 119 * https://developers.facebook.com/docs/sharing/webmasters/getting-started/versioned-link/ 120 */ 121 $canonicalOgKeyKey = ""; 122 $canonicalPropertyKey = "og:url"; 123 $canonicalOgArray = array("property" => $canonicalPropertyKey, "content" => $canonicalUrl); 124 // Search if the canonical property is already present 125 foreach ($event->data['meta'] as $key => $meta) { 126 if (array_key_exists("property", $meta)) { 127 /** 128 * We may have several properties 129 */ 130 if ($meta["property"] == $canonicalPropertyKey) { 131 $canonicalOgKeyKey = $key; 132 } 133 } 134 } 135 if ($canonicalOgKeyKey != "") { 136 // Update 137 $event->data['meta'][$canonicalOgKeyKey] = $canonicalOgArray; 138 } else { 139 // Add 140 $event->data['meta'][] = $canonicalOgArray; 141 } 142 } 143 144 /** 145 * Add the following meta 146 * <meta name="apple-mobile-web-app-title" content="appName"> 147 * <meta name="application-name" content="appName"> 148 * 149 * @param $event 150 * @param MarkupPath $page 151 * @return void 152 */ 153 private function appNameMetadata($event, MarkupPath $page) 154 { 155 $applicationName = Site::getName(); 156 157 $applicationMetaNameValues = [ 158 self::APPLE_MOBILE_WEB_APP_TITLE_META, 159 self::APPLICATION_NAME_META 160 ]; 161 $metaNameKeyProperty = "name"; 162 foreach ($applicationMetaNameValues as $applicationNameValue) { 163 164 $appMobileWebAppTitle = array($metaNameKeyProperty => $applicationNameValue, "content" => $applicationName);; 165 try { 166 $metaKey = $this->getMetaArrayIndex($metaNameKeyProperty, $applicationNameValue, $event->data['meta']); 167 // Update 168 $event->data['meta'][$metaKey] = $appMobileWebAppTitle; 169 } catch (ExceptionNotFound $e) { 170 // Add 171 $event->data['meta'][] = $appMobileWebAppTitle; 172 } 173 } 174 175 176 } 177 178 /** 179 * @throws \ComboStrap\ExceptionNotFound 180 */ 181 private function getMetaArrayIndex(string $keyToSearch, string $keyValueToSearch, $metas) 182 { 183 // Search if the canonical property is already present 184 foreach ($metas as $metaKey => $metaValue) { 185 if (array_key_exists($keyToSearch, $metaValue)) { 186 /** 187 * We may have several properties 188 */ 189 if ($metaValue[$keyToSearch] == $keyValueToSearch) { 190 return $metaKey; 191 } 192 } 193 } 194 throw new ExceptionNotFound("The meta key {$keyToSearch} with the value {$keyValueToSearch} was not found"); 195 } 196 197 198} 199