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