1<?php
2
3/**
4 * Plugin facebookwall: Displays status messages on a facebook wall.
5 *
6 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
7 * @version    1.2
8 * @date       September 2016
9 * @author     J. Drost-Tenfelde <info@drost-tenfelde.de>
10 *
11 * This plugin uses Facebook Graph API v2.7.
12 *
13 */
14
15// must be run within Dokuwiki
16if(!defined('DOKU_INC')) die();
17
18if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/');
19require_once(DOKU_PLUGIN.'syntax.php');
20
21// Syntax parameters
22define( "FB_WALL_APPLICATION_ID", "appid" ); // Facebook application id
23define( "FB_WALL_SECRET", "secret" ); // Facebook secret
24define( "FB_WALL_FAN_PAGE_ID", "fanpageid" ); // Facebook page id
25define( "FB_WALL_SHOW_AS", "showAs" ); // Allow alternate displaying of the status messages
26define( "FB_WALL_FROM_DATE", "from" ); // date from which to include the status message
27define( "FB_WALL_TO_DATE", "to" ); // date to which to include status messages
28define( "FB_WALL_NR_ENTRIES", "numberOfEntries" ); // maximum number of entries
29define( "FB_WALL_SORT", "sort" ); // Sort by create date ASC or DESC
30define( "FB_WALL_LIMIT", "limit" ); // Limit size of the description by number of chars
31
32// Configuration parameters
33define( "FB_WALL_DATE_FORMAT", "dformat" ); // Date format
34define( "FB_WALL_TIME_FORMAT", "tformat" ); // Time format
35define( "FB_WALL_TEMPLATE", "template" ); // Template
36define( "FB_WALL_PICTURE_MAX_WIDTH", "maxWidth" ); // Maximum allowed width for attachment picture
37define( "FB_WALL_PICTURE_MAX_HEIGHT", "maxHeight" ); // Maximum allowed height for attachment picture
38
39/**
40 * This plugin retrieves facebook status messages and displays them in HTML.
41 *
42 * Usage: {{facebookwall#appid=1234&secret=12345&fanpageid=12345&showAs=default}}
43 *
44 */
45class syntax_plugin_facebookwall extends DokuWiki_Syntax_Plugin
46{
47    function getInfo() {
48      return array(
49        'author' => 'J. Drost-Tenfelde',
50        'email'  => 'info@drost-tenfelde.de',
51        'date'   => '2012-02-09',
52        'name'   => 'facebookwall',
53        'desc'   => 'Displays status messages on a facebook wall',
54        'url'    => 'http://www.drost-tenfelde.de/?id=dokuwiki:plugins:facebookwall',
55      );
56    }
57
58    // implement necessary Dokuwiki_Syntax_Plugin methods
59    function getType() {
60        return 'substition';
61    }
62
63    function getSort() {
64        return 42;
65    }
66
67    function connectTo($mode) {
68        $this->Lexer->addSpecialPattern('\{\{facebookwall.*?\}\}',$mode,'plugin_facebookwall');
69    }
70
71	function getData($url) {
72		$ch = curl_init();
73		$timeout = 5;
74		curl_setopt($ch, CURLOPT_URL, $url);
75		curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
76		curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
77		curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
78		$data = curl_exec($ch);
79		curl_close($ch);
80		return $data;
81	}
82
83	/**
84	 * parse parameters from the {{facebookwall#...}} tag.
85	 * @return an array that will be passed to the renderer function
86	 */
87	function handle($match, $state, $pos, &$handler) {
88        $match = substr($match, 15, -2);
89		parse_str($match, $params);
90
91		// Make sure the necessary data is set
92		if ( !$params[FB_WALL_APPLICATION_ID] ) {
93		  $this->error = $this->getLang('error_appid_not_set');
94        }
95        if ( !$params[FB_WALL_SECRET] ) {
96		  $this->error = $this->getLang('error_secret_not_set');
97        }
98        if ( !$params[FB_WALL_FAN_PAGE_ID] ) {
99		  $this->error = $this->getLang('error_fanpageid_not_set');
100        }
101        if ( !$params[FB_WALL_SHOW_AS] ) {
102            $params[FB_WALL_SHOW_AS] = 'default';
103        }
104        if ( !$params[FB_WALL_LIMIT] ) {
105            $params[FB_WALL_LIMIT] = 0;
106        }
107
108        // Max width
109        $maxWidth = $this->getConf( FB_WALL_PICTURE_MAX_WIDTH );
110        if ( !isset($maxWidth ) || $maxWidth == '' ) {
111            $maxWidth = 999;
112        }
113        $params[FB_WALL_PICTURE_MAX_WIDTH] = $maxWidth;
114
115        // Max height
116        $maxHeight = $this->getConf( FB_WALL_PICTURE_MAX_HEIGHT );
117        if ( !isset($maxHeight) || $maxHeight== '' ) {
118            $maxHeight = 0;
119        }
120        $params[FB_WALL_PICTURE_MAX_HEIGHT] = $maxHeight;
121
122        // Get the appropriate display template
123        $template = $this->getConf( $params[FB_WALL_SHOW_AS] );
124        if ( !isset($template ) || $template == '' ) {
125			$template = $this->getConf('default');
126		}
127        $params[FB_WALL_TEMPLATE] = $template;
128
129		// Get the FROM_DATE parameter
130		if ($params[FB_WALL_FROM_DATE] == 'today') {
131			$from = time();
132		}
133        else if (preg_match('#(\d\d)/(\d\d)/(\d\d\d\d)#', $params[FB_WALL_FROM_DATE], $fromDate)) {
134			// must be MM/dd/yyyy
135			$from = mktime(0, 0, 0, $fromDate[1], $fromDate[2], $fromDate[3]);
136		}
137        else if (preg_match('/\d+/', $params[FB_WALL_FROM_DATE])) {
138			$from = $params[FB_WALL_FROM_DATE];
139		}
140		$params[FB_WALL_FROM_DATE] = $from;
141
142        // Get the to parameter
143		if ($params[FB_WALL_TO_DATE] == 'today') {
144			$to = mktime(24, 0, 0, date("m") , date("d"), date("Y"));
145
146		}
147        else if (preg_match('#(\d\d)/(\d\d)/(\d\d\d\d)#', $params[FB_WALL_TO_DATE], $toDate)) {
148			// must be MM/dd/yyyy
149			$to = mktime(0, 0, 0, $toDate[1], $toDate[2], $toDate[3]);
150		}
151        else if (preg_match('/\d+/', $params[FB_WALL_TO_DATE])) {
152			$to = $params[FB_WALL_TO_DATE];
153		}
154		$params[FB_WALL_TO_DATE] = $to;
155
156		// Sorting
157		if ( !$params[FB_WALL_SORT ] ) {
158            $params[FB_WALL_SORT ] = 'DESC';
159        }
160        else {
161            if ( $params[FB_WALL_SORT] != 'ASC') {
162                $params[FB_WALL_SORT ] = 'DESC';
163            }
164        }
165
166		return $params;
167	}
168
169	/**
170	 * Retrieves the facebook events and parses them to HTML.
171	 */
172	function render($mode, &$renderer, $data) {
173        global $conf;
174
175        $info = $this->getInfo();
176
177        $content = '';
178
179		if ($mode == 'xhtml') {
180            // Catch errors
181            if ($this->error) {
182                $renderer->doc .= 'Error in Plugin '.$info['name'].': '.$this->error;
183                return;
184            }
185
186			// Get the facebook information
187			$fb_app_id = $data[FB_WALL_APPLICATION_ID];
188			$fb_secret = $data[FB_WALL_SECRET];
189			$fb_page_id = $data[FB_WALL_FAN_PAGE_ID];
190
191			// Get the access token using app-id and secret
192			$token_url ="https://graph.facebook.com/oauth/access_token?client_id={$fb_app_id}&client_secret={$fb_secret}&grant_type=client_credentials";
193			$token_data = $this->getData( $token_url );
194
195			$elements = split("=", $token_data );
196			if ( count($elements) < 2) {
197				$renderer->doc .= 'Access token could not be retrieved for Plugin '.$info['name'].': '.$this->error;
198				return;
199			}
200			$fb_access_token = $elements[1];
201
202            // Get the date format
203            $date_format = $this->getConf(FB_WALL_DATE_FORMAT);
204            $time_format = $this->getConf(FB_WALL_TIME_FORMAT);
205            $datetime_format = $date_format.' '.$time_format;
206
207			$numberOfEntries = $data[FB_WALL_NR_ENTRIES];
208
209            // Get the time offset
210            //$offset = $this->get_timezone_offset( "America/Los_Angeles" );
211            $offset = 0;
212
213			$fields = "id,message,picture,link,name,description,type,icon,created_time,from,object_id";
214
215			$limit = $numberOfEntries + 5;
216			$json_link = "https://graph.facebook.com/{$fb_page_id}/feed?access_token={$fb_access_token}&fields={$fields}&limit={$limit}";
217			$json = $this->getData( $json_link);
218
219			//$objects = json_decode($json, true, 512, JSON_BIGINT_AS_STRING);
220			$objects = json_decode($json, true);
221
222			// count the number of wallposts
223			$post_count = count($objects['data']);
224
225			// Loop through the events
226			for ($post_index = 0; $post_index < $post_count; $post_index++) {
227				$post = $objects['data'][$post_index];
228
229                $entry = $data[FB_WALL_TEMPLATE];
230
231                // If no message was provided, skip to the next
232                if ( !$post['message'] ) {
233                    continue;
234                }
235
236                // If the date is lower than the from date, skip to the next
237                if ( isset($data[FB_WALL_FROM_DATE]) && ($post['created_time'] < $data[FB_WALL_FROM_DATE] ) )  {
238                    continue;
239                }
240                // If the date is higher than the to data, skip to the next
241                if ( $data[FB_WALL_TO_DATE] && ($post['created_time'] > $data[FB_WALL_TO_DATE] ))  {
242                    continue;
243                }
244
245                // Limit?
246                if ( isset( $data[FB_WALL_LIMIT]) && ($data[FB_WALL_LIMIT] > 0 ) ) {
247                    if ( strlen( $post['message'] ) > $data[FB_WALL_LIMIT] ) {
248                        $post['message_short'] = substr( $post['message'], 0, $data[FB_WALL_LIMIT] ).'...';
249                        // Find the first occurance of a space
250                        $index = strrpos ( $post['message_short'], ' ' );
251						$post['message_short'] = substr( $post['message_short'], 0, $index ).'...';
252                        $post['message'] = substr( $post['message_short'], 0, $index ).'...';
253                    }
254                }
255				else {
256					$post['message_short'] = substr( $post['message'], 0, 150 ).'...';
257					$index = strrpos ( $post['message_short'], ' ' );
258                    $post['message_short'] = substr( $post['message_short'], 0, $index ).'...';
259				}
260                // Process the message
261                $post['message'] = str_replace("\r\n", '<html><br /></html>', $post['message'] );
262                $post['message'] = str_replace("\n", '<html><br /></html>', $post['message'] );
263				$post['message_short'] = str_replace("\r\n", '<html><br /></html>', $post['message_short'] );
264                $post['message_short'] = str_replace("\n", '<html><br /></html>', $post['message_short'] );
265
266                $entry = str_replace('{message}', $post['message'], $entry );
267				$entry = str_replace('{message_short}', $post['message_short'], $entry );
268
269                // Replace tags in template
270                $entry = str_replace('{date}', date( $date_format, strtotime($post['created_time'])), $entry );
271                $entry = str_replace('{time}', date( $time_format, strtotime($post['created_time'])), $entry );
272                $entry = str_replace('{datetime}', date( $datetime_format, strtotime($post['created_time'])), $entry );
273                $entry = str_replace('{timestamp}', $post['created_time'], $entry );
274
275				$pic = $post['picture'];
276				// Add a fix for urls with get parameters
277				if ( strpos($pic, '?') > 0 )
278				{
279					$pic .= '&.png';
280				}
281				$entry = str_replace('{image}', $pic, $entry );
282
283                // Url
284                $post_id = $post['id'];
285                $post_values = explode( "_", $post_id);
286                $post_url = "http://www.facebook.com/".$post_values[0]."/posts/".$post_values[1];
287                $entry = str_replace('{url}', $post_url, $entry );
288
289                $entry = str_replace('{more}', '[['.$post_url.'|'.$this->getLang('read_more').']]', $entry );
290
291                // Add the entry to the content
292                $content .= $entry;
293
294				$numberOfEntries--;
295				if ( $numberOfEntries == 0 ) {
296					break;
297				}
298            }
299
300			//$renderer->doc .= $ret;
301			$html = p_render($mode, p_get_instructions( $content ), $info );
302			$renderer->doc .= $html;
303
304			return true;
305		}
306		return false;
307	}
308
309	function get_timezone_offset($remote_tz, $origin_tz = null) {
310        if($origin_tz === null) {
311            if(!is_string($origin_tz = date_default_timezone_get())) {
312                return false; // A UTC timestamp was returned -- bail out!
313            }
314        }
315        $origin_dtz = new DateTimeZone($origin_tz);
316        $remote_dtz = new DateTimeZone($remote_tz);
317        $origin_dt = new DateTime("now", $origin_dtz);
318        $remote_dt = new DateTime("now", $remote_dtz);
319        $offset = $origin_dtz->getOffset($origin_dt) - $remote_dtz->getOffset($remote_dt);
320        return $offset;
321    }
322
323    /**
324     * Makes a linked image.
325     *
326     * @href link
327     * @alt tooltip
328     * @src location of the image
329     * @data configuration parameters for the plugin.
330     * @conf DokuWiki configuration.
331     *
332     * @return HTML code with linked image.
333     */
334    function makeImage( $href, $alt, $src, $data, $conf) {
335        $html = '<html><a href="'.$href.'" alt="'.$alt.'" target="'.$conf['target']['extern'].'">';
336        $html .= '<img src="'.$src.'" align="left"';
337
338        if ( $data[FB_WALL_PICTURE_MAX_WIDTH] > 0 || $data[FB_WALL_PICTURE_MAX_HEIGHT] > 0) {
339            $html .= ' style="';
340            if ( $data[FB_WALL_PICTURE_MAX_WIDTH] > 0 ) {
341                $html .= 'max-width: '.$data[FB_WALL_PICTURE_MAX_WIDTH].'px !important;';
342            }
343            if ( $data[FB_WALL_PICTURE_MAX_HEIGHT] > 0 ) {
344                $html .= 'max-height: '.$data[FB_WALL_PICTURE_MAX_HEIGHT].'px !important;';
345            }
346            $html .= '"';
347        }
348        $html .= '/>';
349        $html .= '</a></html>';
350        return $html;
351    }
352}
353
354?>