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