xref: /plugin/combo/action/metacanonical.php (revision 04fd306c7c155fa133ebb3669986875d65988276)
1007225e5Sgerardnico<?php
2007225e5Sgerardnico
3c3437056SNickeau
4*04fd306cSNickeauuse ComboStrap\ExceptionNotFound;
5*04fd306cSNickeauuse ComboStrap\MarkupPath;
63dc080c1Sgerardnicouse ComboStrap\Site;
7007225e5Sgerardnico
8007225e5Sgerardnico/**
93dc080c1Sgerardnico * Add all canonical HTML metadata
103dc080c1Sgerardnico *
113dc080c1Sgerardnico * In 1.14. we keep the name of the class with canonical to be able to update
123dc080c1Sgerardnico * Above 1.15, in a release branch, you can just modify it
13007225e5Sgerardnico */
14*04fd306cSNickeauclass action_plugin_combo_metacanonical extends DokuWiki_Action_Plugin
15007225e5Sgerardnico{
16007225e5Sgerardnico
17007225e5Sgerardnico
183dc080c1Sgerardnico    const APPLE_MOBILE_WEB_APP_TITLE_META = "apple-mobile-web-app-title";
193dc080c1Sgerardnico    const APPLICATION_NAME_META = "application-name";
203dc080c1Sgerardnico
21*04fd306cSNickeau    /**
22*04fd306cSNickeau     * @throws ExceptionNotFound
23*04fd306cSNickeau     */
24*04fd306cSNickeau    public static function getContextPageForHeadHtmlMeta(): MarkupPath
25*04fd306cSNickeau    {
26*04fd306cSNickeau
27*04fd306cSNickeau        try {
28*04fd306cSNickeau            $page = MarkupPath::createFromRequestedPage();
29*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
30*04fd306cSNickeau            // $_SERVER['SCRIPT_NAME']== "/lib/exe/mediamanager.php"
31*04fd306cSNickeau            // $ID is null
32*04fd306cSNickeau            // Admin call for instance
33*04fd306cSNickeau            throw new ExceptionNotFound("No requested page");
34*04fd306cSNickeau        }
35*04fd306cSNickeau
36*04fd306cSNickeau        /**
37*04fd306cSNickeau         * No metadata for slot page
38*04fd306cSNickeau         */
39*04fd306cSNickeau        if ($page->isSlot()) {
40*04fd306cSNickeau            throw new ExceptionNotFound("Secondary slot");
41*04fd306cSNickeau        }
42*04fd306cSNickeau
43*04fd306cSNickeau        return $page;
44*04fd306cSNickeau    }
45*04fd306cSNickeau
46007225e5Sgerardnico    public function register(Doku_Event_Handler $controller)
47007225e5Sgerardnico    {
4885e82846SNickeau        /**
4985e82846SNickeau         * https://www.dokuwiki.org/devel:event:tpl_metaheader_output
5085e82846SNickeau         */
513dc080c1Sgerardnico        $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'htmlHeadMetadataProcessing', array());
52c3437056SNickeau
53c3437056SNickeau
54007225e5Sgerardnico    }
55007225e5Sgerardnico
563dc080c1Sgerardnico
573dc080c1Sgerardnico    function htmlHeadMetadataProcessing($event)
58007225e5Sgerardnico    {
59007225e5Sgerardnico
60*04fd306cSNickeau        try {
61*04fd306cSNickeau            $page = self::getContextPageForHeadHtmlMeta();
62*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
639da76789Sgerardnico            return;
649da76789Sgerardnico        }
659da76789Sgerardnico
66007225e5Sgerardnico
67007225e5Sgerardnico        /**
683dc080c1Sgerardnico         * Add the canonical metadata value
69007225e5Sgerardnico         */
703dc080c1Sgerardnico        $this->canonicalHeadMetadata($event, $page);
71007225e5Sgerardnico        /**
723dc080c1Sgerardnico         * Add the app name value
733dc080c1Sgerardnico         */
743dc080c1Sgerardnico        $this->appNameMetadata($event, $page);
753dc080c1Sgerardnico
763dc080c1Sgerardnico    }
773dc080c1Sgerardnico
783dc080c1Sgerardnico    /**
793dc080c1Sgerardnico     * Dokuwiki has already a canonical methodology
803dc080c1Sgerardnico     * https://www.dokuwiki.org/canonical
813dc080c1Sgerardnico     *
823dc080c1Sgerardnico     */
83*04fd306cSNickeau    private function canonicalHeadMetadata($event, MarkupPath $page)
843dc080c1Sgerardnico    {
853dc080c1Sgerardnico
863dc080c1Sgerardnico        /**
873dc080c1Sgerardnico         * Where do we pick the canonical URL
88007225e5Sgerardnico         * Canonical from meta
89007225e5Sgerardnico         *
90007225e5Sgerardnico         * FYI: The creation of the link was extracted from
91007225e5Sgerardnico         * {@link wl()} that call {@link idfilter()} that performs just a replacement
92007225e5Sgerardnico         * Calling the wl function will not work because
93007225e5Sgerardnico         * {@link wl()} use the constant DOKU_URL that is set before any test via getBaseURL(true)
94007225e5Sgerardnico         */
95c3437056SNickeau        $canonicalUrl = $page->getAbsoluteCanonicalUrl();
96007225e5Sgerardnico
97007225e5Sgerardnico        /**
98007225e5Sgerardnico         * Replace the meta entry
99007225e5Sgerardnico         *
100007225e5Sgerardnico         * First search the key of the meta array
101007225e5Sgerardnico         */
102007225e5Sgerardnico        $canonicalKey = "";
103007225e5Sgerardnico        $canonicalRelArray = array("rel" => "canonical", "href" => $canonicalUrl);
104007225e5Sgerardnico        foreach ($event->data['link'] as $key => $link) {
105007225e5Sgerardnico            if ($link["rel"] == "canonical") {
106007225e5Sgerardnico                $canonicalKey = $key;
107007225e5Sgerardnico            }
108007225e5Sgerardnico        }
109007225e5Sgerardnico        if ($canonicalKey != "") {
110007225e5Sgerardnico            // Update
111007225e5Sgerardnico            $event->data['link'][$canonicalKey] = $canonicalRelArray;
112007225e5Sgerardnico        } else {
113007225e5Sgerardnico            // Add
114007225e5Sgerardnico            $event->data['link'][] = $canonicalRelArray;
115007225e5Sgerardnico        }
116007225e5Sgerardnico
117007225e5Sgerardnico        /**
118007225e5Sgerardnico         * Add the Og canonical meta
119007225e5Sgerardnico         * https://developers.facebook.com/docs/sharing/webmasters/getting-started/versioned-link/
120007225e5Sgerardnico         */
121007225e5Sgerardnico        $canonicalOgKeyKey = "";
122007225e5Sgerardnico        $canonicalPropertyKey = "og:url";
123007225e5Sgerardnico        $canonicalOgArray = array("property" => $canonicalPropertyKey, "content" => $canonicalUrl);
124ebdc69ceSgerardnico        // Search if the canonical property is already present
125007225e5Sgerardnico        foreach ($event->data['meta'] as $key => $meta) {
126ebdc69ceSgerardnico            if (array_key_exists("property", $meta)) {
127ebdc69ceSgerardnico                /**
128ebdc69ceSgerardnico                 * We may have several properties
129ebdc69ceSgerardnico                 */
130007225e5Sgerardnico                if ($meta["property"] == $canonicalPropertyKey) {
131007225e5Sgerardnico                    $canonicalOgKeyKey = $key;
132007225e5Sgerardnico                }
133007225e5Sgerardnico            }
134ebdc69ceSgerardnico        }
135007225e5Sgerardnico        if ($canonicalOgKeyKey != "") {
136007225e5Sgerardnico            // Update
137007225e5Sgerardnico            $event->data['meta'][$canonicalOgKeyKey] = $canonicalOgArray;
138007225e5Sgerardnico        } else {
139007225e5Sgerardnico            // Add
140007225e5Sgerardnico            $event->data['meta'][] = $canonicalOgArray;
141007225e5Sgerardnico        }
1423dc080c1Sgerardnico    }
1433dc080c1Sgerardnico
1443dc080c1Sgerardnico    /**
1453dc080c1Sgerardnico     * Add the following meta
1463dc080c1Sgerardnico     * <meta name="apple-mobile-web-app-title" content="appName">
1473dc080c1Sgerardnico     * <meta name="application-name" content="appName">
1483dc080c1Sgerardnico     *
1493dc080c1Sgerardnico     * @param $event
150*04fd306cSNickeau     * @param MarkupPath $page
1513dc080c1Sgerardnico     * @return void
1523dc080c1Sgerardnico     */
153*04fd306cSNickeau    private function appNameMetadata($event, MarkupPath $page)
1543dc080c1Sgerardnico    {
1553dc080c1Sgerardnico        $applicationName = Site::getName();
1563dc080c1Sgerardnico
1573dc080c1Sgerardnico        $applicationMetaNameValues = [
1583dc080c1Sgerardnico            self::APPLE_MOBILE_WEB_APP_TITLE_META,
1593dc080c1Sgerardnico            self::APPLICATION_NAME_META
1603dc080c1Sgerardnico        ];
1613dc080c1Sgerardnico        $metaNameKeyProperty = "name";
1623dc080c1Sgerardnico        foreach ($applicationMetaNameValues as $applicationNameValue) {
1633dc080c1Sgerardnico
1643dc080c1Sgerardnico            $appMobileWebAppTitle = array($metaNameKeyProperty => $applicationNameValue, "content" => $applicationName);;
1653dc080c1Sgerardnico            try {
1663dc080c1Sgerardnico                $metaKey = $this->getMetaArrayIndex($metaNameKeyProperty, $applicationNameValue, $event->data['meta']);
1673dc080c1Sgerardnico                // Update
1683dc080c1Sgerardnico                $event->data['meta'][$metaKey] = $appMobileWebAppTitle;
169*04fd306cSNickeau            } catch (ExceptionNotFound $e) {
1703dc080c1Sgerardnico                // Add
1713dc080c1Sgerardnico                $event->data['meta'][] = $appMobileWebAppTitle;
1723dc080c1Sgerardnico            }
1733dc080c1Sgerardnico        }
1743dc080c1Sgerardnico
175007225e5Sgerardnico
176007225e5Sgerardnico    }
177007225e5Sgerardnico
1783dc080c1Sgerardnico    /**
179*04fd306cSNickeau     * @throws \ComboStrap\ExceptionNotFound
1803dc080c1Sgerardnico     */
1810db54741Sgerardnico    private function getMetaArrayIndex(string $keyToSearch, string $keyValueToSearch, $metas)
1823dc080c1Sgerardnico    {
1833dc080c1Sgerardnico        // Search if the canonical property is already present
1840db54741Sgerardnico        foreach ($metas as $metaKey => $metaValue) {
1850db54741Sgerardnico            if (array_key_exists($keyToSearch, $metaValue)) {
1863dc080c1Sgerardnico                /**
1873dc080c1Sgerardnico                 * We may have several properties
1883dc080c1Sgerardnico                 */
1890db54741Sgerardnico                if ($metaValue[$keyToSearch] == $keyValueToSearch) {
1900db54741Sgerardnico                    return $metaKey;
1913dc080c1Sgerardnico                }
1923dc080c1Sgerardnico            }
1933dc080c1Sgerardnico        }
194*04fd306cSNickeau        throw new ExceptionNotFound("The meta key {$keyToSearch} with the value {$keyValueToSearch} was not found");
1953dc080c1Sgerardnico    }
1963dc080c1Sgerardnico
1973dc080c1Sgerardnico
198007225e5Sgerardnico}
199