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