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