xref: /plugin/socialcards/action.php (revision f3501156ad8d0d9571cc3a64d41e66b8321c323c)
1fdb4710bSMark Prins<?php
2635a5e2dSMark Prins/*
32765f2acSMark Prins * Copyright (c) 2013-2016 Mark C. Prins <mprins@users.sf.net>
4fdb4710bSMark Prins *
5fdb4710bSMark Prins * Permission to use, copy, modify, and distribute this software for any
6fdb4710bSMark Prins * purpose with or without fee is hereby granted, provided that the above
7fdb4710bSMark Prins * copyright notice and this permission notice appear in all copies.
8fdb4710bSMark Prins *
9fdb4710bSMark Prins * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10fdb4710bSMark Prins * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11fdb4710bSMark Prins * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12fdb4710bSMark Prins * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13fdb4710bSMark Prins * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14fdb4710bSMark Prins * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15fdb4710bSMark Prins * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16635a5e2dSMark Prins */
17635a5e2dSMark Prins
18e0737b20SMark Prinsif (!defined('DOKU_INC')){
1989906951SMark Prins	die();
20e0737b20SMark Prins}
21e0737b20SMark Prinsif (!defined('DOKU_PLUGIN')) {
2289906951SMark Prins	define('DOKU_PLUGIN', DOKU_INC . 'lib/plugins/');
23e0737b20SMark Prins}
24fdb4710bSMark Prins
25fdb4710bSMark Prinsrequire_once DOKU_PLUGIN . 'action.php';
26ae2f5c78SMark Prins/**
27ae2f5c78SMark Prins * DokuWiki Plugin socialcards (Action Component).
28ae2f5c78SMark Prins *
29ae2f5c78SMark Prins * @license BSD license
30ae2f5c78SMark Prins * @author  Mark C. Prins <mprins@users.sf.net>
31ae2f5c78SMark Prins */
32fdb4710bSMark Prinsclass action_plugin_socialcards extends DokuWiki_Action_Plugin {
33fdb4710bSMark Prins
34ae2f5c78SMark Prins	/**
35ae2f5c78SMark Prins	 * Register our callback for the TPL_METAHEADER_OUTPUT event.
36ae2f5c78SMark Prins	 *
37ae2f5c78SMark Prins	 * @param $controller Doku_Event_Handler
38ae2f5c78SMark Prins	 * @see DokuWiki_Action_Plugin::register()
39ae2f5c78SMark Prins	 */
40ae2f5c78SMark Prins	public function register(Doku_Event_Handler $controller) {
4189906951SMark Prins		$controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this,
4289906951SMark Prins				'handle_tpl_metaheader_output');
43fdb4710bSMark Prins	}
44fdb4710bSMark Prins
45fdb4710bSMark Prins	/**
4689906951SMark Prins	 * Retrieve metadata and add to the head of the page using appropriate meta
47156465eeSMark Prins	 * tags unless the page does not exist.
4889906951SMark Prins	 *
4989906951SMark Prins	 * @global string $ID page id
5089906951SMark Prins	 * @global array $conf global wiki configuration
5189906951SMark Prins	 * @global array $INFO
52fdb4710bSMark Prins	 * @param Doku_Event $event the DokuWiki event. $event->data is a two-dimensional
53fdb4710bSMark Prins	 *             array of all meta headers. The keys are meta, link and script.
542765f2acSMark Prins	 * @param mixed $param the parameters passed to register_hook when this
552765f2acSMark Prins	 *             handler was registered (not used)
5689906951SMark Prins	 *
5789906951SMark Prins	 * @see http://www.dokuwiki.org/devel:event:tpl_metaheader_output
58fdb4710bSMark Prins	 */
592765f2acSMark Prins	public function handle_tpl_metaheader_output(Doku_Event $event, $param) {
6089906951SMark Prins		global $ID, $conf, $INFO;
61fdb4710bSMark Prins
62e0737b20SMark Prins		if (!page_exists($ID)) { return; }
63156465eeSMark Prins
642765f2acSMark Prins		// twitter card, see https://dev.twitter.com/cards/markup
652765f2acSMark Prins		// creat a summary card, see https://dev.twitter.com/cards/types/summary
6689906951SMark Prins		$event->data['meta'][] = array('name' => 'twitter:card',
6789906951SMark Prins				'content' => "summary",);
682765f2acSMark Prins
692765f2acSMark Prins		$event->data['meta'][] = array('name' => 'twitter:site',
702765f2acSMark Prins				'content' => $this->getConf('twitterName'),);
712765f2acSMark Prins
7289906951SMark Prins		$event->data['meta'][] = array('name' => 'twitter:title',
7389906951SMark Prins				'content' => p_get_metadata($ID, 'title', true),);
742765f2acSMark Prins
75aeb704feSMark Prins		$desc = p_get_metadata($ID, 'description', true);
76aeb704feSMark Prins		if (!empty($desc)) {
7789906951SMark Prins			$desc = str_replace("\n", " ", $desc['abstract']);
78fc03d0b3SMark Prins			$event->data['meta'][] = array('name' => 'twitter:description',
7989906951SMark Prins					'content' => $desc,);
80fc03d0b3SMark Prins		}
812765f2acSMark Prins
82e0737b20SMark Prins		if ($this->getConf('twitterUserName') != '') {
8389906951SMark Prins			$event->data['meta'][] = array('name' => 'twitter:creator',
84e0737b20SMark Prins				'content' => $this->getConf('twitterUserName'),);
85e0737b20SMark Prins		}
862765f2acSMark Prins
872765f2acSMark Prins		$event->data['meta'][] = array('name' => 'twitter:image',
8889906951SMark Prins				'content' => $this->getImage(),);
8989906951SMark Prins
9089906951SMark Prins		// opengraph, see http://ogp.me/
9189906951SMark Prins		//
9289906951SMark Prins		// to make this work properly the template should be modified adding the
9389906951SMark Prins		// namespaces for a (x)html 4 template make html tag:
9489906951SMark Prins		//
9589906951SMark Prins		// <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="nl" lang="nl"
9689906951SMark Prins		//       xmlns:og="http://ogp.me/ns#" xmlns:fb="http://ogp.me/ns/fb#"
9789906951SMark Prins		//       xmlns:article="http://ogp.me/ns/article#" xmlns:place="http://ogp.me/ns/place#">
9889906951SMark Prins		//
9989906951SMark Prins		// and for a (x)html 5 template make head tag:
10089906951SMark Prins		//
10189906951SMark Prins		// <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#">
10289906951SMark Prins
10389906951SMark Prins		// og namespace http://ogp.me/ns#
10489906951SMark Prins		$event->data['meta'][] = array('property' => 'og:locale',
10589906951SMark Prins				'content' => $this->getConf('languageTerritory'),);
10689906951SMark Prins		$event->data['meta'][] = array('property' => 'og:site_name',
10789906951SMark Prins				'content' => $conf['title'],);
10889906951SMark Prins		$event->data['meta'][] = array('property' => 'og:url',
10989906951SMark Prins				'content' => wl($ID, '', true),);
11089906951SMark Prins		$event->data['meta'][] = array('property' => 'og:title',
11189906951SMark Prins				'content' => p_get_metadata($ID, 'title', true),);
11289906951SMark Prins		if (!empty($desc)) {
11389906951SMark Prins			$event->data['meta'][] = array('property' => 'og:description',
11489906951SMark Prins					'content' => $desc,);
115fdb4710bSMark Prins		}
11689906951SMark Prins		$event->data['meta'][] = array('property' => 'og:type',
11789906951SMark Prins				'content' => "article",);
118*f3501156SLaurent Marquet		$ogImage = $this->getImage();
119*f3501156SLaurent Marquet		$secure = substr($ogImage, 0, 5) === 'https' ? ':secure_url' : '';
120*f3501156SLaurent Marquet		$event->data['meta'][] = array('property' => 'og:image' . $secure,
121*f3501156SLaurent Marquet				'content' => $ogImage,);
12289906951SMark Prins
12389906951SMark Prins		// article namespace http://ogp.me/ns/article#
12489906951SMark Prins		$_dates = p_get_metadata($ID, 'date', true);
12589906951SMark Prins		$event->data['meta'][] = array('property' => 'article:published_time',
12689906951SMark Prins				'content' => dformat($_dates['created']),);
12789906951SMark Prins		$event->data['meta'][] = array('property' => 'article:modified_time',
12889906951SMark Prins				'content' => dformat($_dates['modified']),);
12989906951SMark Prins		$event->data['meta'][] = array('property' => 'article:author',
13089906951SMark Prins				'content' => $INFO['editor'],);
13189906951SMark Prins		// $event->data['meta'][] = array('property' => 'article:author','content' => p_get_metadata($ID,'creator',true),);
13289906951SMark Prins		// $event->data['meta'][] = array('property' => 'article:author','content' => p_get_metadata($ID,'user',true),);
13389906951SMark Prins		$_subject = p_get_metadata($ID, 'subject', true);
13489906951SMark Prins		if (!empty($_subject)) {
13589906951SMark Prins			if (!is_array($_subject)) {
13689906951SMark Prins				$_subject = array($_subject);
13789906951SMark Prins			}
13889906951SMark Prins			foreach ($_subject as $tag) {
13989906951SMark Prins				$event->data['meta'][] = array('property' => 'article:tag',
14089906951SMark Prins						'content' => $tag,);
14189906951SMark Prins			}
14289906951SMark Prins		}
14389906951SMark Prins
14489906951SMark Prins		// place namespace http://ogp.me/ns/place#
14589906951SMark Prins		$geotags = p_get_metadata($ID, 'geo', true);
14689906951SMark Prins		$lat = $geotags['lat'];
14789906951SMark Prins		$lon = $geotags['lon'];
14889906951SMark Prins		if (!(empty($lat) && empty($lon))) {
14989906951SMark Prins			$event->data['meta'][] = array('property' => 'place:location:latitude',
15089906951SMark Prins					'content' => $lat,);
15189906951SMark Prins			$event->data['meta'][] = array('property' => 'place:location:longitude',
15289906951SMark Prins					'content' => $lon,);
1533322a5daSMark Prins		}
1543322a5daSMark Prins		// see https://developers.facebook.com/docs/opengraph/property-types/#geopoint
1553322a5daSMark Prins		$alt = $geotags['alt'];
1563322a5daSMark Prins		if (!empty($alt)) {
1573322a5daSMark Prins			// facebook expects feet...
1583322a5daSMark Prins			$alt = $alt * 3.2808;
1593322a5daSMark Prins			$event->data['meta'][] = array('property' => 'place:location:altitude',
1603322a5daSMark Prins					'content' => $alt,);
16189906951SMark Prins		}
16289906951SMark Prins
163ae2f5c78SMark Prins		/* these are not valid for the GeoPoint type..
164ae2f5c78SMark Prins
16589906951SMark Prins		$region=$geotags['region'];
16689906951SMark Prins		$country=$geotags['country'];
16789906951SMark Prins		$placename=$geotags['placename'];
16889906951SMark Prins		if (!empty($region))    {$event->data['meta'][] = array('property' => 'place:location:region',		'content' => $region,);}
16989906951SMark Prins		if (!empty($placename)) {$event->data['meta'][] = array('property' => 'place:location:locality',	'content' => $placename,);}
17089906951SMark Prins		if (!empty($country))   {$event->data['meta'][] = array('property' => 'place:location:country-name','content' => $country,);}
17189906951SMark Prins		*/
17289906951SMark Prins
17389906951SMark Prins		// optional facebook app ID
17489906951SMark Prins		$appId = $this->getConf('fbAppId');
17589906951SMark Prins		if (!empty($appId)) {
17689906951SMark Prins			$event->data['meta'][] = array('property' => 'fb:app_id',
17789906951SMark Prins					'content' => $appId,);
17889906951SMark Prins		}
17989906951SMark Prins	}
18089906951SMark Prins
18189906951SMark Prins	/**
18289906951SMark Prins	 * Gets the canonical image path for this page.
18389906951SMark Prins	 *
18489906951SMark Prins	 * @global string $ID page id
18589906951SMark Prins	 * @return string the url to the image to use for this page
18689906951SMark Prins	 */
18789906951SMark Prins	private function getImage() {
18889906951SMark Prins		global $ID;
18989906951SMark Prins		$rel = p_get_metadata($ID, 'relation', true);
19089906951SMark Prins		$img = $rel['firstimage'];
191ae2f5c78SMark Prins
192ae2f5c78SMark Prins		if (empty($img)) {
19393bf3c56SMark Prins			$img = $this->getConf('fallbackImage');
19493bf3c56SMark Prins			if (substr($img, 0, 4 ) === "http") {
195ae2f5c78SMark Prins				// don't use ml() as this results in a HTTP redirect after hitting the wiki
19693bf3c56SMark Prins				return $img;
19793bf3c56SMark Prins			}
19893bf3c56SMark Prins		}
199ae2f5c78SMark Prins
200ae2f5c78SMark Prins		return ml($img, array(), true, '&amp;', true);
20189906951SMark Prins	}
20289906951SMark Prins
203fdb4710bSMark Prins}
204