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->existInFs()) { 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 "og:description" => $page->getDescriptionOrElseDokuWiki(), 86 ); 87 88 $title = Site::getTitle(); 89 if (!empty($title)) { 90 $facebookMeta["og:site_name"] = $title; 91 } 92 93 /** 94 * Type of page 95 */ 96 $ogType = $page->getType(); 97 if (!empty($ogType)) { 98 $facebookMeta["og:type"] = $ogType; 99 } else { 100 // The default facebook value 101 $facebookMeta["og:type"] = Page::WEBSITE_TYPE; 102 } 103 104 if ($ogType == Page::ARTICLE_TYPE) { 105 // https://ogp.me/#type_article 106 $facebookMeta["article:published_time"] = date("c", $page->getPublishedElseCreationTimeStamp()); 107 $facebookMeta["article:modified_time"] = date("c", $page->getModifiedTimestamp()); 108 } 109 110 /** 111 * @var InternalMediaLink[] 112 */ 113 $facebookImages = $page->getImageSet(); 114 if (empty($facebookImages)) { 115 $defaultFacebookImage = cleanID(PluginUtility::getConfValue(self::CONF_DEFAULT_FACEBOOK_IMAGE)); 116 if (!empty($defaultFacebookImage)) { 117 $image = InternalMediaLink::createMediaLinkFromPathId($defaultFacebookImage); 118 if ($image->exists()) { 119 $facebookImages[] = $image; 120 } else { 121 if ($defaultFacebookImage != "logo-facebook.png") { 122 LogUtility::msg("The default facebook image ($defaultFacebookImage) does not exist", LogUtility::LVL_MSG_ERROR, self::CANONICAL); 123 } 124 } 125 126 127 } 128 } 129 if (!empty($facebookImages)) { 130 131 /** 132 * One of image/jpeg, image/gif or image/png 133 * As stated here: https://developers.facebook.com/docs/sharing/webmasters#images 134 **/ 135 $facebookMime = ["image/jpeg","image/gif","image/png"]; 136 foreach ($facebookImages as $facebookImage) { 137 138 if(!in_array($facebookImage->getMime(),$facebookMime)){ 139 continue; 140 } 141 142 /** @var RasterImageLink $facebookImage */ 143 if (!($facebookImage instanceof RasterImageLink)) { 144 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"); 145 continue; 146 } 147 148 if (!$facebookImage->exists()) { 149 LogUtility::msg("The image ($facebookImage) does not exist and was not added", LogUtility::LVL_MSG_ERROR, self::CANONICAL); 150 } else { 151 152 $toSmall = false; 153 if ($facebookImage->isAnalyzable()) { 154 155 // There is a minimum size constraint of 200px by 200px 156 if ($facebookImage->getMediaWidth() < 200) { 157 $toSmall = true; 158 } else { 159 $facebookMeta["og:image:width"] = $facebookImage->getMediaWidth(); 160 if ($facebookImage->getMediaHeight() < 200) { 161 $toSmall = true; 162 } else { 163 $facebookMeta["og:image:height"] = $facebookImage->getMediaHeight(); 164 } 165 } 166 } 167 168 if ($toSmall) { 169 $message = "The facebook image ($facebookImage) is too small (" . $facebookImage->getMediaWidth() . " x " . $facebookImage->getMediaHeight() . "). The minimum size constraint is 200px by 200px"; 170 if ($facebookImage->getId() != $page->getFirstImage()->getId()) { 171 LogUtility::msg($message, LogUtility::LVL_MSG_ERROR, self::CANONICAL); 172 } else { 173 LogUtility::log2BrowserConsole($message); 174 } 175 } 176 177 178 /** 179 * We may don't known the dimensions 180 */ 181 if (!$toSmall) { 182 $mime = $facebookImage->getMime(); 183 if (!empty($mime)) { 184 $facebookMeta["og:image:type"] = $mime[1]; 185 } 186 $facebookMeta["og:image"] = $facebookImage->getAbsoluteUrl(); 187 // One image only 188 break; 189 } 190 } 191 192 } 193 } 194 195 196 $facebookMeta["fb:app_id"] = self::FACEBOOK_APP_ID; 197 198 $lang = $page->getLang(); 199 if (!empty($lang)) { 200 201 $country = $page->getCountry(); 202 if (empty($country)) { 203 $country = $lang; 204 } 205 $facebookMeta["og:locale"] = $lang . "_" . strtoupper($country); 206 207 } else { 208 209 // The Facebook default 210 $facebookMeta["og:locale"] = "en_US"; 211 212 } 213 214 /** 215 * Add the properties 216 */ 217 foreach ($facebookMeta as $property => $content) { 218 $event->data['meta'][] = array("property" => $property, "content" => $content); 219 } 220 221 222 } 223 224} 225