1<?php 2 3use ComboStrap\RasterImageLink; 4use ComboStrap\MediaLink; 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 = Page::createPageFromId($ID); 67 if (!$page->exists()) { 68 return; 69 } 70 71 /** 72 * No social for bars 73 */ 74 if ($page->isSlot()) { 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"] = $page->getPublishedElseCreationTime()->format(DATE_ISO8601); 111 $modifiedTime = $page->getModifiedTime(); 112 if($modifiedTime!=null) { 113 $facebookMeta["article:modified_time"] = $modifiedTime->format(DATE_ISO8601); 114 } 115 } 116 117 /** 118 * @var MediaLink[] 119 */ 120 $facebookImages = $page->getLocalImageSet(); 121 if (empty($facebookImages)) { 122 $defaultFacebookImage = cleanID(PluginUtility::getConfValue(self::CONF_DEFAULT_FACEBOOK_IMAGE)); 123 if (!empty($defaultFacebookImage)) { 124 $image = MediaLink::createMediaLinkFromNonQualifiedPath($defaultFacebookImage); 125 if ($image->exists()) { 126 $facebookImages[] = $image; 127 } else { 128 if ($defaultFacebookImage != "logo-facebook.png") { 129 LogUtility::msg("The default facebook image ($defaultFacebookImage) does not exist", LogUtility::LVL_MSG_ERROR, self::CANONICAL); 130 } 131 } 132 133 134 } 135 } 136 if (!empty($facebookImages)) { 137 138 /** 139 * One of image/jpeg, image/gif or image/png 140 * As stated here: https://developers.facebook.com/docs/sharing/webmasters#images 141 **/ 142 $facebookMime = ["image/jpeg", "image/gif", "image/png"]; 143 foreach ($facebookImages as $facebookImage) { 144 145 if (!in_array($facebookImage->getMime(), $facebookMime)) { 146 continue; 147 } 148 149 /** @var RasterImageLink $facebookImage */ 150 if (!($facebookImage instanceof RasterImageLink)) { 151 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"); 152 continue; 153 } 154 155 if (!$facebookImage->exists()) { 156 LogUtility::msg("The image ($facebookImage) does not exist and was not added", LogUtility::LVL_MSG_ERROR, self::CANONICAL); 157 } else { 158 159 $toSmall = false; 160 if ($facebookImage->isAnalyzable()) { 161 162 // There is a minimum size constraint of 200px by 200px 163 if ($facebookImage->getMediaWidth() < 200) { 164 $toSmall = true; 165 } else { 166 $facebookMeta["og:image:width"] = $facebookImage->getMediaWidth(); 167 if ($facebookImage->getMediaHeight() < 200) { 168 $toSmall = true; 169 } else { 170 $facebookMeta["og:image:height"] = $facebookImage->getMediaHeight(); 171 } 172 } 173 } 174 175 if ($toSmall) { 176 $message = "The facebook image ($facebookImage) is too small (" . $facebookImage->getMediaWidth() . " x " . $facebookImage->getMediaHeight() . "). The minimum size constraint is 200px by 200px"; 177 if ($facebookImage->getId() != $page->getFirstImage()->getId()) { 178 LogUtility::msg($message, LogUtility::LVL_MSG_ERROR, self::CANONICAL); 179 } else { 180 LogUtility::log2BrowserConsole($message); 181 } 182 } 183 184 185 /** 186 * We may don't known the dimensions 187 */ 188 if (!$toSmall) { 189 $mime = $facebookImage->getMime(); 190 if (!empty($mime)) { 191 $facebookMeta["og:image:type"] = $mime[1]; 192 } 193 $facebookMeta["og:image"] = $facebookImage->getAbsoluteUrl(); 194 // One image only 195 break; 196 } 197 } 198 199 } 200 } 201 202 203 $facebookMeta["fb:app_id"] = self::FACEBOOK_APP_ID; 204 205 $lang = $page->getLang(); 206 if (!empty($lang)) { 207 208 $country = $page->getCountry(); 209 if (empty($country)) { 210 $country = $lang; 211 } 212 $facebookMeta["og:locale"] = $lang . "_" . strtoupper($country); 213 214 } else { 215 216 // The Facebook default 217 $facebookMeta["og:locale"] = "en_US"; 218 219 } 220 221 /** 222 * Add the properties 223 */ 224 foreach ($facebookMeta as $property => $content) { 225 $event->data['meta'][] = array("property" => $property, "content" => $content); 226 } 227 228 229 } 230 231} 232