xref: /plugin/socialcards/action.php (revision 788439b3516a0ded34f093c4f5d047ed197a82d9) !
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, '&amp;', true);
201	}
202
203}
204