diff --git a/class/FBAutopost.php b/class/FBAutopost.php index cdf25f4..1aa702e 100644 --- a/class/FBAutopost.php +++ b/class/FBAutopost.php @@ -6,7 +6,7 @@ */ _load_facebook_sdk(); - +use Facebook\Facebook; /** * API class to handle common actions when autoposting * This class uses FBAutopostException for error handling. Severity is @@ -124,12 +124,15 @@ class FBAutopost extends Facebook { */ private $retry; - function __construct($conf = array()) { - if (!isset($conf['appId'])) { - $conf['appId'] = variable_get('fb_autopost_app_id', ''); + function __construct($conf = array()) { + if (!isset($conf['app_id'])) { + $conf['app_id'] = variable_get('fb_autopost_app_id', ''); + } + if (!isset($conf['app_secret'])) { + $conf['app_secret'] = variable_get('fb_autopost_app_secret', ''); } - if (!isset($conf['secret'])) { - $conf['secret'] = variable_get('fb_autopost_app_secret', ''); + if (!isset($conf['default_graph_version'])) { + $conf['default_graph_version'] = 'v2.5'; // TODO make graph version a variable } $this->destination = NULL; $this->retry = TRUE; @@ -190,11 +193,11 @@ class FBAutopost extends Facebook { // location that handles the publishing and redirects back to the // original location $publication['type'] = $this->getType(); - $session->storePublication($publication); - $login_url = $this->getLoginUrl(array( - 'scope' => fb_permissions_get_facebook_permissions(), - 'redirect_uri' => url('fbautopost/authorization/retry', array('absolute' => TRUE)), - )); + $session->storePublication($publication); + $login_url = $this->getRedirectLoginHelper()->getLoginUrl( + url('fbautopost/authorization/retry', array('absolute' => TRUE)), + fb_permissions_get_facebook_permissions() + ); // Redirect the user token the login URL that will redirect back to // the retry URI. // Remove the destination parameter, this is bad for redirections @@ -228,18 +231,18 @@ class FBAutopost extends Facebook { $this->publishParameterPrepare($publication); // Call api method from ancestor. - $result = $this->api( + $result = $this->post( // Post to destination on the selected endpoint. '/' . $this->getDestination() . '/' . $this->getEndpoint(), - // This is fixed. - 'POST', - // Add access token to the params. - $publication['params'] + // Add params. + $publication['params'], + // Add access token. + $publication['params']['access_token'] ); module_invoke_all('fb_autopost_publication_publish', $publication, $result); - return $result; + return $result->getDecodedBody(); } /** @@ -253,10 +256,12 @@ class FBAutopost extends Facebook { $publication['params'] += array('access_token' => $pages[$destination]['access_token']); } else { + $publication['params'] += array('access_token' => variable_get('fb_autopost_token', '')); // Privacy only makes sense when publishing to timeline if ($p = $this->getPrivacy()) { - $publication['params'] += array('privacy' => $p); + $publication['params'] += array('privacy' => $p); } + } } @@ -300,9 +305,7 @@ class FBAutopost extends Facebook { throw new FBAutopostException(t('Cannot ask for user accounts without an access token.'), FBAutopost::missing_param, WATCHDOG_ERROR); } try { - return $this->api('/' . $account_id . '/accounts', 'GET', array( - 'limit' => 999, - )); + return $this->get('/' . $account_id . '/accounts', $account_access_token)->getDecodedBody(); } catch (FacebookApiException $e) { // Get the FacebookApiException and throw an ordinary @@ -337,12 +340,14 @@ class FBAutopost extends Facebook { * Deletes a publication from Facebook * * @param $publication_id - * The id that identifies the publication in Facebook + * The id of the publication in Facebook + * * @param $access_token + * An access token that is allowed to delete the publication * @throws FacebookApiException */ - public function remoteDelete($publication_id) { - // TODO: Add a remoetDelete callback to the entity delete (optional) - $this->api('/' . $publication_id, 'DELETE'); + public function remoteDelete($publication_id, $access_token) { + // TODO: Add a remoteDelete callback to the entity delete (optional) + $this->delete('/' . $publication_id, array(), $access_token); // If request fails then an exception is thrown and there is no return value return TRUE; } diff --git a/class/FBAutopostPost.php b/class/FBAutopostPost.php index 7a9c3fd..092a006 100644 --- a/class/FBAutopostPost.php +++ b/class/FBAutopostPost.php @@ -15,6 +15,7 @@ class FBAutopostPost extends FBAutopost { */ protected function publishParameterPrepare(&$publication) { parent::publishParameterPrepare($publication); + /* actions are deprecated since July 2015 // It is mandatory to have action links for posts. Provide them if empty. $name = t('Visit'); $link = $publication['params']['link'] ?: $GLOBALS['base_url']; @@ -27,5 +28,6 @@ class FBAutopostPost extends FBAutopost { 'name' => $name, 'link' => $link, )); + */ } } diff --git a/fb_autopost.admin.inc b/fb_autopost.admin.inc index e575147..ee4f300 100644 --- a/fb_autopost.admin.inc +++ b/fb_autopost.admin.inc @@ -45,8 +45,8 @@ function fb_autopost_global_settings($form, &$form_state) { try { // Create the FBAutpost object with the stored data. - $fb = facebook_autopost(); - if ($fb_user = $fb->getUser()) { + $fb = facebook_autopost(); + if (variable_get('fb_autopost_token', '')) { // If there is a connected account, get all FB pages related to that // account. Make these pages the values of a select. try { @@ -85,11 +85,10 @@ function fb_autopost_global_settings($form, &$form_state) { '#prefix' => '

' . t('You need to connect your Facebook account to fetch the pages you are allowed to post to.') . '

', '#type' => 'link', '#title' => t('Login to Facebook'), - '#href' => $fb->getLoginUrl(array( - 'scope' => fb_permissions_get_facebook_permissions(array('manage_pages', 'publish_actions')), - 'redirect_uri' => url('admin/config/services/fbautopost/login', array('absolute' => TRUE)), - )), - ); + '#href' => $fb->getRedirectLoginHelper()->getLoginUrl( + url('admin/config/services/fbautopost/login', array('absolute' => TRUE)), + fb_permissions_get_facebook_permissions(array('manage_pages', 'publish_actions'))), + ); } } catch (Exception $e) { diff --git a/fb_autopost.drush.inc b/fb_autopost.drush.inc index db72569..d7152c1 100644 --- a/fb_autopost.drush.inc +++ b/fb_autopost.drush.inc @@ -8,8 +8,8 @@ /** * The Facebook PHP SDK URI. */ -define('FBAUTOPOST_DOWNLOAD_URI', 'https://github.com/facebook/facebook-php-sdk/archive/master.zip'); -define('FBAUTOPOST_DIRNAME', 'facebook-php-sdk'); +define('FBAUTOPOST_DOWNLOAD_URI', 'https://github.com/facebook/facebook-php-sdk-v4/archive/master.zip'); +define('FBAUTOPOST_DIRNAME', 'facebook-php-sdk-v4'); /** * Implements hook_drush_command(). diff --git a/fb_autopost.info b/fb_autopost.info index c8abcf4..972e0ca 100644 --- a/fb_autopost.info +++ b/fb_autopost.info @@ -3,7 +3,7 @@ description = Allows auto-posting to Facebook Fan Pages when publishing a node package = Facebook Autopost core = 7.x configure = admin/config/services/fbautopost -php = 5.3 +php = 5.4 dependencies[] = fb_permissions dependencies[] = libraries @@ -15,3 +15,4 @@ files[] = class/FBAutopostPhoto.php files[] = class/FBAutopostEvent.php files[] = class/FBAutopostPost.php files[] = class/FBSession.php + diff --git a/fb_autopost.install b/fb_autopost.install index 7666b77..5cc2847 100644 --- a/fb_autopost.install +++ b/fb_autopost.install @@ -23,11 +23,15 @@ function fb_autopost_requirements($phase) { // Check for the presence of facebook-php-sdk library // given that we are in the runtime phase we can assume that // drupal has been fully loaded and we can use libraries helpers. - $machine = 'facebook-php-sdk'; + // Check for both versions, use the newer one if found + $machine = (libraries_get_path('facebook-php-sdk-v4') ? 'facebook-php-sdk-v4' : 'facebook-php-sdk'); + $requirements[$machine] = array( 'title' => $t('Facebook PHP SDK'), ); $lib_path = libraries_get_path($machine); + + /* Skip looking for the v3 SDK if (file_exists($lib_path . '/src/facebook.php')) { // Inform that the library has been found and what is the version of it // If there is no such file or we cannot find the version. @@ -41,9 +45,8 @@ function fb_autopost_requirements($phase) { else { require_once $lib_path . '/src/base_facebook.php'; // The file has been found, now check the version of it. - $matches = array(); - // Facebook PHP SDK contains a constant in base_facebook.php with the - // version. + // Facebook PHP SDK contains a constant in base_facebook.php with + // the version. $version = BaseFacebook::VERSION; if (!empty($version)) { // Set the requirement OK. @@ -58,11 +61,35 @@ function fb_autopost_requirements($phase) { } } } + */ + + if (file_exists($lib_path . '/src/Facebook/autoload.php')) { + require_once $lib_path . '/src/Facebook/autoload.php'; + // The file has been found, now check the version of it. + // Facebook PHP SDK V5 contains a constant in Facebook\Facebook class + // with the version. + $version = Facebook\Facebook::VERSION; + if (!empty($version)) { + // Set the requirement OK. + $requirements[$machine] += array( + 'value' => $version.' - Support for this version is experimental', + 'severity' => REQUIREMENT_INFO, + ); + } + else { + // If there is no information about the version return a warning. + $warning_data = array( + 'description' => $t('The Facebook PHP SDK library has been found but we could not verify the version.'), + 'severity' => REQUIREMENT_WARNING, + ); + $requirements += $warning_data; + } + } else { // Set a requirement error. $requirements[$machine] += array( 'severity' => REQUIREMENT_ERROR, - 'description' => $t('The Facebook PHP SDK library has not been installed. Please clone the git repository or download the library in the common library paths from !link.', array('!link' => l($t('here'), 'https://github.com/facebook/facebook-php-sdk'))), + 'description' => $t('The Facebook PHP SDK library has not been installed. Please clone the git repository or download the library in the common library paths from !link.', array('!link' => l($t('here'), 'https://github.com/facebook/facebook-php-sdk-v4'))), ); } diff --git a/fb_autopost.module b/fb_autopost.module index 7ca032a..924374b 100644 --- a/fb_autopost.module +++ b/fb_autopost.module @@ -101,12 +101,12 @@ function fb_autopost_facebook_login() { drupal_set_message(t('An error happened while connecting with Facebook. Reason: %reason. Description: %description', array('%reason' => $_GET['error_reason'], '%description' => $_GET['error_description'])), 'error'); } else { - try { + try { $fb = facebook_autopost(); - if ($fb_user = $fb->getUser()) { - $token = $fb->getAccessToken(); - variable_set('fb_autopost_token', $token); - $user_profile = $fb->api('/me?fields=id'); + $token = $fb->getRedirectLoginHelper()->getAccessToken(); + if ($token != null) { + variable_set('fb_autopost_token', $token->getValue()); + $user_profile = $fb->get('/me?fields=id', $token->getValue())->getDecodedBody(); variable_set('fb_autopost_account_id', $user_profile['id']); } } @@ -125,7 +125,7 @@ function fb_autopost_facebook_logout() { try { $fb = facebook_autopost(); // Invalidate access_token - $fb->api('/' . variable_get('fb_autopost_account_id', 'me') . '/permissions', 'DELETE'); + $fb->delete('/' . variable_get('fb_autopost_account_id', 'me') . '/permissions', array(), variable_get('fb_autopost_token', '') ); } catch (FacebookApiException $e) { // Do nothing with the exception. @@ -135,6 +135,7 @@ function fb_autopost_facebook_logout() { // Delete stored variables. variable_del('fb_autopost_account_id'); variable_del('fb_autopost_page'); + variable_del('fb_autopost_token', ''); drupal_goto('admin/config/services/fbautopost'); } @@ -199,8 +200,18 @@ function fb_autopost_theme($existing, $type, $theme, $path) { * @throws Exception */ function _load_facebook_sdk($basename = NULL) { - $lib_path = libraries_get_path('facebook-php-sdk', $basename); - if (!file_exists($lib_path . '/src/facebook.php')) { + + // Look for Facebook PHP SDK 4.0+ + if (file_exists(libraries_get_path('facebook-php-sdk-v4', $basename) . '/src/Facebook/autoload.php')) { + require_once libraries_get_path('facebook-php-sdk-v4', $basename) . '/src/Facebook/autoload.php'; + } + /* + // Look for Facebook PHP SDK 3.0 + elseif (file_exists(libraries_get_path('facebook-php-sdk', $basename) . '/src/facebook.php')){ + require_once libraries_get_path('facebook-php-sdk', $basename) . '/src/facebook.php'; + } + */ + else { // In case we cannot find the file display an error and redirect to the // report status page (if the user can access it). if (user_access('view site reports')) { @@ -210,16 +221,13 @@ function _load_facebook_sdk($basename = NULL) { ) ))), 'error'); drupal_goto('admin/reports/status'); - } - else { + } + else { // The user cannot see the site reports with further instructions // throw an Exception. throw new FBAutopostException(t('Facebook Autopost could not be started because the Facebook PHP library is missing. Please contact system administration to communicate this error.')); } } - else { - require_once $lib_path . '/src/facebook.php'; - } } /** diff --git a/fb_autopost.theme.inc b/fb_autopost.theme.inc index 46f00e0..2a7740a 100644 --- a/fb_autopost.theme.inc +++ b/fb_autopost.theme.inc @@ -58,13 +58,14 @@ function theme_fb_autopost_facebook_profile($variables) { function template_preprocess_fb_autopost_facebook_profile(&$variables) { try { $fb = facebook_autopost(); - $fbuser_profile = $fb->api('/' . $variables['fbprofile']['#fbprofile_id'] . '?fields=picture,id,name,link'); + $fbuser_profile = $fb->get('/' . $variables['fbprofile']['#fbprofile_id'] . '?fields=picture,id,name,link',variable_get('fb_autopost_token', ''))->getDecodedBody(); $variables['fbprofile']['#fbprofile_name'] = empty($fbuser_profile['name']) ? $variables['fbprofile']['#fbprofile_id'] : $fbuser_profile['name']; $variables['fbprofile']['#fbprofile_link'] = empty($fbuser_profile['link']) ? 'http://facebook.com/' . $variables['fbprofile']['#fbprofile_id'] : $fbuser_profile['link']; $variables['fbprofile']['#fbprofile_picture'] = $fbuser_profile['picture']; - $variables['fbprofile']['#fbprofile_logout_url'] = $fb->getLogoutUrl(array( - 'next' => url('admin/config/services/fbautopost/logout', array('absolute' => TRUE)), - )); + $variables['fbprofile']['#fbprofile_logout_url'] = $fb->getRedirectLoginHelper()->getLogoutUrl( + variable_get('fb_autopost_token', ''), + url('admin/config/services/fbautopost/logout', array('absolute' => TRUE)) + ); } catch (FacebookApiException $e) { // Do nothing with the exception. diff --git a/fb_autopost_entity/class/FBAutopostEntity.php b/fb_autopost_entity/class/FBAutopostEntity.php index ee540cd..d4536c8 100644 --- a/fb_autopost_entity/class/FBAutopostEntity.php +++ b/fb_autopost_entity/class/FBAutopostEntity.php @@ -48,17 +48,32 @@ class FBAutopostEntity extends FBAutopost { * @throws FBAutopostException * @see FBAutopost::remoteDelete() */ - public function remoteEntityDelete(FacebookPublicationEntity $publication) { - // Get a wrapper for the entity and extract the remote ID. - $wrapper = entity_metadata_wrapper('facebook_publication', $publication); + public function remoteEntityDelete(FacebookPublicationEntity $publication_entity) { + // Get a wrapper for the entity and extract the remote ID + $wrapper = entity_metadata_wrapper('facebook_publication', $publication_entity); $remote_id = $wrapper->facebook_id->value(); - // If there is a remote ID in return, then delete the associated - // publication. - if (!empty($remote_id)) { - return parent::remoteDelete($remote_id); + + // Get a publication Array + $publication = array( + 'type' => $publication_entity->type, + 'params' => fb_autopost_entity_get_properties($publication_entity), + ); + // Call prepare because we need the access token + $this->publishParameterPrepare($publication); + $token = (array_key_exists('access_token', $publication['params'])) ? $publication['params']['access_token'] : ''; + + // If there is a remote ID and an access token, then delete the + // associated publication. + if (!empty($remote_id) && !empty($token)) { + return parent::remoteDelete($remote_id, $token); } else { - throw new FBAutopostException(t('Remote ID could not be found.'), FBAutopost::missing_param, WATCHDOG_ERROR); + if (empty($remote_id)) { + throw new FBAutopostException(t('Remote ID could not be found.'), FBAutopost::missing_param, WATCHDOG_ERROR); + } + if (empty($token)) { + throw new FBAutopostException(t('Access token could not be found.'), FBAutopost::missing_param, WATCHDOG_ERROR); + } } } diff --git a/fb_autopost_entity/class/FBAutopostEntityEvent.php b/fb_autopost_entity/class/FBAutopostEntityEvent.php index 2bb2f66..1736922 100644 --- a/fb_autopost_entity/class/FBAutopostEntityEvent.php +++ b/fb_autopost_entity/class/FBAutopostEntityEvent.php @@ -26,6 +26,8 @@ class FBAutopostEntityEvent extends FBAutopostEntity { } /** + * Events can no longer be created/updated via API (09.2015) + * * Edits a publication in facebook from a stored entity. Events in Facebook * can actually be updated, this means that there is no deletion needed. * @@ -34,7 +36,7 @@ class FBAutopostEntityEvent extends FBAutopostEntity { * * @throws FBAutopostException * @see FBAutopost::remoteEdit() - */ + public function remoteEntityEdit(FacebookPublicationEntity $publication_entity) { // For an event we should update the Entity, instead of deleting an // recreating it. @@ -56,5 +58,5 @@ class FBAutopostEntityEvent extends FBAutopostEntity { // Add access token to the params. $publication['params'] ); - } + }*/ } diff --git a/fb_autopost_entity/class/FBAutopostEntityPost.php b/fb_autopost_entity/class/FBAutopostEntityPost.php index 6503126..9fa2c1b 100644 --- a/fb_autopost_entity/class/FBAutopostEntityPost.php +++ b/fb_autopost_entity/class/FBAutopostEntityPost.php @@ -15,6 +15,7 @@ class FBAutopostEntityPost extends FBAutopostEntity { */ protected function publishParameterPrepare(&$publication) { parent::publishParameterPrepare($publication); + /* actions are deprecated since July 2015 // It is mandatory to have action links for posts. Provide them if empty. $name = t('Visit'); $link = empty($publication['params']['link']) ? $GLOBALS['base_url'] : $publication['params']['link']; @@ -23,9 +24,37 @@ class FBAutopostEntityPost extends FBAutopostEntity { if (!empty($publication['params']['actions'])) { list($name, $link) = explode('|', $publication['params']['actions']); } - $publication['params']['actions'] = array(array( + $publication['params']['actions'] = array( 'name' => $name, 'link' => $link, - )); + );*/ + } + + /** + * Edits a publication in facebook from a stored entity. Posts in Facebook + * can actually be updated, this means that there is no deletion needed. + * + * @param FacebookPublicationEntity $publication + * The fully loaded Facebook publication entity + * + * @throws FBAutopostException + * @see FBAutopost::remoteEdit() + */ + public function remoteEntityEdit(FacebookPublicationEntity $publication_entity) { + // For a post we should update the Entity, instead of deleting an + // recreating it. + $wrapper = entity_metadata_wrapper('facebook_publication', $publication_entity); + $remote_id = $wrapper->facebook_id->value(); + $publication = array( + 'type' => $publication_entity->type, + 'params' => fb_autopost_entity_get_properties($publication_entity), + ); + $this->publishParameterPrepare($publication); + + // Call api method from ancestor. + $result = $this->post($remote_id, $publication['params'],$publication['params']['access_token']); + // return the same id since we updated it + return array('id' => $remote_id); } } + diff --git a/fb_autopost_entity/fb_autopost_entity.info b/fb_autopost_entity/fb_autopost_entity.info index 1a29c94..03633c5 100644 --- a/fb_autopost_entity/fb_autopost_entity.info +++ b/fb_autopost_entity/fb_autopost_entity.info @@ -12,3 +12,4 @@ files[] = class/FBAutopostEntity.php files[] = class/FBAutopostEntityPhoto.php files[] = class/FBAutopostEntityEvent.php files[] = class/FBAutopostEntityPost.php + diff --git a/fb_autopost_entity/fb_autopost_entity.rules.inc b/fb_autopost_entity/fb_autopost_entity.rules.inc index d8287ee..ed398fb 100644 --- a/fb_autopost_entity/fb_autopost_entity.rules.inc +++ b/fb_autopost_entity/fb_autopost_entity.rules.inc @@ -60,6 +60,13 @@ function fb_autopost_entity_rules_action_info() { 'label' => t('Target Facebook publication entity'), 'description' => t('The ID returned from Facebook when creating the publication. Typically stored in the facebook_id property of your FacebookPublicationEntity.'), ), + 'destination' => array( + 'type' => 'text', + 'label' => t('Facebook destinations'), + 'options list' => 'available_facebook_destinations', + 'access callback' => 'can_administer_facebook_publications', + 'description' => t('The destination must match the original destination the publication is published on.'), + ), ), 'base' => 'rules_action_delete_from_facebook', ); @@ -148,7 +155,7 @@ function fb_autopost_entity_available_facebook_pages() { $output[$option] = filter_xss($label); } } - return $output; + return $output; } catch (Exception $e) { watchdog_exception('fb_autopost', $e); @@ -160,9 +167,8 @@ function fb_autopost_entity_available_facebook_pages() { * Returns the available destinations Pages + 'me'. */ function available_facebook_destinations() { - return array_merge(array( - 'me' => '- ' . t("Facebook logged in user's timeline"), - ), fb_autopost_entity_available_facebook_pages()); + // array_merge loses the ids so we just use + + return array('me' => t("Facebook logged in user's timeline")) + fb_autopost_entity_available_facebook_pages(); } /** @@ -201,9 +207,14 @@ function rules_action_publish_to_facebook_timeline(FacebookPublicationEntity $pu /** * Deletes a publication from Facebook. */ -function rules_action_delete_from_facebook(FacebookPublicationEntity $publication) { +function rules_action_delete_from_facebook(FacebookPublicationEntity $publication, $destination) { try { $fb = facebook_autopost_entity($publication->type); + if ($destination == 'me') { + $fb + ->setRetry(true); + } + $fb->setDestination($destination); $fb->remoteEntityDelete($publication); } catch (Exception $e) { @@ -215,7 +226,7 @@ function rules_action_delete_from_facebook(FacebookPublicationEntity $publicatio /** * Edits a publication in Facebook. */ -function rules_action_edit_in_facebook(FacebookPublicationEntity $publication, $privacy, $retry, $destination) { +function rules_action_edit_in_facebook(FacebookPublicationEntity $publication, $privacy, $retry, $destination, $destiny) { try { $fb = facebook_autopost_entity($publication->type); if ($destination == 'me') { diff --git a/fb_autopost_types/fb_autopost_types.info b/fb_autopost_types/fb_autopost_types.info index d50669d..043198a 100644 --- a/fb_autopost_types/fb_autopost_types.info +++ b/fb_autopost_types/fb_autopost_types.info @@ -122,3 +122,5 @@ features_exclude[field][facebook_publication-question-field_facebook_options] = features_exclude[field][facebook_publication-question-field_facebook_published] = facebook_publication-question-field_facebook_published features_exclude[field][facebook_publication-question-field_facebook_question] = facebook_publication-question-field_facebook_question features_exclude[field][facebook_publication-question-field_facebook_scheduled_publish] = facebook_publication-question-field_facebook_scheduled_publish + + diff --git a/fb_permissions/fb_permissions.module b/fb_permissions/fb_permissions.module index 2e1e1e3..255ea39 100644 --- a/fb_permissions/fb_permissions.module +++ b/fb_permissions/fb_permissions.module @@ -165,8 +165,8 @@ function replace_dot_vertical_bar($input) { * @param int $role_id * Different roles may yield different permissions. * - * @return string - * An imploded string containing the permissions. + * @return array + * An array containing the permissions. */ function fb_permissions_get_facebook_permissions($defaults = array(), $role_id = NULL) { $permissions = $defaults; @@ -179,7 +179,7 @@ function fb_permissions_get_facebook_permissions($defaults = array(), $role_id = else { $permissions = array_unique(array_merge(variable_get('fb_permissions_facebook_permissions:' . $role_id, array()), $permissions)); } - return implode(',', $permissions); + return $permissions; } /**