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