xref: /plugin/combo/action/metafacebook.php (revision 04fd306c7c155fa133ebb3669986875d65988276)
15f891b7eSNickeau<?php
25f891b7eSNickeau
3c3437056SNickeau
4*04fd306cSNickeauuse ComboStrap\ExecutionContext;
5*04fd306cSNickeauuse ComboStrap\SiteConfig;
6*04fd306cSNickeauuse ComboStrap\WikiPath;
7*04fd306cSNickeauuse ComboStrap\ExceptionCompile;
8*04fd306cSNickeauuse ComboStrap\ExceptionNotFound;
9*04fd306cSNickeauuse ComboStrap\IFetcherLocalImage;
10*04fd306cSNickeauuse ComboStrap\FileSystems;
115f891b7eSNickeauuse ComboStrap\LogUtility;
12c3437056SNickeauuse ComboStrap\Mime;
13*04fd306cSNickeauuse ComboStrap\MarkupPath;
14c3437056SNickeauuse ComboStrap\PageImageUsage;
15c3437056SNickeauuse ComboStrap\PageType;
16c3437056SNickeauuse ComboStrap\PluginUtility;
175f891b7eSNickeauuse ComboStrap\Site;
185f891b7eSNickeauuse ComboStrap\StringUtility;
195f891b7eSNickeau
205f891b7eSNickeau
215f891b7eSNickeau/**
225f891b7eSNickeau *
234cadd4f8SNickeau * Implementation of the ogp protocol
244cadd4f8SNickeau *
254cadd4f8SNickeau * followed by:
264cadd4f8SNickeau *   * Facebook: https://developers.facebook.com/docs/sharing/webmasters
274cadd4f8SNickeau *   * Linkedin:  https://www.linkedin.com/help/linkedin/answer/a521928/making-your-website-shareable-on-linkedin?lang=en
284cadd4f8SNickeau *
295f891b7eSNickeau * For the canonical meta, see {@link action_plugin_combo_metacanonical}
305f891b7eSNickeau *
315f891b7eSNickeau * Inspiration, reference:
325f891b7eSNickeau * https://github.com/twbs/bootstrap/blob/v4-dev/site/layouts/partials/social.html
335f891b7eSNickeau * https://github.com/mprins/dokuwiki-plugin-socialcards/blob/master/action.php
344cadd4f8SNickeau *
354cadd4f8SNickeau *
365f891b7eSNickeau */
375f891b7eSNickeauclass action_plugin_combo_metafacebook extends DokuWiki_Action_Plugin
385f891b7eSNickeau{
395f891b7eSNickeau
405f891b7eSNickeau    const FACEBOOK_APP_ID = "486120022012342";
415f891b7eSNickeau
425f891b7eSNickeau    /**
435f891b7eSNickeau     * The image
445f891b7eSNickeau     */
455f891b7eSNickeau    const CONF_DEFAULT_FACEBOOK_IMAGE = "defaultFacebookImage";
465f891b7eSNickeau
475f891b7eSNickeau
485f891b7eSNickeau    const CANONICAL = "facebook";
495f891b7eSNickeau
505f891b7eSNickeau
515f891b7eSNickeau    function __construct()
525f891b7eSNickeau    {
535f891b7eSNickeau        // enable direct access to language strings
545f891b7eSNickeau        // ie $this->lang
555f891b7eSNickeau        $this->setupLocale();
565f891b7eSNickeau    }
575f891b7eSNickeau
585f891b7eSNickeau    public function register(Doku_Event_Handler $controller)
595f891b7eSNickeau    {
605f891b7eSNickeau        $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'metaFacebookProcessing', array());
615f891b7eSNickeau    }
625f891b7eSNickeau
635f891b7eSNickeau    /**
645f891b7eSNickeau     *
655f891b7eSNickeau     * @param $event
665f891b7eSNickeau     */
675f891b7eSNickeau    function metaFacebookProcessing($event)
685f891b7eSNickeau    {
695f891b7eSNickeau
70*04fd306cSNickeau        $executionContext = ExecutionContext::getActualOrCreateFromEnv();
71*04fd306cSNickeau        try {
72*04fd306cSNickeau            $templateForWebPage = $executionContext->getExecutingPageTemplate();
73*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
745f891b7eSNickeau            return;
755f891b7eSNickeau        }
765f891b7eSNickeau
77*04fd306cSNickeau        if (!$templateForWebPage->isSocial()) {
785f891b7eSNickeau            return;
795f891b7eSNickeau        }
805f891b7eSNickeau
815f891b7eSNickeau
825f891b7eSNickeau        /**
835f891b7eSNickeau         * "og:url" is already created in the {@link action_plugin_combo_metacanonical}
845f891b7eSNickeau         * "og:description" is already created in the {@link action_plugin_combo_metadescription}
855f891b7eSNickeau         */
86*04fd306cSNickeau        try {
87*04fd306cSNickeau            $requestedPath = $templateForWebPage->getRequestedContextPath();
88*04fd306cSNickeau        } catch (ExceptionNotFound $e) {
89*04fd306cSNickeau            return;
90*04fd306cSNickeau        }
91*04fd306cSNickeau        $page = MarkupPath::createPageFromPathObject($requestedPath);
925f891b7eSNickeau        $facebookMeta = array(
93c3437056SNickeau            "og:title" => StringUtility::truncateString($page->getTitleOrDefault(), 70)
945f891b7eSNickeau        );
95f788f694Sgerardnico        $descriptionOrElseDokuWiki = $page->getDescriptionOrElseDokuWiki();
96f788f694Sgerardnico        if (!empty($descriptionOrElseDokuWiki)) {
97f788f694Sgerardnico            // happens in test with document without content
98f788f694Sgerardnico            $facebookMeta["og:description"] = $descriptionOrElseDokuWiki;
99f788f694Sgerardnico        }
1005f891b7eSNickeau
101*04fd306cSNickeau
102*04fd306cSNickeau        $title = Site::getName();
1035f891b7eSNickeau        if (!empty($title)) {
1045f891b7eSNickeau            $facebookMeta["og:site_name"] = $title;
1055f891b7eSNickeau        }
1065f891b7eSNickeau
1075f891b7eSNickeau        /**
1085f891b7eSNickeau         * Type of page
1095f891b7eSNickeau         */
110c3437056SNickeau        $pageType = $page->getTypeOrDefault();
111c3437056SNickeau        switch ($pageType) {
112c3437056SNickeau            case PageType::ARTICLE_TYPE:
1135f891b7eSNickeau                // https://ogp.me/#type_article
114*04fd306cSNickeau                try {
11585e82846SNickeau                    $facebookMeta["article:published_time"] = $page->getPublishedElseCreationTime()->format(DATE_ISO8601);
116*04fd306cSNickeau                } catch (ExceptionNotFound $e) {
117*04fd306cSNickeau                    // Internal error, the page should exist
118*04fd306cSNickeau                    LogUtility::error("Internal Error: We were unable to define the publication date for the page ($page)", self::CANONICAL);
119*04fd306cSNickeau
12085e82846SNickeau                }
121*04fd306cSNickeau                try {
122*04fd306cSNickeau                    $modifiedTime = $page->getModifiedTimeOrDefault();
123*04fd306cSNickeau                    $facebookMeta["article:modified_time"] = $modifiedTime->format(DATE_ISO8601);
124*04fd306cSNickeau                } catch (ExceptionNotFound $e) {
125*04fd306cSNickeau                    // Internal error, the page should exist
126*04fd306cSNickeau                    LogUtility::error("Internal Error: We were unable to define the modification date for the page ($page)", self::CANONICAL);
127*04fd306cSNickeau                }
128*04fd306cSNickeau
129*04fd306cSNickeau
130c3437056SNickeau                $facebookMeta["og:type"] = $pageType;
131c3437056SNickeau                break;
132c3437056SNickeau            default:
133c3437056SNickeau                // The default facebook value
134c3437056SNickeau                $facebookMeta["og:type"] = PageType::WEBSITE_TYPE;
135c3437056SNickeau                break;
1365f891b7eSNickeau        }
1375f891b7eSNickeau
138c3437056SNickeau
139*04fd306cSNickeau        $facebookImages = $page->getImagesForTheFollowingUsages([PageImageUsage::FACEBOOK, PageImageUsage::SOCIAL, PageImageUsage::ALL]);
1405f891b7eSNickeau        if (empty($facebookImages)) {
141*04fd306cSNickeau            $defaultFacebookImage = SiteConfig::getConfValue(self::CONF_DEFAULT_FACEBOOK_IMAGE);
1425f891b7eSNickeau            if (!empty($defaultFacebookImage)) {
143*04fd306cSNickeau                $dokuPath = WikiPath::createMediaPathFromId($defaultFacebookImage);
144*04fd306cSNickeau                if (FileSystems::exists($dokuPath)) {
145*04fd306cSNickeau                    try {
146*04fd306cSNickeau                        $facebookImages[] = IFetcherLocalImage::createImageFetchFromPath($dokuPath);
147*04fd306cSNickeau                    } catch (ExceptionCompile $e) {
148*04fd306cSNickeau                        LogUtility::error("We were unable to add the default facebook image ($defaultFacebookImage) because of the following error: {$e->getMessage()}", self::CANONICAL);
149*04fd306cSNickeau                    }
1505f891b7eSNickeau                } else {
1511fa8c418SNickeau                    if ($defaultFacebookImage != ":logo-facebook.png") {
152*04fd306cSNickeau                        LogUtility::error("The default facebook image ($defaultFacebookImage) does not exist", self::CANONICAL);
1535f891b7eSNickeau                    }
1545f891b7eSNickeau                }
1555f891b7eSNickeau            }
1565f891b7eSNickeau        }
1575f891b7eSNickeau        if (!empty($facebookImages)) {
15821913ab3SNickeau
15921913ab3SNickeau            /**
16021913ab3SNickeau             * One of image/jpeg, image/gif or image/png
16121913ab3SNickeau             * As stated here: https://developers.facebook.com/docs/sharing/webmasters#images
16221913ab3SNickeau             **/
163*04fd306cSNickeau            $facebookMimes = [Mime::JPEG, Mime::GIF, Mime::PNG];
1645f891b7eSNickeau            foreach ($facebookImages as $facebookImage) {
1655f891b7eSNickeau
166*04fd306cSNickeau                $path = $facebookImage->getSourcePath();
167*04fd306cSNickeau                if (!FileSystems::exists($path)) {
168*04fd306cSNickeau                    LogUtility::error("The image ($path) does not exist and was not added", self::CANONICAL);
16921913ab3SNickeau                    continue;
17021913ab3SNickeau                }
171*04fd306cSNickeau                try {
172*04fd306cSNickeau                    $mime = FileSystems::getMime($path);
173*04fd306cSNickeau                } catch (ExceptionNotFound $e) {
174*04fd306cSNickeau                    LogUtility::internalError($e->getMessage());
17521913ab3SNickeau                    continue;
17621913ab3SNickeau                }
177*04fd306cSNickeau                if (!in_array($mime->toString(), $facebookMimes)) {
178*04fd306cSNickeau                    continue;
179*04fd306cSNickeau                }
1805f891b7eSNickeau
1815f891b7eSNickeau                $toSmall = false;
1825f891b7eSNickeau
1835f891b7eSNickeau                // There is a minimum size constraint of 200px by 200px
18482a60d03SNickeau                // The try is in case we can't get the width and height
18582a60d03SNickeau                try {
18682a60d03SNickeau                    $intrinsicWidth = $facebookImage->getIntrinsicWidth();
18782a60d03SNickeau                    $intrinsicHeight = $facebookImage->getIntrinsicHeight();
188*04fd306cSNickeau                } catch (ExceptionCompile $e) {
189*04fd306cSNickeau                    LogUtility::error("No image was added for facebook. Error while retrieving the dimension of the image: {$e->getMessage()}", self::CANONICAL);
19082a60d03SNickeau                    break;
1915f891b7eSNickeau                }
19282a60d03SNickeau
19382a60d03SNickeau                if ($intrinsicWidth < 200) {
19482a60d03SNickeau                    $toSmall = true;
19582a60d03SNickeau                } else {
19682a60d03SNickeau                    $facebookMeta["og:image:width"] = $intrinsicWidth;
19782a60d03SNickeau                    if ($intrinsicHeight < 200) {
19882a60d03SNickeau                        $toSmall = true;
19982a60d03SNickeau                    } else {
20082a60d03SNickeau                        $facebookMeta["og:image:height"] = $intrinsicHeight;
2015f891b7eSNickeau                    }
2025f891b7eSNickeau                }
2035f891b7eSNickeau
2045f891b7eSNickeau                if ($toSmall) {
20582a60d03SNickeau                    $message = "The facebook image ($facebookImage) is too small (" . $intrinsicWidth . " x " . $intrinsicHeight . "). The minimum size constraint is 200px by 200px";
206*04fd306cSNickeau                    try {
207*04fd306cSNickeau                        $firstImagePath = $page->getFirstImage()->getSourcePath();
208c3437056SNickeau                        if (
209*04fd306cSNickeau                            $path->toAbsolutePath()->toAbsoluteId() !== $firstImagePath->toAbsolutePath()->toAbsoluteId()
210c3437056SNickeau                        ) {
211*04fd306cSNickeau                            // specified image
212*04fd306cSNickeau                            LogUtility::error($message, self::CANONICAL);
2135f891b7eSNickeau                        } else {
214*04fd306cSNickeau                            // first image
2155f891b7eSNickeau                            LogUtility::log2BrowserConsole($message);
2165f891b7eSNickeau                        }
217*04fd306cSNickeau                    } catch (ExceptionNotFound $e) {
218*04fd306cSNickeau                        // no first image
219*04fd306cSNickeau                        LogUtility::error($message, self::CANONICAL);
220*04fd306cSNickeau                    }
2215f891b7eSNickeau                }
2225f891b7eSNickeau
2235f891b7eSNickeau
2245f891b7eSNickeau                /**
2255f891b7eSNickeau                 * We may don't known the dimensions
2265f891b7eSNickeau                 */
2275f891b7eSNickeau                if (!$toSmall) {
228*04fd306cSNickeau                    $facebookMeta["og:image:type"] = $mime->toString();
229*04fd306cSNickeau                    try {
230*04fd306cSNickeau                        $facebookMeta["og:image"] = $facebookImage->getFetchUrl()->toAbsoluteUrlString();
231*04fd306cSNickeau                    } catch (ExceptionCompile $e) {
232*04fd306cSNickeau                        // Oeps
233*04fd306cSNickeau                        LogUtility::internalError("Og Image could not be added. Error: {$e->getMessage()}", self::CANONICAL);
2345f891b7eSNickeau                    }
2355f891b7eSNickeau                    // One image only
2365f891b7eSNickeau                    break;
2375f891b7eSNickeau                }
2385f891b7eSNickeau
2395f891b7eSNickeau            }
2405f891b7eSNickeau        }
2415f891b7eSNickeau
2425f891b7eSNickeau
2435f891b7eSNickeau        $facebookMeta["fb:app_id"] = self::FACEBOOK_APP_ID;
2445f891b7eSNickeau
245*04fd306cSNickeau        // FYI default if not set by facebook: "en_US"
246*04fd306cSNickeau        $locale = ComboStrap\Locale::createForPage($page, "_")->getValueOrDefault();
2471fa8c418SNickeau        $facebookMeta["og:locale"] = $locale;
2485f891b7eSNickeau
2495f891b7eSNickeau
2505f891b7eSNickeau        /**
2515f891b7eSNickeau         * Add the properties
2525f891b7eSNickeau         */
2535f891b7eSNickeau        foreach ($facebookMeta as $property => $content) {
2545f891b7eSNickeau            $event->data['meta'][] = array("property" => $property, "content" => $content);
2555f891b7eSNickeau        }
2565f891b7eSNickeau
2575f891b7eSNickeau
2585f891b7eSNickeau    }
2595f891b7eSNickeau
2605f891b7eSNickeau}
261