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