, J. Drost-Tenfelde * * This plugin uses Facebook's Graph API v2.12. * */ // must be run within Dokuwiki if(!defined('DOKU_INC')) die(); if(!defined('DOKU_PLUGIN')) define('DOKU_PLUGIN',DOKU_INC.'lib/plugins/'); require_once(DOKU_PLUGIN.'syntax.php'); // Syntax parameters define("FB_EVENTS_APPLICATION_ID", "fb_application_id"); define("FB_EVENTS_APPLICATION_SECRET", "fb_application_secret"); define("FB_EVENTS_FAN_PAGE_ID", "fanpageid"); define("FB_EVENTS_SHOW_AS", "showAs"); define("FB_EVENTS_WALLPOSTS_SHOW_AS", "showPostsAs"); define("FB_EVENTS_FROM_DATE", "from"); define("FB_EVENTS_TO_DATE", "to"); define("FB_EVENTS_SORT", "sort"); define("FB_EVENTS_NR_ENTRIES", "numberOfEntries"); define("FB_EVENTS_SHOW_END_TIMES", "showEndTimes"); define("FB_EVENTS_LIMIT", "limit"); define("FB_EVENTS_QUOTE_PREFIX", "quoteprefix"); // Configuration parameters define("FB_EVENTS_DATE_FORMAT", "dformat"); define("FB_EVENTS_TIME_FORMAT", "tformat"); define("FB_EVENTS_TEMPLATE", "template"); define("FB_EVENTS_WALLPOSTS_TEMPLATE", "wallposts"); // Helper sorting functions function compareEventStartDateAsc($a, $b) { return strtotime($a['start_time']) - strtotime($b['start_time']); } function compareEventStartDateDesc($b, $a) { return strtotime($a['start_time']) - strtotime($b['start_time']); } /** * This plugin retrieves facebook events and displays them in HTML. * * Usage (simple): {{facebookevents>fanpageid=12345}} * Usage (complex): {{facebookevents>fanpageid=12345&showAs=table&showPostsAs=wallposts_alternate&from=-2 weeks&to=today}} * */ class syntax_plugin_importfacebookevents extends DokuWiki_Syntax_Plugin { function getInfo() { return array( 'author' => 'G. Surrel, J. Drost-Tenfelde', 'email' => '', 'date' => '2018-03-08', 'name' => 'import_facebook_events', 'desc' => 'Displays facebook events as HTML', 'url' => 'https://www.dokuwiki.org/plugin:import_facebook_events', ); } // implement necessary Dokuwiki_Syntax_Plugin methods function getType() { return 'substition'; } function connectTo($mode) { $this->Lexer->addSpecialPattern('\{\{facebookevents.*?\}\}',$mode,'plugin_importfacebookevents'); } function getData($url) { $ch = curl_init(); $timeout = 5; curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); $data = curl_exec($ch); curl_close($ch); return $data; } /** * parse parameters from the {{facebookevents>...}} tag. * @return an array that will be passed to the renderer function */ function handle($match, $state, $pos, &$handler) { $match = substr($match, 17, -2); parse_str($match, $params); // Make sure the necessary data is set if ($this->getConf(FB_EVENTS_APPLICATION_ID) == '') { $this->error = /*$this->getLang*/('error_appid_not_set'); } if ($this->getConf(FB_EVENTS_APPLICATION_SECRET) == '') { $this->error = /*$this->getLang*/('error_appsecret_not_set'); } if (!$params[FB_EVENTS_FAN_PAGE_ID]) { $this->error = /*$this->getLang*/('error_fanpageid_not_set'); } if (!$params[FB_EVENTS_SHOW_AS]) { $params[FB_EVENTS_SHOW_AS] = 'default'; } if (!$params[FB_EVENTS_WALLPOSTS_SHOW_AS]) { $params[FB_EVENTS_WALLPOSTS_SHOW_AS] = 'wallposts_default'; } if (!$params[FB_EVENTS_QUOTE_PREFIX]) { $params[FB_EVENTS_QUOTE_PREFIX] = '> '; } if (!$params[FB_EVENTS_LIMIT]) { $params[FB_EVENTS_LIMIT] = 0; } // Get the appropriate display template $template = $this->getConf($params[FB_EVENTS_SHOW_AS]); if (!isset($template) || $template == '') { $template = $this->getConf('default'); } $params[FB_EVENTS_TEMPLATE] = $template; // Get the appropriate display template for comments $wallposts_template = $this->getConf($params[FB_EVENTS_WALLPOSTS_SHOW_AS]); if (!isset($wallposts_template) || $wallposts_template == '') { $wallposts_template = $this->getConf('wallposts_default'); } $params[FB_EVENTS_WALLPOSTS_TEMPLATE] = $wallposts_template; // Sorting if (!$params[FB_EVENTS_SORT]) { $params[FB_EVENTS_SORT] = 'ASC'; } elseif ($params[FB_EVENTS_SORT] != 'DESC') { $params[FB_EVENTS_SORT] = 'ASC'; } return $params; } /** * Retrieves the facebook events and parses them to HTML. */ function render($mode, &$renderer, $data) { $info = $this->getInfo(); // Disable caching because the result depends on an external ressource //$renderer->info['cache'] = false; $content = ''; if ($mode == 'xhtml') { // Catch errors if ($this->error) { $renderer->doc .= 'Error in Plugin '.$info['name'].': '.$this->error; return; } // Get the date format $date_format = $this->getConf(FB_EVENTS_DATE_FORMAT); $time_format = $this->getConf(FB_EVENTS_TIME_FORMAT); // Get the facebook information $fb_app_id = $this->getConf(FB_EVENTS_APPLICATION_ID); $fb_secret = $this->getConf(FB_EVENTS_APPLICATION_SECRET); $fb_page_id = $data[FB_EVENTS_FAN_PAGE_ID]; // Get the access token using app-id and secret $token_url ="https://graph.facebook.com/oauth/access_token?client_id={$fb_app_id}&client_secret={$fb_secret}&grant_type=client_credentials"; $token_data = $this->getData($token_url); $elements = explode('"',$token_data); if (count($elements) < 9) { $renderer->doc .= 'Access token could not be retrieved for Plugin '.$info['name'].': '.$this->error.' | '.$token_data; return; } $fb_access_token = $elements[3]; // Get the events $since_date = strtotime("-2 month", $data[FB_EVENTS_FROM_DATE]); // Go back in time as recurrent events disappear $until_date = strtotime($data[FB_EVENTS_TO_DATE]); $limit = $data[FB_EVENTS_NR_ENTRIES]; $fb_fields="id,name,place,updated_time,timezone,start_time,end_time,event_times,cover,photos{picture},picture{url},description,feed.limit(10){from{name,picture},created_time,type,message,link,permalink_url,source,picture}"; $json_link = "https://graph.facebook.com/v2.12/{$fb_page_id}/events/?fields={$fb_fields}&access_token={$fb_access_token}&limit={$limit}&since={$since_date}&until={$until_date}"; $json = $this->getData($json_link); dbglog(date(DATE_ATOM)); dbglog($json_link); //$objects = json_decode($json, true, 512, JSON_BIGINT_AS_STRING); $objects = json_decode($json, true); $events = $objects['data']; // Save timezone setting $origin_timezone = date_default_timezone_get(); // Handle recurring events foreach($events as $i => $event){ if(isset($event['event_times'])) { dbglog("Subevent found"); foreach($event['event_times'] as $event_time) { $json_link = "https://graph.facebook.com/v2.12/".$event_time['id']."/?fields={$fb_fields}&access_token={$fb_access_token}"; array_push($events, json_decode($this->getData($json_link), true)); } unset($events[$i]); } } // Sort array of events by start time if ($data[FB_EVENTS_SORT] === 'ASC') { usort($events, 'compareEventStartDateAsc'); } else { usort($events, 'compareEventStartDateDesc'); } // Iterate over events foreach($events as $event){ date_default_timezone_set($event['timezone']); $start_date = date($date_format, strtotime($event['start_time'])); $start_time = date($time_format, strtotime($event['start_time'])); if(strtotime($event['start_time']) < strtotime($data[FB_EVENTS_FROM_DATE])) continue; if (!isset($event['end_time'])) { $event['end_time'] = $event['start_time']; } $end_date = date($date_format, strtotime($event['end_time'])); $end_time = date($time_format, strtotime($event['end_time'])); $eid = $event['id']; $name = $event['name']; $description = isset($event['description']) ? $event['description'] : ""; // Limit? if (isset($data[FB_EVENTS_LIMIT]) && ($data[FB_EVENTS_LIMIT] > 0)) { if (strlen($description) > $data[FB_EVENTS_LIMIT]) { $description = substr($description, 0, $data[FB_EVENTS_LIMIT]); // Find the first occurance of a space $index = strrpos ($description, ' '); $description = substr($description, 0, $index).'…'; } } $description = str_replace("\r", '', $description); $description = str_replace("\n", "\\\\\n", $description); $picFull = isset($event['cover']['source']) ? $event['cover']['source'] : "https://graph.facebook.com/v2.7/{$fb_page_id}/picture"; if (strpos($picFull, '?') > 0) $picFull .= '&.jpg'; $picSmall = isset($event['photos']['data'][0]['picture']) ? $event['photos']['data'][0]['picture'] : "https://graph.facebook.com/v2.7/{$fb_page_id}/picture"; if (strpos($picSmall, '?') > 0) $picSmall .= '&.jpg'; $picSquare = isset($event['picture']['data']['url']) ? $event['picture']['data']['url'] : "https://graph.facebook.com/v2.7/{$fb_page_id}/picture"; if (strpos($picSquare, '?') > 0) $picSquare .= '&.jpg'; // place $place_name = isset($event['place']['name']) ? $event['place']['name'] : ""; $street = isset($event['place']['location']['street']) ? $event['place']['location']['street'] : ""; $city = isset($event['place']['location']['city']) ? $event['place']['location']['city'] : ""; $country = isset($event['place']['location']['country']) ? $event['place']['location']['country'] : ""; $zip = isset($event['place']['location']['zip']) ? $event['place']['location']['zip'] : ""; $location=""; $location_address=""; if ($place_name && $street & $city && $country && $zip){ $location = "{$place_name}"; $location_address = "{$street}, {$zip} {$city}, {$country}"; } else{ $location = '?'; $location_address = '?'; } // Build the entry $entry = $data[FB_EVENTS_TEMPLATE]; // Date $dateStart = date($this->getConf(FB_EVENTS_DATE_FORMAT), strtotime($event['start_time'])); $dateEnd = date($this->getConf(FB_EVENTS_DATE_FORMAT), strtotime($event['end_time'])); $dateStart != $dateEnd ? $date = "{$dateStart} - {$dateEnd}" : $date = "{$dateStart}"; // Time $timeStart = date($this->getConf(FB_EVENTS_TIME_FORMAT), strtotime($event['start_time'])); $timeEnd = date($this->getConf(FB_EVENTS_TIME_FORMAT), strtotime($event['end_time'])); $timeStart != $timeEnd ? $time = "{$timeStart} - {$timeEnd}" : $time = "{$timeStart}"; // DateTime $dateTimeStart = date($this->getConf(FB_EVENTS_TIME_FORMAT).', '.$this->getConf(FB_EVENTS_DATE_FORMAT), strtotime($event['start_time'])); $dateTimeEnd = date($this->getConf(FB_EVENTS_TIME_FORMAT).', '.$this->getConf(FB_EVENTS_DATE_FORMAT), strtotime($event['end_time'])); if($dateStart != $dateEnd) { $dateTime = $timeStart.', '.$dateStart.' - '.$timeEnd.', '.$dateEnd; } else { $dateTime = $timeStart.' - '.$timeEnd.', '.$dateEnd; } // Metadata $microdata = ''; $json_microdata = array ( '@context' => 'http://schema.org', '@type' => 'Event', 'name' => '{title}', 'startDate' => '{starttimestamp}', 'endDate' => '{endtimestamp}', 'url' => '{url}', 'location' => array ( '@type' => 'Place', 'name' => '{location}', 'address' => '{location_address}', ), 'description' => $description, 'image' => '{image}', ); $microdata = str_replace('{json_microdata}', json_encode($json_microdata), $microdata); $entry = str_replace('{microdata}',$microdata, $entry); // Replace the values $entry = str_replace('{title}', $name, $entry); $entry = str_replace('{description}', $description, $entry); $entry = str_replace('{location}', $location, $entry); $entry = str_replace('{location_address}', $location_address, $entry); $entry = str_replace('{place}', $place_name, $entry); $entry = str_replace('{city}', $city, $entry); $entry = str_replace('{country}', $country, $entry); $entry = str_replace('{zip}', $zip, $entry); $entry = str_replace('{image}', $picFull, $entry); $entry = str_replace('{image_large}', $picFull, $entry); $entry = str_replace('{image_small}', $picSmall, $entry); $entry = str_replace('{image_square}', $picSquare, $entry); // Date & time replacements $entry = str_replace('{date}', $dateStart, $entry); $entry = str_replace('{time}', $time, $entry); $entry = str_replace('{datetime}', $dateTime, $entry); $entry = str_replace('{startdatetime}', $dateTimeStart, $entry); $entry = str_replace('{startdate}', $dateStart, $entry); $entry = str_replace('{starttime}', $timeStart, $entry); $entry = str_replace('{enddatetime}', $dateTimeEnd, $entry); $entry = str_replace('{enddate}', $dateEnd, $entry); $entry = str_replace('{endtime}', $timeEnd, $entry); $entry = str_replace('{timestamp}', date('c', strtotime($event['start_time'])), $entry); $entry = str_replace('{starttimestamp}', date('c', strtotime($event['start_time'])), $entry); $entry = str_replace('{endtimestamp}', date('c', strtotime($event['end_time'])), $entry); // [[ url | read more ] $event_url = "http://www.facebook.com/events/".$eid; $entry = str_replace('{url}', $event_url, $entry); $entry = str_replace('{more}', '[['.$event_url.'|'.$this->getLang('read_more').']]', $entry); // Handle wall posts $wallposts = ''; foreach($event['feed']['data'] as $post) { $wallpost = $data[FB_EVENTS_WALLPOSTS_TEMPLATE]; $quoteprefix = $data[FB_EVENTS_QUOTE_PREFIX]; $userImage = $post['from']['picture']['data']['url'].'&.jpg'; $userName = $post['from']['name']; $postDateTime = date($this->getConf(FB_EVENTS_DATE_FORMAT), strtotime($post['created_time'])); $postLink = $post['permalink_url']; $description = $quoteprefix.$post['message']; $description = str_replace("\r", '', $description); $description = str_replace("\n", "\n{$quoteprefix}", $description); if(isset($post['source'])) { $mediaSource = $post['source'].'&.jpg'; } elseif(isset($post['link'])) { $mediaSource = $post['link']; } else { $mediaSource = ''; } isset($post['picture']) ? $mediaImage = $post['picture'].'&.jpg' : $mediaImage = ''; $wallpost = str_replace('{wp_userImage}', $userImage, $wallpost); $wallpost = str_replace('{wp_userName}', $userName, $wallpost); $wallpost = str_replace('{wp_datetime}', $postDateTime, $wallpost); $wallpost = str_replace('{wp_content}', $description, $wallpost); $wallpost = str_replace('{wp_mediaSource}', $mediaSource, $wallpost); $wallpost = str_replace('{wp_mediaImage}', $mediaImage, $wallpost); $wallpost = str_replace('{wp_permalink}', $postLink, $wallpost); $wallposts .= $wallpost; } $entry = str_replace('{wallposts}', $wallposts, $entry); // Add the entry to the content $content .= $entry; } $html = p_render($mode, p_get_instructions($content), $info); $renderer->doc .= $html; // Set the timezone back to the original date_default_timezone_set($origin_timezone); return true; } return false; } }