xref: /plugin/socialcards/action.php (revision 3322a5da45318abd68a7bf878f24b895a6886bc9)
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, '&amp;', true);
199	}
200
201}
202