1<?php
2
3require_once(__DIR__ . '/../ComboStrap/PluginUtility.php');
4
5use ComboStrap\DokuPath;
6use ComboStrap\Image;
7use ComboStrap\LogUtility;
8use ComboStrap\Mime;
9use ComboStrap\Page;
10use ComboStrap\PageImage;
11use ComboStrap\PageImageUsage;
12use ComboStrap\PageType;
13use ComboStrap\PluginUtility;
14use ComboStrap\Site;
15use ComboStrap\StringUtility;
16
17
18
19
20/**
21 *
22 * For the canonical meta, see {@link action_plugin_combo_metacanonical}
23 *
24 * Inspiration, reference:
25 * https://developers.facebook.com/docs/sharing/webmasters
26 * https://github.com/twbs/bootstrap/blob/v4-dev/site/layouts/partials/social.html
27 * https://github.com/mprins/dokuwiki-plugin-socialcards/blob/master/action.php
28 */
29class action_plugin_combo_metafacebook extends DokuWiki_Action_Plugin
30{
31
32    const FACEBOOK_APP_ID = "486120022012342";
33
34    /**
35     * The image
36     */
37    const CONF_DEFAULT_FACEBOOK_IMAGE = "defaultFacebookImage";
38
39
40    const CANONICAL = "facebook";
41
42
43    function __construct()
44    {
45        // enable direct access to language strings
46        // ie $this->lang
47        $this->setupLocale();
48    }
49
50    public function register(Doku_Event_Handler $controller)
51    {
52        $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'metaFacebookProcessing', array());
53    }
54
55    /**
56     *
57     * @param $event
58     */
59    function metaFacebookProcessing($event)
60    {
61
62        global $ID;
63        if (empty($ID)) {
64            // $ID is null for media
65            return;
66        }
67
68
69        $page = Page::createPageFromId($ID);
70        if (!$page->exists()) {
71            return;
72        }
73
74        /**
75         * No social for bars
76         */
77        if ($page->isSlot()) {
78            return;
79        }
80
81
82        /**
83         * "og:url" is already created in the {@link action_plugin_combo_metacanonical}
84         * "og:description" is already created in the {@link action_plugin_combo_metadescription}
85         */
86        $facebookMeta = array(
87            "og:title" => StringUtility::truncateString($page->getTitleOrDefault(), 70)
88        );
89        $descriptionOrElseDokuWiki = $page->getDescriptionOrElseDokuWiki();
90        if (!empty($descriptionOrElseDokuWiki)) {
91            // happens in test with document without content
92            $facebookMeta["og:description"] = $descriptionOrElseDokuWiki;
93        }
94
95        $title = Site::getTitle();
96        if (!empty($title)) {
97            $facebookMeta["og:site_name"] = $title;
98        }
99
100        /**
101         * Type of page
102         */
103        $pageType = $page->getTypeOrDefault();
104        switch ($pageType) {
105            case PageType::ARTICLE_TYPE:
106                // https://ogp.me/#type_article
107                $facebookMeta["article:published_time"] = $page->getPublishedElseCreationTime()->format(DATE_ISO8601);
108                $modifiedTime = $page->getModifiedTimeOrDefault();
109                if ($modifiedTime !== null) {
110                    $facebookMeta["article:modified_time"] = $modifiedTime->format(DATE_ISO8601);
111                }
112                $facebookMeta["og:type"] = $pageType;
113                break;
114            default:
115                // The default facebook value
116                $facebookMeta["og:type"] = PageType::WEBSITE_TYPE;
117                break;
118        }
119
120
121        /**
122         * @var Image[]
123         */
124        $facebookImages = $page->getImagesOrDefaultForTheFollowingUsages([PageImageUsage::FACEBOOK, PageImageUsage::SOCIAL, PageImageUsage::ALL]);
125        if (empty($facebookImages)) {
126            $defaultFacebookImage = PluginUtility::getConfValue(self::CONF_DEFAULT_FACEBOOK_IMAGE);
127            if (!empty($defaultFacebookImage)) {
128                DokuPath::addRootSeparatorIfNotPresent($defaultFacebookImage);
129                $image = Image::createImageFromId($defaultFacebookImage);
130                if ($image->exists()) {
131                    $facebookImages[] = $image;
132                } else {
133                    if ($defaultFacebookImage != ":logo-facebook.png") {
134                        LogUtility::msg("The default facebook image ($defaultFacebookImage) does not exist", LogUtility::LVL_MSG_ERROR, self::CANONICAL);
135                    }
136                }
137            }
138        }
139        if (!empty($facebookImages)) {
140
141            /**
142             * One of image/jpeg, image/gif or image/png
143             * As stated here: https://developers.facebook.com/docs/sharing/webmasters#images
144             **/
145            $facebookMime = [Mime::JPEG, Mime::GIF, Mime::PNG];
146            foreach ($facebookImages as $facebookImage) {
147
148                if (!in_array($facebookImage->getPath()->getMime()->toString(), $facebookMime)) {
149                    continue;
150                }
151
152                /** @var Image $facebookImage */
153                if (!($facebookImage->isRaster())) {
154                    LogUtility::msg("Internal: The image ($facebookImage) is not a raster image and this should not be the case for facebook", LogUtility::LVL_MSG_ERROR);
155                    continue;
156                }
157
158                if (!$facebookImage->exists()) {
159                    LogUtility::msg("The image ($facebookImage) does not exist and was not added", LogUtility::LVL_MSG_ERROR, self::CANONICAL);
160                } else {
161
162                    $toSmall = false;
163                    if ($facebookImage->isAnalyzable()) {
164
165                        // There is a minimum size constraint of 200px by 200px
166                        if ($facebookImage->getIntrinsicWidth() < 200) {
167                            $toSmall = true;
168                        } else {
169                            $facebookMeta["og:image:width"] = $facebookImage->getIntrinsicWidth();
170                            if ($facebookImage->getIntrinsicHeight() < 200) {
171                                $toSmall = true;
172                            } else {
173                                $facebookMeta["og:image:height"] = $facebookImage->getIntrinsicHeight();
174                            }
175                        }
176                    }
177
178                    if ($toSmall) {
179                        $message = "The facebook image ($facebookImage) is too small (" . $facebookImage->getIntrinsicWidth() . " x " . $facebookImage->getIntrinsicHeight() . "). The minimum size constraint is 200px by 200px";
180                        if (
181                            $facebookImage->getPath()->toAbsolutePath()->toString()
182                            !==
183                            $page->getFirstImage()->getPath()->toAbsolutePath()->toString()
184                        ) {
185                            LogUtility::msg($message, LogUtility::LVL_MSG_ERROR, self::CANONICAL);
186                        } else {
187                            LogUtility::log2BrowserConsole($message);
188                        }
189                    }
190
191
192                    /**
193                     * We may don't known the dimensions
194                     */
195                    if (!$toSmall) {
196                        $mime = $facebookImage->getPath()->getMime()->toString();
197                        if (!empty($mime)) {
198                            $facebookMeta["og:image:type"] = $mime;
199                        }
200                        $facebookMeta["og:image"] = $facebookImage->getAbsoluteUrl();
201                        // One image only
202                        break;
203                    }
204                }
205
206            }
207        }
208
209
210        $facebookMeta["fb:app_id"] = self::FACEBOOK_APP_ID;
211
212        $facebookDefaultLocale = "en_US";
213        $locale = $page->getLocale($facebookDefaultLocale);
214        $facebookMeta["og:locale"] = $locale;
215
216
217        /**
218         * Add the properties
219         */
220        foreach ($facebookMeta as $property => $content) {
221            $event->data['meta'][] = array("property" => $property, "content" => $content);
222        }
223
224
225    }
226
227}
228