Advertisement
Guest User

facebook page publish 0.3.9 with fix

a guest
Nov 1st, 2011
442
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 68.84 KB | None | 0 0
  1. <?php
  2. /**
  3.  * Facebook Page Publish - publishes your blog posts to your fan page.
  4.  * Copyright (C) 2011 Martin Tschirsich
  5.  *
  6.  * This program is free software: you can redistribute it and/or modify
  7.  * it under the terms of the GNU General Public License as published by
  8.  * the Free Software Foundation, either version 3 of the License, or
  9.  * (at your option) any later version.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18.  *
  19.  *
  20.  * Plugin Name: Facebook Page Publish
  21.  * Plugin URI:  http://wordpress.org/extend/plugins/facebook-page-publish/
  22.  * Description: Publishes your posts on the wall of a Facebook profile or page.
  23.  * Author:      Martin Tschirsich
  24.  * Version:     0.3.9a
  25.  * Author URI:  http://www.tu-darmstadt.de/~m_t/
  26.  */
  27.  
  28. /**********************************************************************
  29.  * Constants
  30.  **********************************************************************/
  31. #error_reporting(E_ALL);
  32. define('FPP_VERSION', '0.3.9a');
  33. define('FPP_BASE_DIR', dirname(__file__));
  34. define('FPP_BASE_URL', WP_PLUGIN_URL.'/'.str_replace(basename( __FILE__), '', plugin_basename(__FILE__)));
  35. define('FPP_ADMIN_URL', admin_url('admin.php?page='.urlencode(plugin_basename(__FILE__))));
  36. define('FPP_DEFAULT_POST_TO_FACEBOOK', false); // Checkbox admin panel always preselected
  37. define('FPP_FACEBOOK_LINK_DESCRIPTION_MAX_LENGTH', 340); // Facebook link description allows max. 420 characters, 340 are always shown
  38. define('FPP_REQUEST_TIMEOUT', 20); // The default 5s are not sufficient on some servers
  39. define('FPP_TEXT_DOMAIN', 'facebook-page-publish');
  40.  
  41. /**********************************************************************
  42.  * Exceptions
  43.  **********************************************************************/
  44. class CommunicationException extends Exception {
  45.         public function __construct($message = null, $code = 0) {
  46.                 if (!empty($message)) $message .= '<br />';
  47.                 $message .= '[Error occured at line '.$this->getLine().']';
  48.                 parent::__construct($message, $code);
  49.         }
  50. }
  51.  
  52. class FacebookUnreachableException extends CommunicationException {
  53.         public function __construct($message = null, $code = 0) {
  54.                 if (empty($message)) {
  55.                         $message = __('Facebook is not reachable from your server.', FPP_TEXT_DOMAIN).' <a target="_blank" href="'.FPP_BASE_URL.'diagnosis.php">'.__('Check the connection!', FPP_TEXT_DOMAIN).'</a>';
  56.                 } else {
  57.                         $message = sprintf(__('Facebook is not reachable from your server: %s', FPP_TEXT_DOMAIN), $message).'<br /><a target="_blank" href="'.FPP_BASE_URL.'diagnosis.php">'.__('Check your connection!', FPP_TEXT_DOMAIN).'</a>';
  58.                 }
  59.                 parent::__construct($message, $code);
  60.         }
  61. }
  62.  
  63. class FacebookErrorException extends CommunicationException {
  64.         public function __construct($message, $code = 0) {
  65.                 $message = sprintf(__('Facebook returned an error: %s', FPP_TEXT_DOMAIN), $message);
  66.                 parent::__construct($message, $code);
  67.         }
  68. }
  69.  
  70. class FacebookUnexpectedErrorException extends CommunicationException {
  71.         public function __construct($message = null, $code = 0) {
  72.                 if (empty($message)) {
  73.                         $message = __('Facebook returned an unexpected error.', FPP_TEXT_DOMAIN).' '.__('Try to resolve this issue, update the plugin or <a target="_blank" href="http://wordpress.org/tags/facebook-page-publish">inform the author</a> about the problem.', FPP_TEXT_DOMAIN);
  74.                 } else {
  75.                         $message = sprintf(__('Facebook returned an unexpected error: %s', FPP_TEXT_DOMAIN), $message).'<br />'.__('Try to resolve this issue, update the plugin or <a target="_blank" href="http://wordpress.org/tags/facebook-page-publish">inform the author</a> about the problem.', FPP_TEXT_DOMAIN);
  76.                 }
  77.                 parent::__construct($message, $code);
  78.         }
  79. }
  80.  
  81. class FacebookUnexpectedDataException extends CommunicationException {
  82.         public function __construct($message = null, $code = 0) {
  83.                 if (empty($message)) {
  84.                         $message = __('Facebook returned an unexpected dataset.', FPP_TEXT_DOMAIN).' '.__('Try to resolve this issue, update the plugin or <a target="_blank" href="http://wordpress.org/tags/facebook-page-publish">inform the author</a> about the problem.', FPP_TEXT_DOMAIN);
  85.                 } else {
  86.                         $message = sprintf(__('Facebook returned an unexpected dataset: %s', FPP_TEXT_DOMAIN), $message).'<br />'.__('Try to resolve this issue, update the plugin or <a target="_blank" href="http://wordpress.org/tags/facebook-page-publish">inform the author</a> about the problem.', FPP_TEXT_DOMAIN);
  87.                 }
  88.                 parent::__construct($message, $code);
  89.         }
  90. }
  91.  
  92. /**********************************************************************
  93.  * Action handler
  94.  **********************************************************************/
  95.  
  96.  # Add publish actions for all post types (posts, pages, attachements, custom post types):
  97. #$post_types = get_post_types(array('exclude_from_search' => false), 'objects');
  98. #foreach ($post_types as $post_type) {
  99. #        add_action('future_'.$post_type->name, 'fpp_future_action');
  100. #        add_action('publish_'.$post_type->name, 'fpp_publish_action');
  101. #}
  102.  
  103. add_action('transition_post_status', 'fpp_transition_post_status', 10, 3);
  104. function fpp_transition_post_status($new_status, $old_status, $post) {
  105.         if ($new_status == 'future') fpp_future_action($post->ID, $old_status);
  106.         else if ($new_status == 'publish') fpp_publish_action($post->ID, $old_status);
  107. }
  108.  
  109. add_action('admin_init', 'fpp_admin_init_action');
  110. add_action('admin_menu', 'fpp_admin_menu_action');
  111. add_action('wp_head', 'fpp_head_action');
  112. add_action('post_submitbox_start', 'fpp_post_submitbox_start_action');
  113. add_action('init', 'fpp_init_action');
  114.  
  115. function fpp_init_action() {
  116.         //global $locale; $locale='de_DE';
  117.         load_plugin_textdomain(FPP_TEXT_DOMAIN, false, dirname(plugin_basename(__FILE__)));
  118. }
  119.  
  120. /**
  121.  * Called on html head rendering. Prints meta tags to make posts appear
  122.  * correctly in Facebook.
  123.  */
  124. function fpp_head_action() {
  125.         global $post;
  126.  
  127.         if (is_object($post) /*&& ($post->post_type == 'post') */ && is_singular()) {
  128.                 fpp_render_meta_tags($post);
  129.         }
  130. }
  131.  
  132. /**
  133.  * Called on admin menu rendering, adds an options page and its
  134.  * rendering callback.
  135.  *
  136.  * @see fpp_render_options_page()
  137.  */
  138. function fpp_admin_menu_action() {
  139.         $page = add_options_page(__('Facebook Page Publish Options', FPP_TEXT_DOMAIN), __('Facebook Page Publish', FPP_TEXT_DOMAIN), 'manage_options', __FILE__, 'fpp_render_options_page');
  140.        
  141.         add_action('admin_print_scripts-'.$page, 'fpp_admin_print_scripts_action');
  142.         add_action('admin_print_styles-'.$page, 'fpp_admin_print_styles_action');
  143. }
  144.  
  145. /**
  146.  * Called when a user accesses the admin area. Registers settings and a
  147.  * sanitization callback. Initializes the plugin options when a version
  148.  * update has been detected.
  149.  *
  150.  * @see fpp_validate_options
  151.  */
  152. function fpp_admin_init_action() {
  153.         register_setting('fpp_options_group', 'fpp_options', 'fpp_validate_options');
  154.        
  155.         $version = get_option('fpp_installed_version');
  156.         if ($version != FPP_VERSION) {
  157.                 fpp_initialize_options();
  158.         }
  159. }
  160.  
  161.  
  162. /**
  163.  * Loads media gallery scripts.
  164.  *
  165.  * @last_review 0.3.0
  166.  */
  167. function fpp_admin_print_scripts_action() {
  168.         wp_enqueue_script('media-upload');
  169.         wp_enqueue_script('thickbox');
  170.         wp_register_script('fpp-upload', FPP_BASE_URL.'fpp_script.js', array('jquery','media-upload','thickbox'));
  171.         wp_enqueue_script('fpp-upload');
  172. }
  173.  
  174. /**
  175.  * Loads media gallery styles.
  176.  *
  177.  * @last_review 0.3.0
  178.  */
  179. function fpp_admin_print_styles_action() {
  180.         wp_enqueue_style('thickbox');
  181. }
  182.  
  183. /**
  184.  * Called when the submitbox is rendered. Renders a publish to Facebook
  185.  * button if the current user has the 'publish_posts' permission.
  186.  *
  187.  * @last_review 0.3.0
  188.  */
  189. function fpp_post_submitbox_start_action() {
  190.         global $post;
  191.  
  192.         if (is_object($post) and /*($post->post_type == 'post') and*/ current_user_can('publish_posts')) {
  193.                 fpp_render_post_button();
  194.         }
  195. }
  196.  
  197. /**
  198.  * Called on any_to_future post state transition (scheduled post).
  199.  * Marks any given post for publishing on Facebook
  200.  * - if it was send directly from the admin panel and the
  201.  *   post-to-facebook checkbox was checked.
  202.  * Marks any given post for NOT publishing on Facebook
  203.  * - if it was send directly from the admin panel and the
  204.  *   post-to-facebook checkbox was NOT checked.
  205.  *
  206.  * @last_review 0.3.0
  207.  */
  208. function fpp_future_action($post_id, $old_status) {
  209.         $send_from_admin = isset($_REQUEST['fpp_send_from_admin']);
  210.        
  211.         if ($send_from_admin) {
  212.                 $post = get_post($post_id);
  213.                 if (/*($post->post_type == 'post')*/ true) {
  214.                         if (!empty($_REQUEST['fpp_post_to_facebook'])) { // Directly send from the user (admin panel) with active checkbox
  215.                                 $options = get_option('fpp_options');
  216.                                 update_post_meta($post->ID, '_fpp_is_scheduled', true);
  217.                         } else {
  218.                                 update_post_meta($post->ID, '_fpp_is_scheduled', false);
  219.                         }
  220.                 }
  221.         }
  222. }
  223.  
  224. /**
  225.  * Called on any_to_publish post state transition (published post).
  226.  * Publishes any given non password protected post to Facebook
  227.  * - if it was send directly from the admin panel and the
  228.  *   post-to-facebook checkbox was checked.
  229.  * - if it was previously marked for publishing on Facebook (scheduled).
  230.  * - if it was NOT send directly from the admin panel and the plugin
  231.  *   is set to always post on Facebook and the post is not already
  232.  *   published (on wordpress or Facebook).
  233.  *
  234.  * @see fpp_render_post_button()
  235.  */
  236. function fpp_publish_action($post_id, $old_status) {
  237.         $post = get_post($post_id);
  238.         if (/*($post->post_type == 'post')*/ true) {
  239.        
  240.                 $object_access_token = get_option('fpp_object_access_token');
  241.                 if (!empty($object_access_token)) { // Incomplete plugin configuration, do nothing, report no error
  242.                
  243.                         $send_from_admin = isset($_REQUEST['fpp_send_from_admin']);
  244.                         unset($_REQUEST['fpp_send_from_admin']); // Sometimes fpp_publish_action is called twice in a row
  245.                        
  246.                         $options = get_option('fpp_options');
  247.                         try {
  248.                                 if ($send_from_admin && !empty($_REQUEST['fpp_post_to_facebook'])) { // Directly send from the user (admin panel) with active post checkbox
  249.                                         // If post was already published on FB, delete it from FB:
  250.                                         $post_id = get_post_meta($post->ID, '_fpp_post_id', true);
  251.                                         if (!empty($post_id)) { // Old posts (version <= 0.3.7) do not contain a post_id
  252.                                                 fpp_unpublish_from_facebook($post_id, $options['object_id'], get_option('fpp_object_access_token'));
  253.                                         }
  254.                                
  255.                                         $post_id = fpp_publish_to_facebook($post, $options['object_id'], get_option('fpp_object_access_token'));
  256.                                         update_post_meta($post->ID, '_fpp_is_published', true);
  257.                                         update_post_meta($post->ID, '_fpp_post_id', $post_id);
  258.                                 }
  259.                                 else if (get_post_meta($post->ID, '_fpp_is_scheduled', true) == true) { // Scheduled post previously marked for Facebook publishing by the user
  260.                                         // If post was already published, delete it from FB:
  261.                                         $post_id = get_post_meta($post->ID, '_fpp_post_id', true);
  262.                                         if (!empty($post_id)) { // Old posts (version <= 0.3.7) do not contain a post_id
  263.                                                 fpp_unpublish_from_facebook($post_id, $options['object_id'], get_option('fpp_object_access_token'));
  264.                                         }
  265.                                        
  266.                                         $post_id = fpp_publish_to_facebook($post, $options['object_id'], get_option('fpp_object_access_token'));
  267.                                         update_post_meta($post->ID, '_fpp_is_published', true);
  268.                                         delete_post_meta($post->ID, '_fpp_is_scheduled');
  269.                                         update_post_meta($post->ID, '_fpp_post_id', $post_id);
  270.                                 }
  271.                                 else if (!$send_from_admin && (array_search('_fpp_is_scheduled', get_post_custom_keys($post->ID)) === false) && !get_post_meta($post->ID, '_fpp_is_published', true) && ($old_status != 'publish')) { // not send from admin panel, not already published (FB and WP), user never decided for or against publishing
  272.                                         if (empty($post->post_password) and fpp_get_default_publishing($post)) { // Dont post password protected posts without the users consient
  273.          
  274.                                                 // If post was already published, delete it from FB:
  275.                                                 $post_id = get_post_meta($post->ID, '_fpp_post_id', true);
  276.                                                 if (!empty($post_id)) { // Old posts (version <= 0.3.7) do not contain a post_id
  277.                                                         fpp_unpublish_from_facebook($post_id, $options['object_id'], get_option('fpp_object_access_token'));
  278.                                                 }
  279.                                                
  280.                                                 $post_id = fpp_publish_to_facebook($post, $options['object_id'], get_option('fpp_object_access_token'));
  281.                                                 update_post_meta($post->ID, '_fpp_is_published', true);
  282.                                                 update_post_meta($post->ID, '_fpp_post_id', $post_id);
  283.                                         }
  284.                                 }
  285.                         } catch (CommunicationException $exception) {
  286.                                 update_option('fpp_error', sprintf(__('<p>While publishing "%1$s" to Facebook, an error occured: </p><p><strong>%2$s</strong></p>', FPP_TEXT_DOMAIN), $post->post_title, $exception->getMessage()));
  287.                         }
  288.                 }
  289.         }
  290. }
  291.  
  292. /**********************************************************************
  293.  * Facebook functions
  294.  **********************************************************************/
  295. /**
  296.  * Publishes the given post to a Facebook page.
  297.  *
  298.  * @param post Wordpress post to publish
  299.  * @param object_id Facebook page or wall ID
  300.  * @param object_acces_token Access token for the given object
  301.  * @return Facebook(!) post id
  302.  *
  303.  * @last_review 0.3.8
  304.  */
  305. function fpp_publish_to_facebook($post, $object_id, $object_acces_token) {
  306.         $options = get_option('fpp_options');
  307.        
  308.         if (empty($post->post_password) and $options['show_post_text']) {
  309.                 $message = fpp_extract_message(apply_filters('the_excerpt', $post->post_excerpt), $options['show_post_link']);
  310.                 if (empty($message)) $message = fpp_extract_message(apply_filters('the_content', $post->post_content), $options['show_post_link']);
  311.                
  312.                 if ($options['show_post_link']) {
  313.                         $message = wp_kses($message, array('a' => array('href' => array())));
  314.                         $message = preg_replace('/<a[^>]*?href *= *["|\']([^"|\']+).[^>]*?>(.*?)<\/a>\s*/is', '${2} ${1} ', $message);
  315.                         $message = trim(stripslashes(html_entity_decode($message, ENT_QUOTES, 'UTF-8')));
  316.                 }
  317.                 else {
  318.                         $message = trim(stripslashes(html_entity_decode(wp_filter_nohtml_kses($message), ENT_QUOTES, 'UTF-8')));
  319.                 }
  320.                
  321.                 // Remove empty lines:
  322.                 $message = preg_replace("/\n\s*\n(\n|\s)*/s", "\n",$message);
  323.                
  324.                 if (strpos($message, '<!--more-->') !== false) {
  325.                         $message = substr($message, 0, strpos($message, '<!--more-->'));
  326.                 }
  327.                
  328.                 // Remove HTML Comments:
  329.                 $message = preg_replace('/<!--(.*)-->/Uis', '', $message);
  330.                
  331.                 if (strlen($message) >= FPP_FACEBOOK_LINK_DESCRIPTION_MAX_LENGTH) {
  332.                         $last_space_pos = strrpos(substr($message, 0, FPP_FACEBOOK_LINK_DESCRIPTION_MAX_LENGTH - 3), ' ');
  333.                         $message = substr($message, 0, !empty($last_space_pos) ? $last_space_pos : FPP_FACEBOOK_LINK_DESCRIPTION_MAX_LENGTH - 3).__('...', FPP_TEXT_DOMAIN);
  334.                 }
  335.         } else {
  336.                 $message = ''; // Password protected, no content displayed.
  337.         }
  338.        
  339.         // Publish:
  340.         $request = new WP_Http;
  341.        
  342.         // Facebook bug 19336 is resolved, so /links should work (usage of /feed no longer necessary):
  343.         $api_url = 'https://graph.facebook.com/'.urlencode($object_id).'/links';
  344.          
  345.         $body = array('message' => $message, 'link' => get_permalink($post->ID), 'access_token' => $object_acces_token);
  346.         $response = $request->request($api_url, array('method' => 'POST', 'body' => $body, 'timeout' => FPP_REQUEST_TIMEOUT, 'sslverify' => fpp_get_ssl_verify()));
  347.  
  348.         // Error detection:
  349.         if (array_key_exists('errors', $response))
  350.                 throw new FacebookUnreachableException(!empty($response->errors) ? array_pop(array_pop($response->errors)) : '');
  351.  
  352.         $json_response = json_decode($response['body']);
  353.         if (is_object($json_response) and property_exists($json_response, 'error')) {
  354.                 throw new FacebookUnexpectedErrorException((is_object($json_response->error) and property_exists($json_response->error, 'message')) ? $json_response->error->message : '');
  355.         }
  356.        
  357.         return $json_response->id; // Return ID of the published link (usefull to delete it later).
  358. }
  359.  
  360. /**
  361.  * Extract a message to be posted on Facebook from a text string that
  362.  * may contain html, WP shortcodes etc.
  363.  *
  364.  * @param string
  365.  * @param include_links Include URLs from HTML links if true
  366.  * @return extracted message ready to be posted on Facebook
  367.  */
  368. function fpp_extract_message($string, $include_links) {
  369.         $message = do_shortcode($string);
  370.  
  371.         $message = preg_replace('~<\s*\bscript\b[^>]*>(.*?)<\s*\/\s*script\s*>~is', '', $message); // Remove javascript code (not only tags)
  372.        
  373.         $message = preg_replace('/<!--(.*)-->/Uis', '', $message); // Remove HTML comments (not only tags)
  374.        
  375.         if ($include_links) {
  376.                 $message = wp_kses($message, array('a' => array('href' => array())));
  377.                 $message = preg_replace('/<a[^>]*?href *= *["|\']([^"|\']+).[^>]*?>(.*?)<\/a>\s*/is', '${2} ${1} ', $message);
  378.                 $message = trim(stripslashes(html_entity_decode($message, ENT_QUOTES, 'UTF-8')));
  379.         }
  380.         else {
  381.                 $message = trim(stripslashes(html_entity_decode(wp_filter_nohtml_kses($message), ENT_QUOTES, 'UTF-8')));
  382.         }
  383.  
  384.         // Remove empty lines:
  385.         $message = preg_replace("/\n\s*\n(\n|\s)*/s", "\n",$message);
  386.  
  387.         if (strpos($message, '<!--more-->') !== false) {
  388.                 $message = substr($message, 0, strpos($message, '<!--more-->'));
  389.         }
  390.  
  391.         if (strlen($message) >= FPP_FACEBOOK_LINK_DESCRIPTION_MAX_LENGTH) {
  392.                 $last_space_pos = strrpos(substr($message, 0, FPP_FACEBOOK_LINK_DESCRIPTION_MAX_LENGTH - 3), ' ');
  393.                 $message = substr($message, 0, !empty($last_space_pos) ? $last_space_pos : FPP_FACEBOOK_LINK_DESCRIPTION_MAX_LENGTH - 3).__('...', FPP_TEXT_DOMAIN);
  394.         }
  395.        
  396.         return $message;
  397. }
  398.  
  399. /**
  400.  * Removes the given post from a Facebook page. If a post with the given
  401.  * ID does not exist on FB, the return value is 'false.
  402.  *
  403.  * @param post_id Facebook(!) post ID
  404.  * @param object_id Facebook page or wall ID
  405.  * @param object_acces_token Access token for the given object
  406.  * @return True if post was found and removed
  407.  *
  408.  * @last_review 0.3.8
  409.  */
  410. function fpp_unpublish_from_facebook($post_id, $object_id, $object_acces_token) {
  411.         $request = new WP_Http;
  412.         $api_url = 'https://graph.facebook.com/'.urlencode($object_id).'_'.urlencode($post_id).'?method=delete';
  413.         $body = array('access_token' => $object_acces_token);
  414.         $response = $request->request($api_url, array('method' => 'POST', 'body' => $body, 'timeout' => FPP_REQUEST_TIMEOUT, 'sslverify' => fpp_get_ssl_verify()));
  415.  
  416.         // Error detection:
  417.         if (array_key_exists('errors', $response))
  418.                 throw new FacebookUnreachableException(!empty($response->errors) ? array_pop(array_pop($response->errors)) : '');
  419.  
  420.         $object = json_decode($response['body']);
  421.        
  422.         if (is_object($object) and property_exists($object, 'error')) {
  423.                 if (property_exists($object->error, 'message')) {
  424.                         if (strpos($object->error->message, 'Invalid parameter') !== false)
  425.                                 return false;
  426.                                
  427.                          throw new FacebookUnexpectedErrorException($object->error->message);
  428.                  }
  429.                  throw new FacebookUnexpectedErrorException('');
  430.         }
  431.                        
  432.         return $response['body'] === 'true';
  433. }
  434.  
  435. /**
  436.  * Checks whether a given access_token is valid and has the give
  437.  * permissions.
  438.  *
  439.  * @param object_id Page or profile ID
  440.  * @param object_type Either 'page' or 'profile'
  441.  * @param object_access_token The access token to validate
  442.  * @param permissions Array of permission strings to validate
  443.  * @return True if access_token valid and all permissions granted
  444.  *
  445.  * @last_review 0.3.1
  446.  */
  447. function fpp_verify_profile_access_permissions($profile_access_token, $permissions) {
  448.         $request = new WP_Http;
  449.         $api_url =  'https://api.facebook.com/method/fql.query?access_token='.urlencode($profile_access_token).'&format=json&query='.urlencode('SELECT '.implode(',', $permissions).' FROM permissions WHERE uid = me()');
  450.         $response = $request->get($api_url, array('timeout' => FPP_REQUEST_TIMEOUT, 'sslverify' => fpp_get_ssl_verify()));
  451.  
  452.         if (array_key_exists('errors', $response))
  453.                 throw new FacebookUnreachableException((!empty($response->errors) ? array_pop(array_pop($response->errors)) : ''));
  454.  
  455.         $json_response = json_decode($response['body']);
  456.         if (is_object($json_response) and property_exists($json_response, 'error_msg')) {
  457.                 if (property_exists($json_response, 'error_code') and ($json_response->error_code == 190)) // Access token expired or invalid
  458.                         return false;
  459.                 if (property_exists($json_response, 'error_code') and ($json_response->error_code == 104)) // 'Requires valid signature'-error
  460.                         return false;
  461.                 throw new FacebookUnexpectedErrorException($json_response->error_msg);
  462.         }
  463.         if (!is_array($json_response)) {
  464.                 throw new FacebookUnexpectedDataException();
  465.         }
  466.         $json_response = array_pop($json_response);
  467.         if (!is_object($json_response)) {
  468.                 throw new FacebookUnexpectedDataException();
  469.         }
  470.        
  471.         foreach ($permissions as $permission) {
  472.                 if (!property_exists($json_response, $permission) or empty($json_response->$permission)) return false;
  473.         }
  474.         return true;
  475. }
  476.  
  477. function fpp_verify_page_access_token($page_id, $page_access_token) {
  478.         $request = new WP_Http;
  479.         $api_url = 'https://graph.facebook.com/'.urlencode($page_id).'?access_token='.urlencode($page_access_token);
  480.         $response = $request->get($api_url, array('timeout' => FPP_REQUEST_TIMEOUT, 'sslverify' => fpp_get_ssl_verify()));
  481.  
  482.         if (array_key_exists('errors', $response))
  483.                 throw new FacebookUnreachableException(!empty($response->errors) ? array_pop(array_pop($response->errors)) : '');
  484.        
  485.         # TODO: verify access_permissions...
  486.        
  487.         return ($response['response']['code'] == 200);
  488. }
  489.  
  490. /**
  491.  * Classifies Facebook object ids.
  492.  *
  493.  * @param object_ids Array of Facebook object ids
  494.  * @return map with a type string for each Facebook object id
  495.  *
  496.  * @last_review 0.3.0
  497.  */
  498. function fpp_classify_facebook_objects($object_ids) {
  499.         $numerical_object_ids = array_filter($object_ids, 'is_numeric'); // Alphabetical id's produce a Facebook error.
  500.        
  501.         $request = new WP_Http;
  502.         $api_url = 'https://api.facebook.com/method/fql.query?format=json&query='.urlencode('SELECT id, type FROM object_url WHERE id IN ('.implode(',', $numerical_object_ids).')');
  503.         $response = $request->get($api_url, array('timeout' => FPP_REQUEST_TIMEOUT, 'sslverify' => fpp_get_ssl_verify()));
  504.  
  505.         if (array_key_exists('errors', $response))
  506.                 throw new FacebookUnreachableException(!empty($response->errors) ? array_pop(array_pop($response->errors)) : '');
  507.  
  508.         $json_response = json_decode($response['body']);
  509.         if (is_object($json_response) and property_exists($json_response, 'error')) {
  510.                 throw new FacebookUnexpectedErrorException((is_object($json_response->error) and property_exists($json_response->error, 'message')) ? $json_response->error->message : '');
  511.         }
  512.         if (!is_array($json_response)) {
  513.                 throw new FacebookUnexpectedDataException();
  514.         }
  515.        
  516.         $result = array();
  517.         $float_object_ids = array_map('floatval', $numerical_object_ids); // PHP <= 5.2 is missing JSON_BIGINT
  518.         foreach ($json_response as $json_response_entry) {
  519.                 if (!property_exists($json_response_entry, 'type') or !property_exists($json_response_entry, 'id'))
  520.                         throw new FacebookUnexpectedDataException();
  521.                 $result[$numerical_object_ids[array_search($json_response_entry->id, $float_object_ids)]] = $json_response_entry->type;
  522.         }
  523.        
  524.         foreach ($object_ids as $object_id) {
  525.                 if (!array_key_exists($object_id, $result))
  526.                         $result[$object_id]= '';
  527.         }
  528.        
  529.         return $result;
  530. }
  531.  
  532. /**
  533.  * Checks whether a given Facebook application id and its secret are
  534.  * valid.
  535.  *
  536.  * @param app_id Application id to verify
  537.  * @param app_secret Application secret
  538.  * @param redirect_uri URL equal to the URL in the Facebook app settings
  539.  * @return True if the given application id and secret are valid
  540.  *
  541.  * @last_review 0.3.0
  542.  */
  543. function fpp_is_valid_facebook_application($app_id, $app_secret, $redirect_uri) {
  544.         $request = new WP_Http;
  545.         $api_url = 'https://graph.facebook.com/oauth/access_token?client_id='.urlencode($app_id).'&client_secret='.urlencode($app_secret).'&redirect_uri='.urlencode($redirect_uri).'&code=SOME_INVALID_CODE';
  546.         $response = $request->get($api_url, array('timeout' => FPP_REQUEST_TIMEOUT, 'sslverify' => fpp_get_ssl_verify()));
  547.  
  548.         if (array_key_exists('errors', $response))
  549.                 throw new FacebookUnreachableException(!empty($response->errors) ? array_pop(array_pop($response->errors)) : '');
  550.  
  551.         $object = json_decode($response['body']);
  552.        
  553.         if (property_exists($object, 'error')) {
  554.                 if (property_exists($object->error, 'message')) {
  555.                         if (strpos($object->error->message, 'Error validating client secret') !== false)
  556.                                 return false;
  557.                
  558.                         if (strpos($object->error->message, 'Invalid verification code format') !== false)
  559.                                 return true;
  560.                              
  561.                         if (strpos($object->error->message, 'Invalid redirect_uri') !== false)  
  562.                                 throw new FacebookErrorException(sprintf(__('The site URL in your Facebook application settings does not match your wordpress blog URL. Please refer to the <a target="_blank" href="%s">detailed setup instructions</a>.', FPP_TEXT_DOMAIN), FPP_BASE_URL.'setup.htm#site_url'));
  563.                        
  564.                         throw new FacebookUnexpectedErrorException($object->error->message);
  565.                 }
  566.                 throw new FacebookUnexpectedErrorException();
  567.         }
  568.         throw new FacebookUnexpectedDataException();
  569. }
  570.  
  571. /**
  572.  * Acquires an object access token with all these permissions that
  573.  * were specified when retrieving the code.
  574.  *
  575.  * @param app_id Application ID
  576.  * @param app_secret Application secret
  577.  * @param object_id Facebook page or profile ID
  578.  * @param object_type Either 'profile' or 'page'
  579.  * @param redirect_uri URL used to get the transaction code
  580.  * @param code Transaction code (refer to the OAuth protocoll docs)
  581.  *
  582.  * @last_rewiev 0.3.1
  583.  */
  584. function fpp_acquire_profile_access_token($app_id, $app_secret, $redirect_uri, $code) {
  585.         $request = new WP_Http;
  586.         $api_url = 'https://graph.facebook.com/oauth/access_token?client_id='.urlencode($app_id).'&redirect_uri='.urlencode($redirect_uri).'&client_secret='.urlencode($app_secret).'&code='.urlencode($code);
  587.         $response = $request->get($api_url, array('timeout' => FPP_REQUEST_TIMEOUT, 'sslverify' => fpp_get_ssl_verify()));
  588.  
  589.         if (array_key_exists('errors', $response))
  590.                 throw new FacebookUnreachableException(!empty($response->errors) ? array_pop(array_pop($response->errors)) : '');
  591.  
  592.         $json_response = json_decode($response['body']);
  593.         if (is_object($json_response) and property_exists($json_response, 'error') and property_exists($json_response->error, 'message')) {
  594.                 if (is_string($json_response->error->message) and (strpos($json_response->error->message, 'Code was invalid or expired') !== false)) {
  595.                         throw new FacebookErrorException(__('Your authorization code was invalid or expired. Please try again. If the problem persists update the plugin or <a target="_blank" href="http://wordpress.org/tags/facebook-page-publish">inform the author</a>.', FPP_TEXT_DOMAIN));
  596.                 }
  597.                 else throw new FacebookUnexpectedErrorException($json_response->error->message);
  598.         }
  599.         $access_token_url = $response['body'];
  600.  
  601.         preg_match('/^.+=\s*(.+)/', $access_token_url, $matches);
  602.         if (!empty($matches[1])) return $matches[1];
  603.        
  604.         throw new FacebookUnexpectedDataException();
  605. }
  606.  
  607. function fpp_acquire_page_access_token($page_id, $profile_access_token) {
  608.         $request = new WP_Http;
  609.         $api_url = 'https://graph.facebook.com/me/accounts?access_token='.urlencode($profile_access_token);
  610.         $response = $request->get($api_url, array('timeout' => FPP_REQUEST_TIMEOUT, 'sslverify' => fpp_get_ssl_verify()));
  611.  
  612.         if (array_key_exists('errors', $response))
  613.                 throw new FacebookUnreachableException(!empty($response->errors) ? array_pop(array_pop($response->errors)): '');
  614.  
  615.         $json_response = json_decode($response['body']);
  616.         if (!is_object($json_response) || !property_exists($json_response, 'data'))
  617.                 throw new FacebookUnexpectedErrorException(__('Can\'t access Facebook user account information.', FPP_TEXT_DOMAIN));
  618.  
  619.         foreach ($json_response->data as $account) {
  620.                 if ($account->id == $page_id) {
  621.                         if (!property_exists($account, 'access_token'))
  622.                                 throw new FacebookUnexpectedErrorException(__('Some or all access permissions for your page are missing.', FPP_TEXT_DOMAIN));
  623.                         $page_access_token = $account->access_token;
  624.                         break;
  625.                 }
  626.         }
  627.         if (!isset($page_access_token))
  628.                 throw new FacebookErrorException(__('Your Facebook user account data contains no page with the given ID. You have to be administrator of the given page.', FPP_TEXT_DOMAIN));
  629.                
  630.         return $page_access_token;
  631. }
  632.        
  633. /**********************************************************************
  634.  * Getter
  635.  **********************************************************************/
  636. /**
  637.  * Determines whether a post should be pulished by default or not,
  638.  * depending on the plugin options and the post category.
  639.  */
  640. function fpp_get_default_publishing($post) {
  641.         $options = get_option('fpp_options');
  642.        
  643.         if ($options['default_publishing'] == 'all') return true;
  644.         if ($options['default_publishing'] == 'category') {
  645.                 $categories = get_the_category($post->ID);
  646.                 foreach ($categories as $category) {
  647.                         if (array_search($category->cat_ID, $options['default_publishing_categories']) !== false)
  648.                                 return true;
  649.                 }
  650.         }
  651.         return false;
  652. }
  653.  
  654. /**
  655.  * @return True if SSL certificates should be checked
  656.  */
  657. function fpp_get_ssl_verify() {
  658.         $options = get_option('fpp_options');
  659.         return !$options['ignore_ssl'];
  660. }
  661.  
  662. /**
  663.  * @param object_type Array of string or string, either 'page' or 'profile'
  664.  * @return Write permissions for a given Facebook object as string
  665.  * @last_review 0.3.1
  666.  */
  667. function fpp_get_required_permissions($object_type) {
  668.         if (!is_array($object_type)) $object_type = array($object_type);
  669.        
  670.         $permissions = array('offline_access', 'share_item');
  671.         if (array_search('page', $object_type) !== false) {
  672.                 $permissions[] = 'manage_pages';
  673.         }
  674.         if (array_search('group', $object_type) !== false) {
  675.                 $permissions[] = 'user_groups';
  676.         }
  677.  
  678.         return $permissions;
  679. }
  680.  
  681. /**
  682.  * @return Post author name as a string
  683.  * @last_review 0.3.1
  684.  */
  685. function fpp_get_post_author($post) {
  686.         $user_info = get_userdata($post->post_author);
  687.         $author = trim(apply_filters('the_author', $user_info->display_name));
  688.         if (empty($author)) {
  689.                 $author = $user_info->user_login;
  690.         }
  691.         return $author;
  692. }
  693.  
  694. /**
  695.  * @return Post categories as csv string
  696.  * @last_review 0.3.0
  697.  */
  698. function fpp_get_post_categories($post) {
  699.         $categories = get_the_category($post->ID);
  700.         $description = '';
  701.         if (!empty($categories)) {
  702.                 $description = $categories[0]->cat_name;
  703.                 for ($i = 1; $i < sizeof($categories); ++$i)
  704.                         $description .= ', '.apply_filters('the_category', $categories[$i]->cat_name);
  705.         }
  706.         return $description;
  707. }
  708.  
  709. /**
  710.  * @return URL of the featured or first embedded or attachement image as string
  711.  * @last_review 0.3.1
  712.  */
  713. function fpp_get_post_image($post) {
  714.         $image_url = '';
  715.        
  716.         if (current_theme_supports('post-thumbnails')) { // get_post_thumbnail_id must be supported by the theme!
  717.                 $thumbnail_id = get_post_thumbnail_id($post->ID);
  718.                 if ($thumbnail_id !== null) {
  719.                         $image_url = wp_get_attachment_image_src($thumbnail_id);
  720.                         $image_url = $image_url[0];
  721.                 }
  722.         }
  723.        
  724.         if (empty($image_url)) { // Image from post content and/or excerpt
  725.                 preg_match('/<img .*?src=["|\']([^"|\']+)/i', $post->post_excerpt.$post->post_content, $matches);
  726.                 if (!empty($matches[1])) $image_url = $matches[1];
  727.         }
  728.        
  729.         if (empty($image_url)) {
  730.                 $images = get_children('post_type=attachment&post_mime_type=image&post_parent='.$post->ID);
  731.                 if (!empty($images)) {
  732.                         foreach ($images as $image_id => $value) {
  733.                                 $image = wp_get_attachment_image_src($image_id);
  734.                                 $image_url = $image[0];
  735.                                 break;
  736.                         }
  737.                 }
  738.         }
  739.         return $image_url;
  740. }
  741.  
  742. /**********************************************************************
  743.  * HTML rendering
  744.  **********************************************************************/
  745. /**
  746.  * Renders the options page. Uses the settings API (options validation, checking and storing by WP).
  747.  * Also validates certain options (Facebook access) that need redirecting.
  748.  * @last_review 0.3.1
  749.  */
  750. function fpp_render_options_page() {
  751.         $options = get_option('fpp_options');
  752.  
  753.         $error = get_option('fpp_error');
  754.         if (!empty($error)) {
  755.               echo '<div class="error">'.$error.'</div>';  
  756.               update_option('fpp_error', '');
  757.         }
  758.        
  759.         $profile_access_token = get_option('fpp_profile_access_token');
  760.        
  761.         if ($options['app_id_valid'] and $options['app_secret_valid'] and empty($profile_access_token) and array_key_exists('code', $_GET)) {
  762.                 // User clicked the authorize button, get profile_access_token:
  763.                 try {
  764.                         $profile_access_token = fpp_acquire_profile_access_token($options['app_id'], $options['app_secret'], FPP_ADMIN_URL, $_GET['code']);
  765.                         update_option('fpp_profile_access_token', $profile_access_token);
  766.                         update_option('fpp_object_access_token', '');
  767.                 } catch (CommunicationException $exception) {
  768.                         echo '<div class="error"><p><strong>'.$exception->getMessage().'</strong></p><p>'.__('Your application\'s access permissions could not be granted.', FPP_TEXT_DOMAIN).'</p></div>';
  769.                 }
  770.         }
  771.  
  772.         // Check if access tokens are valid:
  773.         if ($options['app_id_valid'] and $options['app_secret_valid']) {
  774.                 try {
  775.                         // Verify only profile access token (== object access token):
  776.                         if (!fpp_verify_profile_access_permissions($profile_access_token, fpp_get_required_permissions(array('page', 'profile', 'group')))) {
  777.                                 $profile_access_token = '';
  778.                                 $object_access_token = '';
  779.                                 update_option('fpp_object_access_token', $object_access_token);
  780.                                 update_option('fpp_profile_access_token', $profile_access_token);
  781.                                 throw new CommunicationException(__('Some or all access permissions are missing. Please click the button <em>Grant access rights!</em> and authorize the plugin to post to your Facebook profile or page.', FPP_TEXT_DOMAIN));
  782.                         }
  783.                        
  784.                         $object_access_token = get_option('fpp_object_access_token');
  785.                        
  786.                         // Acquire object access token if empty:
  787.                         if (!empty($profile_access_token) and $options['object_id_valid'] and empty($object_access_token)) {
  788.                                 if ($options['object_type'] == 'page') {
  789.                                         $object_access_token = fpp_acquire_page_access_token($options['object_id'], $profile_access_token);
  790.                                         update_option('fpp_object_access_token', $object_access_token);
  791.                                 } else {
  792.                                         update_option('fpp_object_access_token', $profile_access_token);
  793.                                 }
  794.                         }
  795.                        
  796.                         // Verify page access and profile access token together:
  797.                         if ($options['object_id_valid'] and ($options['object_type'] == 'page')) {
  798.                                 if (!fpp_verify_page_access_token($options['object_id'], $object_access_token)) {
  799.                                         $profile_access_token = '';
  800.                                         $object_access_token = '';
  801.                                         update_option('fpp_object_access_token', $object_access_token);
  802.                                         update_option('fpp_profile_access_token', $profile_access_token);
  803.                                         throw new CommunicationException(__('Some or all access permissions are missing. Please click the button <em>Grant access rights!</em> and authorize the plugin to post to your Facebook profile or page.', FPP_TEXT_DOMAIN));
  804.                                 }
  805.                         }
  806.                 } catch (CommunicationException $exception) {
  807.                         echo '<div class="error"><p><strong>'.$exception->getMessage().'</strong></p><p>'.__('Your page or profile\'s access permissions could not be verified.', FPP_TEXT_DOMAIN).'</p></div>';
  808.                 }
  809.         }
  810.         ?>
  811.         <div class="wrap">
  812.                 <div class="icon32" id="icon-options-general"><br /></div>
  813.                 <h2>
  814.                         <?php _e('Facebook Page Publish Options', FPP_TEXT_DOMAIN) ?>
  815.                         <form style="display:inline; margin-left:1em" target="_blank" action="https://www.paypal.com/cgi-bin/webscr" method="post">
  816.                                 <input type="hidden" name="cmd" value="_s-xclick">
  817.                                 <input type="hidden" name="hosted_button_id" value="BKYW28B3GDLEY">
  818.                                 <input style="vertical-align:middle" type="image" src="<?php  echo FPP_BASE_URL ?>donate.png" border="0" name="submit" alt="Support the development of this plugin.">
  819.                                 <!--<img alt="" border="0" src="https://www.paypalobjects.com/WEBSCR-640-20110401-1/de_DE/i/scr/pixel.gif" width="1" height="1">-->
  820.                         </form>
  821.                 </h2>
  822.                 <form method="post" action="options.php">
  823.                         <?php settings_fields('fpp_options_group'); ?>
  824.                         <h3><?php _e('1. Facebook Connection', FPP_TEXT_DOMAIN) ?></h3>
  825.                         <p><?php printf(__('Connect your blog to Facebook. See <a target="_blank" href="%s">detailed setup instructions</a> for help.', FPP_TEXT_DOMAIN), FPP_BASE_URL.'setup.htm') ?></p>
  826.                         <table class="form-table">
  827.                                 <tr valign="top">
  828.                                         <th scope="row"><label for="fpp_options-app_id"><?php _e('Application ID', FPP_TEXT_DOMAIN) ?></label></th>
  829.                                         <td>
  830.                                         <input style="color:<?php echo $options['app_id_valid'] ? 'green' : (empty($options['app_id']) ? 'black' : 'red') ?>" id="fpp_options-app_id" name="fpp_options[app_id]" type="text" value="<?php echo htmlentities($options['app_id']); ?>" />
  831.                                         <a style="font-size:1.3em" target="_blank" href="<?php echo FPP_BASE_URL ?>setup.htm#app_id">?</a>
  832.                                         <?php
  833.                                         if ($options['app_id_valid'] and $options['app_secret_valid']) {
  834.                                                 $profile_access_token = get_option('fpp_profile_access_token');
  835.                                                 if (empty($profile_access_token)) {
  836.                                                         echo '<a class="button-secondary" style="color:red" href="https://www.facebook.com/dialog/oauth?client_id='.urlencode($options['app_id']).'&redirect_uri='.urlencode(FPP_ADMIN_URL).'&scope='.urlencode(implode(',', fpp_get_required_permissions(array('page', 'profile', 'group')))).'">'.__('Grant access rights!', FPP_TEXT_DOMAIN).'</a>';
  837.                                                 }
  838.                                                 else echo '<span style="color:green">'.__('Access granted.', FPP_TEXT_DOMAIN).'</span>';// <a class="button-secondary" style="color:green" href="https://www.facebook.com/dialog/oauth?client_id='.urlencode($options['app_id']).'&redirect_uri='.urlencode(FPP_ADMIN_URL).'&scope='.urlencode(implode(',', fpp_get_required_permissions(array('page', 'profile')))).'">Renew for '.$options['app_id'].'</a>';
  839.                                         }
  840.                                         else echo '<a class="button-secondary" disabled="disabled">'.__('Grant access rights!', FPP_TEXT_DOMAIN).'</a>';      
  841.                                         ?>
  842.                                         </td>
  843.                                 </tr>
  844.                                 <tr valign="top">
  845.                                         <th scope="row"><label for="fpp_options-app_secret"><?php _e('Application Secret', FPP_TEXT_DOMAIN) ?></label></th>
  846.                                         <td><input style="color:<?php echo $options['app_secret_valid'] ? 'green' : ($options['app_id_valid'] ? 'red' : 'black') ?>" id="fpp_options-app_secret" name="fpp_options[app_secret]" type="text" value="<?php echo htmlentities($options['app_secret']); ?>" />
  847.                                         <a style="font-size:1.3em" target="_blank" href="<?php echo FPP_BASE_URL ?>setup.htm#app_secret">?</a></td>
  848.                                 </tr>
  849.                                 <tr valign="top">
  850.                                         <th scope="row"><?php _e('Compatibility', FPP_TEXT_DOMAIN) ?></th>
  851.                                         <td>
  852.                                                 <fieldset>
  853.                                                 <label style="<?php echo (!fpp_get_ssl_verify()) ? 'color:#aa6600' : '' ?>"><input id="fpp_options-ignore_ssl" type="checkbox" name="fpp_options[ignore_ssl]" value="1" <?php checked('1', $options['ignore_ssl']); ?> /> <span><?php _e('Ignore SSL Certificate', FPP_TEXT_DOMAIN) ?></span></label><br />
  854.                                                 </fieldset>
  855.                                         </td>
  856.                                 </tr>
  857.                         </table>
  858.                         <p class="submit">
  859.                                 <input type="submit" class="button-primary" value="<?php _e('Save Changes') ?>" />
  860.                         </p>
  861.                        
  862.                         <h3><?php _e('2. Publishing', FPP_TEXT_DOMAIN) ?></h3>
  863.                         <p><?php _e('Specify the posts to publish and decide on which Facebook wall they will appear.', FPP_TEXT_DOMAIN) ?></p>
  864.                         <table class="form-table">
  865.                                 <tr valign="top">
  866.                                         <th scope="row"><label for="fpp_options-object_id"><?php _e('Page or profile ID', FPP_TEXT_DOMAIN) ?></label></th>
  867.                                         <td><input style="color:<?php echo $options['object_id_valid'] ? 'green' : (empty($options['object_id']) ? 'black' : 'red') ?>" id="fpp_options-object_id" name="fpp_options[object_id]" type="text" value="<?php echo htmlentities($options['object_id']); ?>" />
  868.                                         <a style="font-size:1.3em" target="_blank" href="<?php echo FPP_BASE_URL ?>setup.htm#object_id">?</a>
  869.                                         <?php
  870.                                         $profile_access_token = get_option('fpp_profile_access_token');
  871.                                         if (!empty($profile_access_token)) {
  872.                                                 echo '<div id="object_id_list"></div>';
  873.                                                 echo '<script type="text/javascript">jQuery("#object_id_list").show_object_id_list("#fpp_options-object_id", "'.urlencode($profile_access_token).'");</script>';
  874.                                         }
  875.                                         ?>
  876.                                         </td>
  877.                                 </tr>
  878.                                 <tr valign="top">
  879.                                         <th scope="row"><?php _e('Publish by default', FPP_TEXT_DOMAIN) ?></th>
  880.                                         <td>
  881.                                                 <div style="float:left">
  882.                                                         <fieldset style="line-height:20px">
  883.                                                         <label style="vertical-align:middle"><input name="fpp_options[default_publishing]" value="all" type="radio" <?php checked('1', $options['default_publishing'] == 'all'); ?> /> <span><?php _e('all posts', FPP_TEXT_DOMAIN) ?></span></label><br />
  884.                                                         <label style="vertical-align:middle"><input name="fpp_options[default_publishing]" value="category" type="radio" <?php checked('1', $options['default_publishing'] == 'category'); ?> /> <span><?php _e('posts from selected categories', FPP_TEXT_DOMAIN) ?></span></label><br />
  885.                                                         <label style="vertical-align:middle"><input name="fpp_options[default_publishing]" value="none" type="radio" <?php checked('1', $options['default_publishing'] == 'none'); ?> /> <span><?php _e('nothing', FPP_TEXT_DOMAIN) ?></span></label><br />
  886.                                                         </fieldset>
  887.                                                 </div>
  888.                                                 <div style="margin-left:230px; width:200px; text-align:center">
  889.                                                         <select name="fpp_options[default_publishing_categories][]" multiple="multiple" style="height:60px; width:200px" size="4">
  890.                                                                 <?php
  891.                                                                 $categories = get_categories(array('hide_empty' => false, 'orderby' => 'name', 'order' => 'ASC'));
  892.                                                                 foreach ($categories as $category) {
  893.                                                                         echo '<option style="height:8pt" value="'.$category->cat_ID.'" '.((array_search($category->cat_ID, $options['default_publishing_categories']) !== false) ? 'selected="selected"' : '').'>'.$category->name.'</option>';
  894.                                                                 }
  895.                                                                 ?>
  896.                                                         </select><br />
  897.                                                         <span style="color:#999; font-size:7pt; line-height:9pt"><?php _e('Hold [Ctrl] to select multiple categories', FPP_TEXT_DOMAIN) ?></span>
  898.                                                 </div>
  899.                                         </td>
  900.                                 </tr>
  901.                         </table>
  902.                         <p class="submit">
  903.                                 <input type="submit" class="button-primary" value="<?php _e('Save Changes') ?>" />
  904.                         </p>
  905.                        
  906.                         <h3><?php _e('3. Customization', FPP_TEXT_DOMAIN) ?></h3>
  907.                         <p><?php _e('Customize the appearance of your posts on Facebook.', FPP_TEXT_DOMAIN) ?></p>
  908.                         <div style="width:450px; padding:5px; background-color:#FFF">
  909.                                 <div style="float:left; width:40px; height:40px; padding:5px; background-color:#EEE; font-size:7pt; line-height:9pt; overflow:hidden"><?php _e('Page or profile photo', FPP_TEXT_DOMAIN) ?></div>
  910.                                 <div style="margin-left:55px">
  911.                                         <span style="font-weight:bold; color:#3B5998"><?php _e('Page or profile name', FPP_TEXT_DOMAIN) ?></span>
  912.                                         <div style="margin-bottom:10px; font-size:9pt; line-height:11pt"><?php _e('This is a short excerpt of your post with an example link to Wordpress <a target="_blank" href="http://wordpress.org">http://wordpress.org</a>. Lorem ipsum dolor...', FPP_TEXT_DOMAIN) ?><br /><label><input id="fpp_options-show_post_text" type="checkbox" name="fpp_options[show_post_text]" value="1" <?php checked('1', $options['show_post_text']); ?> /> <?php _e('Post excerpt', FPP_TEXT_DOMAIN) ?></label> <label><input id="fpp_options-show_post_link" type="checkbox" name="fpp_options[show_post_link]" value="1" <?php checked('1', $options['show_post_link']); ?> /> <?php _e('Include link URLs', FPP_TEXT_DOMAIN) ?></label></div>
  913.                                         <div style="float:left;  padding:0 3px 3px 3px; background-color:#EEE; font-size:8pt;">
  914.                                                 <?php _e('Thumbnail', FPP_TEXT_DOMAIN) ?><br />
  915.                                                 <fieldset>
  916.                                                 <label><input name="fpp_options[show_thumbnail]" value="gravatar" type="radio" <?php checked('1', $options['show_thumbnail'] == 'gravatar'); ?>/> <span>Gravatar <a target="_blank" href="http://gravatar.com">?</a></span></label><br />
  917.                                                 <label><input name="fpp_options[show_thumbnail]" value="post" type="radio" <?php checked('1', $options['show_thumbnail'] == 'post'); ?> /> <span><?php _e('From post', FPP_TEXT_DOMAIN) ?></span></label><br />
  918.                                                 <label><input name="fpp_options[show_thumbnail]" value="default" type="radio" <?php checked('1', $options['show_thumbnail'] == 'default'); ?> /> <span><?php _e('Default', FPP_TEXT_DOMAIN) ?></span></label><br />
  919.                                                 <label><input name="fpp_options[show_thumbnail]" value="none" type="radio" <?php checked('1', $options['show_thumbnail'] == 'none'); ?> /> <span><?php _e('None', FPP_TEXT_DOMAIN) ?></span></label><br />
  920.                                                 </fieldset>
  921.                                         </div>
  922.                                         <div style="margin-left:95px">
  923.                                                 <div style="font-weight:bold; color:#3B5998"><?php _e('Post title linking to your post', FPP_TEXT_DOMAIN) ?></div>
  924.                                                 <div style="color:gray; font-size:8pt; line-height:8pt"><?php _e('Blog domain', FPP_TEXT_DOMAIN) ?></div>
  925.                                                 <div style="color:gray; font-size:8pt; line-height:20pt">
  926.                                                         <label><input id="fpp_options-show_post_author" type="checkbox" name="fpp_options[show_post_author]" value="1" <?php checked('1', $options['show_post_author']); ?> /> <?php _e('Post author', FPP_TEXT_DOMAIN) ?></label> |
  927.                                                         <label><input id="fpp_options-show_post_categories" type="checkbox" name="fpp_options[show_post_categories]" value="1" <?php checked('1', $options['show_post_categories']); ?> /> <?php _e('Post categories', FPP_TEXT_DOMAIN) ?></label>
  928.                                                 </div>
  929.                                         </div>
  930.                                         <div style="clear:left"></div>
  931.                                 </div>
  932.                         </div>
  933.                         <table class="form-table">
  934.                                 <tr valign="top">
  935.                                         <th scope="row"><?php _e('Default thumbnail', FPP_TEXT_DOMAIN) ?></th>
  936.                                         <td>
  937.                                         <input style="text-align:left" id="upload_image" type="text" size="36" name="fpp_options[default_thumbnail_url]" value="<?php echo htmlentities($options['default_thumbnail_url']); ?>" />
  938.                                         <input id="upload_image_button" type="button" value="Media gallery" /><br />
  939.                                         <span style="color:#999; font-size:7pt; line-height:9pt"><?php _e('Enter an URL or upload an image', FPP_TEXT_DOMAIN) ?></span>
  940.                                         </td>
  941.                                 </tr>
  942.                         </table>
  943.                         <p class="submit">
  944.                                 <input type="submit" class="button-primary" value="<?php _e('Save Changes') ?>" />
  945.                         </p>
  946.                 </form>
  947.         </div>
  948.         <?php
  949. }
  950.  
  951. /**
  952.  * Render Facebook recognized meta tags (Open Graph protocol).
  953.  * Facebooks uses them to refine shared links for example.
  954.  *
  955.  * @last_review 0.3.0
  956.  */
  957. function fpp_render_meta_tags($post) {
  958.         $options = get_option('fpp_options');
  959.  
  960.         echo '<meta property="og:type" content="article"/>'; // Required by FB
  961.         echo '<meta property="og:url" content="'.esc_attr(get_permalink($post->ID)).'"/>'; // Required by FB
  962.        
  963.         echo '<meta property="og:title" content="'.esc_attr(apply_filters('the_title', $post->post_title))/*, ENT_COMPAT, 'UTF-8')*/.'"/>';
  964.        
  965.         $description = array();
  966.         if ($options['show_post_author']) {
  967.                 $description[] = esc_attr(fpp_get_post_author($post));/*, ENT_COMPAT, 'UTF-8')*/
  968.         }
  969.         if ($options['show_post_categories']) {
  970.                 $categories = esc_attr(fpp_get_post_categories($post));/*, ENT_COMPAT, 'UTF-8')*/
  971.                 if (!empty($categories)) $description[] = $categories;
  972.         }
  973.         echo '<meta property="og:description" content="'.implode(' | ', $description).'"/>';
  974.        
  975.         if (($options['show_thumbnail'] == 'post') and empty($post->post_password)) {
  976.                 $image_url = fpp_get_post_image($post);
  977.         }
  978.         else if ($options['show_thumbnail'] == 'gravatar') {
  979.                 preg_match('/<img .*?src=["|\']([^"|\']+)/i', get_avatar($post->post_author), $matches);
  980.                 if (!empty($matches[1])) $image_url = $matches[1];
  981.         }
  982.         else if (($options['show_thumbnail'] == 'none') or empty($options['default_thumbnail_url'])) {
  983.                 if ($options['show_post_categories'] or $options['show_post_author']) {
  984.                         $image_url = FPP_BASE_URL.'line.png';
  985.                 } else {
  986.                         $image_url = FPP_BASE_URL.'line_small.png';
  987.                 }
  988.         }
  989.         if (!isset($image_url) or empty($image_url)) {
  990.                 if (!empty($options['default_thumbnail_url'])) {
  991.                         $image_url = $options['default_thumbnail_url'];
  992.                 } else {
  993.                         if ($options['show_post_categories'] or $options['show_post_author']) {
  994.                                 $image_url = FPP_BASE_URL.'line.png';
  995.                         } else {
  996.                                 $image_url = FPP_BASE_URL.'line_small.png';
  997.                         }
  998.                  }
  999.         }
  1000.         echo '<meta property="og:image" content="'.esc_attr($image_url)/*, ENT_COMPAT, 'UTF-8')*/.'"/>';
  1001. }
  1002.  
  1003. /**
  1004.  * Renders a 'publish to facebook' checkbox. Renders the box only if
  1005.  * the current post is a real post, not a page or something else.
  1006.  */
  1007. function fpp_render_post_button() {
  1008.         global $post;
  1009.        
  1010.         $object_access_token = get_option('fpp_object_access_token');
  1011.        
  1012.         if (!array_pop(get_post_meta($post->ID, '_fpp_is_published'))) {
  1013.                 echo '<label for="fpp_post_to_facebook"><img style="vertical-align:middle; margin:2px" src="'.FPP_BASE_URL.'publish_icon.png" alt="'.__('Publish to Facebook', FPP_TEXT_DOMAIN).'" /> '.__('Publish to Facebook', FPP_TEXT_DOMAIN).' </label><input '.(((FPP_DEFAULT_POST_TO_FACEBOOK or fpp_get_default_publishing($post)) and !empty($object_access_token)) ? 'checked="checked"' : '').' type="checkbox" value="1" id="fpp_post_to_facebook" name="fpp_post_to_facebook" '.(empty($object_access_token) ? 'disabled="disabled"' : '').' />';
  1014.         } else {
  1015.                 echo '<label for="fpp_post_to_facebook"><img style="vertical-align:middle; margin:2px" src="'.FPP_BASE_URL.'publish_icon.png" alt="'.__('Publish to Facebook', FPP_TEXT_DOMAIN).'" /> '.__('Post <em>again</em> to Facebook', FPP_TEXT_DOMAIN).' </label><input type="checkbox" value="1" id="fpp_post_to_facebook" name="fpp_post_to_facebook" '.(empty($object_access_token) ? 'disabled="disabled"' : '').' />';
  1016.         }
  1017.         if (empty($object_access_token)) {
  1018.                 echo '<div><em style="color:#aa6600">'.sprintf(__('Facebook Page Publish is not set up.<br />Please check your <a href="%s">plugin settings</a>.', FPP_TEXT_DOMAIN), 'options-general.php?page='.plugin_basename(__FILE__)).'</em></div>';
  1019.         }
  1020.         if ($post->post_status == "private") {
  1021.                 echo '<div><em style="color:#aa6600">'.__('Private posts are not published', FPP_TEXT_DOMAIN).'</em></div>';
  1022.         }
  1023.         echo '<input type="hidden" name="fpp_send_from_admin" value="1" />';
  1024.        
  1025.         $error = get_option('fpp_error');
  1026.         if (!empty($error)) {
  1027.                 echo '<div class="error"><strong>'.sprintf(__('Your Facebook Page Publish plugin reports an error. Please check your <a href="%s">plugin settings</a>.', FPP_TEXT_DOMAIN), 'options-general.php?page='.plugin_basename(__FILE__)).'</strong></div>';
  1028.         }
  1029. }
  1030.  
  1031. /**********************************************************************
  1032.  * Others
  1033.  **********************************************************************/
  1034. /**
  1035.  * @last_review 0.3.0
  1036.  */
  1037. function fpp_validate_options($input) {
  1038.         $options = get_option('fpp_options');
  1039.        
  1040.         // Customization options:
  1041.         $options['show_thumbnail'] = $input['show_thumbnail'];
  1042.         $options['show_post_author'] = array_key_exists('show_post_author', $input) && !empty($input['show_post_author']);
  1043.         $options['show_post_categories'] = array_key_exists('show_post_categories', $input) && !empty($input['show_post_categories']);
  1044.         $options['show_post_text'] = array_key_exists('show_post_text', $input) && !empty($input['show_post_text']);
  1045.         $options['show_post_link'] = array_key_exists('show_post_link', $input) && !empty($input['show_post_link']);
  1046.         $options['default_thumbnail_url'] = trim($input['default_thumbnail_url']);
  1047.        
  1048.         // Validate customization options:
  1049.         if (($options['show_thumbnail'] == 'default') and (substr($options['default_thumbnail_url'], 0, 4) !== 'http'))
  1050.                 add_settings_error('fpp_options', 'customization_error', __('The given default thumbnail URL is not valid. Any valid URL has to start with http:// or https://.', FPP_TEXT_DOMAIN).'</p><p><font style="font-weight:normal">'.__('Facebook won\'t be able to display the choosen default thumbnail.', FPP_TEXT_DOMAIN).'</font></p>');
  1051.        
  1052.         // Connection options:
  1053.         if ($options['app_id'] != $input['app_id']) {
  1054.                 update_option('fpp_profile_access_token', '');
  1055.                 update_option('fpp_object_access_token', '');
  1056.         } else if ($options['object_id'] != $input['object_id']) {
  1057.                 update_option('fpp_object_access_token', '');
  1058.         }
  1059.        
  1060.         $options['app_id'] = $input['app_id'];
  1061.         $options['object_id'] = $input['object_id'];
  1062.         $options['app_secret'] = $input['app_secret'];
  1063.         $options['app_id_valid'] = false;
  1064.         $options['object_id_valid'] = false;
  1065.         $options['object_type'] = '';
  1066.         $options['app_secret_valid'] = false;
  1067.         $options['ignore_ssl'] = array_key_exists('ignore_ssl', $input) && !empty($input['ignore_ssl']);
  1068.         $options['default_publishing'] = $input['default_publishing'];
  1069.         $options['default_publishing_categories'] = array_key_exists('default_publishing_categories', $input) ? $input['default_publishing_categories'] : array();
  1070.        
  1071.         // Validate connection options:
  1072.         try {
  1073.                 if (!empty($options['app_id']) or !empty($options['object_id'])) {
  1074.                         $object_classification = fpp_classify_facebook_objects(array($options['app_id'], $options['object_id']));
  1075.                         $options['app_id_valid'] = $object_classification[$options['app_id']] == 'application';
  1076.                         $options['object_type'] = $object_classification[$options['object_id']];
  1077.                         $options['object_id_valid'] = (($options['object_type'] == 'profile') or ($options['object_type'] == 'page') or ($options['object_type'] == 'group') or ($options['object_type'] == 'application'));
  1078.                 }
  1079.                 $options['app_secret_valid'] = (!empty($options['app_secret']) and $options['app_id_valid']) ? fpp_is_valid_facebook_application($options['app_id'], $options['app_secret'], FPP_ADMIN_URL) : false;
  1080.                
  1081.                 if (!empty($options['app_id']) and !$options['app_id_valid']) {
  1082.                         throw new FacebookErrorException(__('Invalid application ID.', FPP_TEXT_DOMAIN).' '.sprintf(__('Please refer to the <a target="_blank" href="%s">detailed setup instructions</a>.', FPP_TEXT_DOMAIN), FPP_BASE_URL.'setup.htm#app_id'));
  1083.                 }
  1084.                 if (!empty($options['object_id']) and !$options['object_id_valid'])  {
  1085.                         throw new FacebookErrorException(__('Invalid user or page ID.', FPP_TEXT_DOMAIN).' '.sprintf(__('Please refer to the <a target="_blank" href="%s">detailed setup instructions</a>.', FPP_TEXT_DOMAIN), FPP_BASE_URL.'setup.htm#object_id'));
  1086.                 }
  1087.                 if (!$options['app_secret_valid'] and $options['app_id_valid'])
  1088.                         throw new FacebookErrorException(__('Invalid application secret.', FPP_TEXT_DOMAIN).' '.sprintf(__('Please refer to the <a target="_blank" href="%s">detailed setup instructions</a>.', FPP_TEXT_DOMAIN), FPP_BASE_URL.'setup.htm#app_secret'));
  1089.                  
  1090.         } catch (CommunicationException $exception) {
  1091.                 add_settings_error('fpp_options', 'connection_error', $exception->getMessage().'<p><font style="font-weight:normal">'.__('Your connection options could not be validated.', FPP_TEXT_DOMAIN).'</font></p>');
  1092.         }
  1093.         return $options;
  1094. }
  1095.  
  1096. /**
  1097.  * Initializes the plugin options with either default or existing
  1098.  * values (in case there is already a version installed).
  1099.  */
  1100. function fpp_initialize_options() {
  1101.         // default options:
  1102.         $options = array(
  1103.                 'app_id' => '',
  1104.                 'app_id_valid' => false,
  1105.                 'app_secret' => '',
  1106.                 'app_secret_valid' => false,
  1107.                 'object_id' => '',
  1108.                 'object_id_valid' => false,
  1109.                 'object_type' => '',
  1110.                 'ignore_ssl' => false,
  1111.                 'default_publishing' => 'all',
  1112.                 'default_publishing_categories' => array(),
  1113.                 'default_thumbnail_url' => '',
  1114.                 'show_post_categories' => true,
  1115.                 'show_post_author' => true,
  1116.                 'show_post_text' => true,
  1117.                 'show_post_link' => true,
  1118.                 'show_thumbnail' => 'gravatar');
  1119.        
  1120.         $current_version = get_option('fpp_installed_version');
  1121.         $current_options = get_option('fpp_options');
  1122.        
  1123.         $object_access_token = get_option('fpp_object_access_token');
  1124.         $profile_access_token = get_option('fpp_profile_access_token');
  1125.        
  1126.         // Plugin previously installed:
  1127.         if (is_array($current_options)) {
  1128.                 foreach ($options as $key => $value) {
  1129.                         if (array_key_exists($key, $current_options)) {
  1130.                                 $options[$key] = $current_options[$key];
  1131.                         }
  1132.                 }
  1133.        
  1134.                 if (empty($current_version)) { // version <= 0.2.2
  1135.                         $options['app_id_valid'] = !empty($current_options['page_id']);
  1136.                         $options['app_secret_valid'] = !empty($current_options['page_id']);
  1137.                         $options['object_id'] = $current_options['page_id'];
  1138.                         $options['object_id_valid'] = !empty($current_options['page_id']);
  1139.                         $options['object_type'] = 'page';
  1140.                         $options['show_thumbnail'] = $current_options['show_gravatar'] ? 'gravatar' : 'post';
  1141.                        
  1142.                         $object_access_token = get_option('fpp_page_access_token');
  1143.                        
  1144.                         delete_option('fpp_page_access_token');
  1145.                         delete_option('fpp_post_to_facebook');
  1146.                 }
  1147.         }
  1148.         update_option('fpp_options', $options);
  1149.         update_option('fpp_object_access_token', $object_access_token);
  1150.         update_option('fpp_profile_access_token', $profile_access_token);
  1151.         update_option('fpp_error', '');
  1152.         update_option('fpp_installed_version', FPP_VERSION);
  1153. }
  1154. ?>
  1155.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement