1<?php 2/* 3 * Copyright (c) 2013-2016 Mark C. Prins <mprins@users.sf.net> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18if (!defined('DOKU_INC')){ 19 die(); 20} 21if (!defined('DOKU_PLUGIN')) { 22 define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/'); 23} 24 25require_once DOKU_PLUGIN . 'action.php'; 26/** 27 * DokuWiki Plugin socialcards (Action Component). 28 * 29 * @license BSD license 30 * @author Mark C. Prins <mprins@users.sf.net> 31 */ 32class action_plugin_socialcards extends DokuWiki_Action_Plugin { 33 34 /** 35 * Register our callback for the TPL_METAHEADER_OUTPUT event. 36 * 37 * @param $controller Doku_Event_Handler 38 * @see DokuWiki_Action_Plugin::register() 39 */ 40 public function register(Doku_Event_Handler $controller) { 41 $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 42 'handle_tpl_metaheader_output'); 43 } 44 45 /** 46 * Retrieve metadata and add to the head of the page using appropriate meta 47 * tags unless the page does not exist. 48 * 49 * @global string $ID page id 50 * @global array $conf global wiki configuration 51 * @global array $INFO 52 * @param Doku_Event $event the DokuWiki event. $event->data is a two-dimensional 53 * array of all meta headers. The keys are meta, link and script. 54 * @param mixed $param the parameters passed to register_hook when this 55 * handler was registered (not used) 56 * 57 * @see http://www.dokuwiki.org/devel:event:tpl_metaheader_output 58 */ 59 public function handle_tpl_metaheader_output(Doku_Event $event, $param) { 60 global $ID, $conf, $INFO; 61 62 if (!page_exists($ID)) { return; } 63 64 // twitter card, see https://dev.twitter.com/cards/markup 65 // creat a summary card, see https://dev.twitter.com/cards/types/summary 66 $event->data['meta'][] = array('name' => 'twitter:card', 67 'content' => "summary",); 68 69 $event->data['meta'][] = array('name' => 'twitter:site', 70 'content' => $this->getConf('twitterName'),); 71 72 $event->data['meta'][] = array('name' => 'twitter:title', 73 'content' => p_get_metadata($ID, 'title', true),); 74 75 $desc = p_get_metadata($ID, 'description', true); 76 if (!empty($desc)) { 77 $desc = str_replace("\n", " ", $desc['abstract']); 78 $event->data['meta'][] = array('name' => 'twitter:description', 79 'content' => $desc,); 80 } 81 82 if ($this->getConf('twitterUserName') != '') { 83 $event->data['meta'][] = array('name' => 'twitter:creator', 84 'content' => $this->getConf('twitterUserName'),); 85 } 86 87 $event->data['meta'][] = array('name' => 'twitter:image', 88 'content' => $this->getImage(),); 89 90 // opengraph, see http://ogp.me/ 91 // 92 // to make this work properly the template should be modified adding the 93 // namespaces for a (x)html 4 template make html tag: 94 // 95 // <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="nl" lang="nl" 96 // xmlns:og="http://ogp.me/ns#" xmlns:fb="http://ogp.me/ns/fb#" 97 // xmlns:article="http://ogp.me/ns/article#" xmlns:place="http://ogp.me/ns/place#"> 98 // 99 // and for a (x)html 5 template make head tag: 100 // 101 // <head prefix="og: http://ogp.me/ns# fb: http://ogp.me/ns/fb# article: http://ogp.me/ns/article# place: http://ogp.me/ns/place#"> 102 103 // og namespace http://ogp.me/ns# 104 $event->data['meta'][] = array('property' => 'og:locale', 105 'content' => $this->getConf('languageTerritory'),); 106 $event->data['meta'][] = array('property' => 'og:site_name', 107 'content' => $conf['title'],); 108 $event->data['meta'][] = array('property' => 'og:url', 109 'content' => wl($ID, '', true),); 110 $event->data['meta'][] = array('property' => 'og:title', 111 'content' => p_get_metadata($ID, 'title', true),); 112 if (!empty($desc)) { 113 $event->data['meta'][] = array('property' => 'og:description', 114 'content' => $desc,); 115 } 116 $event->data['meta'][] = array('property' => 'og:type', 117 'content' => "article",); 118 $ogImage = $this->getImage(); 119 $secure = substr($ogImage, 0, 5) === 'https' ? ':secure_url' : ''; 120 $event->data['meta'][] = array('property' => 'og:image' . $secure, 121 'content' => $ogImage,); 122 123 // article namespace http://ogp.me/ns/article# 124 $_dates = p_get_metadata($ID, 'date', true); 125 $event->data['meta'][] = array('property' => 'article:published_time', 126 'content' => dformat($_dates['created']),); 127 $event->data['meta'][] = array('property' => 'article:modified_time', 128 'content' => dformat($_dates['modified']),); 129 $event->data['meta'][] = array('property' => 'article:author', 130 'content' => $INFO['editor'],); 131 // $event->data['meta'][] = array('property' => 'article:author','content' => p_get_metadata($ID,'creator',true),); 132 // $event->data['meta'][] = array('property' => 'article:author','content' => p_get_metadata($ID,'user',true),); 133 $_subject = p_get_metadata($ID, 'subject', true); 134 if (!empty($_subject)) { 135 if (!is_array($_subject)) { 136 $_subject = array($_subject); 137 } 138 foreach ($_subject as $tag) { 139 $event->data['meta'][] = array('property' => 'article:tag', 140 'content' => $tag,); 141 } 142 } 143 144 // place namespace http://ogp.me/ns/place# 145 $geotags = p_get_metadata($ID, 'geo', true); 146 $lat = $geotags['lat']; 147 $lon = $geotags['lon']; 148 if (!(empty($lat) && empty($lon))) { 149 $event->data['meta'][] = array('property' => 'place:location:latitude', 150 'content' => $lat,); 151 $event->data['meta'][] = array('property' => 'place:location:longitude', 152 'content' => $lon,); 153 } 154 // see https://developers.facebook.com/docs/opengraph/property-types/#geopoint 155 $alt = $geotags['alt']; 156 if (!empty($alt)) { 157 // facebook expects feet... 158 $alt = $alt * 3.2808; 159 $event->data['meta'][] = array('property' => 'place:location:altitude', 160 'content' => $alt,); 161 } 162 163 /* these are not valid for the GeoPoint type.. 164 165 $region=$geotags['region']; 166 $country=$geotags['country']; 167 $placename=$geotags['placename']; 168 if (!empty($region)) {$event->data['meta'][] = array('property' => 'place:location:region', 'content' => $region,);} 169 if (!empty($placename)) {$event->data['meta'][] = array('property' => 'place:location:locality', 'content' => $placename,);} 170 if (!empty($country)) {$event->data['meta'][] = array('property' => 'place:location:country-name','content' => $country,);} 171 */ 172 173 // optional facebook app ID 174 $appId = $this->getConf('fbAppId'); 175 if (!empty($appId)) { 176 $event->data['meta'][] = array('property' => 'fb:app_id', 177 'content' => $appId,); 178 } 179 } 180 181 /** 182 * Gets the canonical image path for this page. 183 * 184 * @global string $ID page id 185 * @return string the url to the image to use for this page 186 */ 187 private function getImage() { 188 global $ID; 189 $rel = p_get_metadata($ID, 'relation', true); 190 $img = $rel['firstimage']; 191 192 if (empty($img)) { 193 $img = $this->getConf('fallbackImage'); 194 if (substr($img, 0, 4 ) === "http") { 195 // don't use ml() as this results in a HTTP redirect after hitting the wiki 196 return $img; 197 } 198 } 199 200 return ml($img, array(), true, '&', true); 201 } 202 203} 204