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