| 1 | <?php |
| 2 | |
| 3 | if ( $_SERVER['SCRIPT_FILENAME'] == __FILE__ ) |
| 4 | die( 'Access denied.' ); |
| 5 | |
| 6 | if ( ! class_exists( 'TGGRSourceFacebook' ) ) { |
| 7 | /** |
| 8 | * Creates a custom post type and associated taxonomies |
| 9 | * @package Tagregator |
| 10 | */ |
| 11 | class TGGRSourceFacebook extends TGGRMediaSource { |
| 12 | protected static $readable_properties = array( 'view_folder' ); |
| 13 | protected static $writeable_properties = array(); |
| 14 | protected $setting_names, $default_settings, $view_folder; |
| 15 | |
| 16 | const POST_TYPE_NAME_SINGULAR = 'Facebook Activity'; |
| 17 | const POST_TYPE_NAME_PLURAL = 'Facebook Activities'; |
| 18 | const POST_TYPE_SLUG = 'tggr-facebook'; |
| 19 | const SETTINGS_TITLE = 'Facebook'; |
| 20 | const SETTINGS_PREFIX = 'tggr_facebook_'; |
| 21 | const API_URL = 'https://www.api.facebook.com'; // It's important to use HTTPS for security |
| 22 | |
| 23 | /** |
| 24 | * Constructor |
| 25 | * @mvc Controller |
| 26 | */ |
| 27 | protected function __construct() { |
| 28 | $this->view_folder = dirname( __DIR__ ) . '/views/'. str_replace( '.php', '', basename( __FILE__ ) ); |
| 29 | $this->setting_names = array( 'Access Token', 'Highlighted Accounts' ); |
| 30 | |
| 31 | foreach ( $this->setting_names as $key ) { |
| 32 | $this->default_settings[ strtolower( str_replace( ' ', '_', $key ) ) ] = ''; |
| 33 | } |
| 34 | $this->default_settings[ '_newest_activity_date' ] = 0; |
| 35 | |
| 36 | $this->register_hook_callbacks(); |
| 37 | } |
| 38 | |
| 39 | /** |
| 40 | * Prepares site to use the plugin during activation |
| 41 | * @mvc Controller |
| 42 | * |
| 43 | * @param bool $network_wide |
| 44 | */ |
| 45 | public function activate( $network_wide ) { |
| 46 | $this->init(); |
| 47 | } |
| 48 | |
| 49 | /** |
| 50 | * Rolls back activation procedures when de-activating the plugin |
| 51 | * @mvc Controller |
| 52 | */ |
| 53 | public function deactivate() {} |
| 54 | |
| 55 | /** |
| 56 | * Register callbacks for actions and filters |
| 57 | * @mvc Controller |
| 58 | */ |
| 59 | public function register_hook_callbacks() { |
| 60 | add_action( 'init', array( $this, 'init' ) ); |
| 61 | add_action( 'admin_init', array( $this, 'register_settings' ) ); |
| 62 | add_filter( 'excerpt_length', __CLASS__ . '::get_excerpt_length' ); |
| 63 | |
| 64 | add_filter( Tagregator::PREFIX . 'default_settings', __CLASS__ . '::register_default_settings' ); |
| 65 | } |
| 66 | |
| 67 | /** |
| 68 | * Initializes variables |
| 69 | * @mvc Controller |
| 70 | */ |
| 71 | public function init() { |
| 72 | self::register_post_type( |
| 73 | self::POST_TYPE_SLUG, |
| 74 | $this->get_post_type_params( |
| 75 | self::POST_TYPE_SLUG, |
| 76 | self::POST_TYPE_NAME_SINGULAR, |
| 77 | self::POST_TYPE_NAME_PLURAL |
| 78 | ) |
| 79 | ); |
| 80 | self::create_post_author(); // It should already exist from the first time this class was instantiated, but we need to make sure it still exists now |
| 81 | self::get_post_author_user_id(); |
| 82 | } |
| 83 | |
| 84 | /** |
| 85 | * Executes the logic of upgrading from specific older versions of the plugin to the current version |
| 86 | * @mvc Model |
| 87 | * |
| 88 | * @param string $db_version |
| 89 | */ |
| 90 | public function upgrade( $db_version = 0 ) {} |
| 91 | |
| 92 | /** |
| 93 | * Validates submitted setting values before they get saved to the database. |
| 94 | * Invalid data will be overwritten with defaults. |
| 95 | * @mvc Model |
| 96 | * |
| 97 | * @param array $new_settings |
| 98 | * @return array |
| 99 | */ |
| 100 | public function validate_settings( $new_settings ) { |
| 101 | $new_settings = shortcode_atts( $this->default_settings, $new_settings, TGGRSettings::SETTING_SLUG ); |
| 102 | |
| 103 | foreach ( $new_settings as $setting => $value ) { |
| 104 | switch ( $setting ) { |
| 105 | case '_newest_activity_date': |
| 106 | $new_settings[ $setting ] = absint( $value ); |
| 107 | break; |
| 108 | default: |
| 109 | $new_settings[ $setting ] = $this->default_settings[ $setting ]; |
| 110 | if ( is_string( $value ) ) { |
| 111 | $new_settings[ $setting ] = sanitize_text_field( $value ); |
| 112 | } |
| 113 | break; |
| 114 | } |
| 115 | } |
| 116 | |
| 117 | return $new_settings; |
| 118 | } |
| 119 | |
| 120 | /** |
| 121 | * Fetches new items from an external sources and saves them as posts in the local database |
| 122 | * @mvc Controller |
| 123 | * |
| 124 | * @param string $hashtag |
| 125 | */ |
| 126 | public function import_new_items( $hashtag ) { |
| 127 | $activities = self::get_new_activities( |
| 128 | TGGRSettings::get_instance()->settings[ __CLASS__ ]['api_key'], |
| 129 | $hashtag, |
| 130 | TGGRSettings::get_instance()->settings[ __CLASS__ ]['_newest_activity_date'] |
| 131 | ); |
| 132 | |
| 133 | $this->import_new_posts( $this->convert_items_to_posts( $activities, $hashtag ) ); |
| 134 | self::update_newest_activity_date( $hashtag ); |
| 135 | } |
| 136 | |
| 137 | /** |
| 138 | * Retrieves activities containing the given hashtag that were posted since the last import |
| 139 | * @mvc Model |
| 140 | * |
| 141 | * @param string $api_key |
| 142 | * @param string $hashtag |
| 143 | * @param string $last_updated_activities The timestamp of the most recent item that is already saved in the database |
| 144 | * @return mixed string|false |
| 145 | */ |
| 146 | protected static function get_new_activities( $api_key, $hashtag, $last_updated_activities ) { |
| 147 | $response = $activities = false; |
| 148 | |
| 149 | if ( $api_key && $hashtag ) { |
| 150 | $url = sprintf( |
| 151 | '%s/v1/activities?query=%s&key=%s', |
| 152 | self::API_URL, |
| 153 | urlencode( $hashtag ), |
| 154 | urlencode( $api_key ) |
| 155 | ); |
| 156 | |
| 157 | $response = wp_remote_get( $url ); |
| 158 | $body = json_decode( wp_remote_retrieve_body( $response ) ); |
| 159 | |
| 160 | if ( isset( $body->updated ) && strtotime( $body->updated ) > $last_updated_activities && ! empty( $body->items ) ) { |
| 161 | $activities = $body->items; |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | self::log( __METHOD__, 'Results', compact( 'api_key', 'hashtag', 'last_updated_activities', 'response' ) ); |
| 166 | |
| 167 | return $activities; |
| 168 | } |
| 169 | |
| 170 | /** |
| 171 | * Converts data from external source into a post/postmeta format so it can be saved in the local database |
| 172 | * @mvc Model |
| 173 | * |
| 174 | * @param array $items |
| 175 | * @param string $term |
| 176 | * @return array |
| 177 | */ |
| 178 | public function convert_items_to_posts( $items, $term ) { |
| 179 | $posts = array(); |
| 180 | |
| 181 | if ( $items ) { |
| 182 | foreach ( $items as $item ) { |
| 183 | $post_timestamp_gmt = strtotime( $item->published ); |
| 184 | $post_timestamp_local = self::convert_gmt_timestamp_to_local( $post_timestamp_gmt ); |
| 185 | |
| 186 | $post = array( |
| 187 | 'post_author' => TGGRMediaSource::$post_author_id, |
| 188 | 'post_content' => wp_kses( $item->object->content, wp_kses_allowed_html( 'data' ), array( 'http', 'https', 'mailto' ) ), |
| 189 | 'post_date' => date( 'Y-m-d H:i:s', $post_timestamp_local ), |
| 190 | 'post_date_gmt' => date( 'Y-m-d H:i:s', $post_timestamp_gmt ), |
| 191 | 'post_status' => 'publish', |
| 192 | 'post_title' => sanitize_text_field( $item->title ), |
| 193 | 'post_type' => self::POST_TYPE_SLUG, |
| 194 | ); |
| 195 | |
| 196 | $post_meta = array( |
| 197 | 'source_id' => sanitize_text_field( $item->id ), |
| 198 | 'post_permalink' => esc_url_raw( $item->url ), |
| 199 | 'author_id' => sanitize_text_field( $item->actor->id ), |
| 200 | 'author_name' => sanitize_text_field( $item->actor->displayName ), |
| 201 | 'author_url' => esc_url( $item->actor->url ), |
| 202 | 'author_image_url' => esc_url( $item->actor->image->url ), |
| 203 | 'media' => array( |
| 204 | array( |
| 205 | 'small_url' => isset( $item->object->attachments[0]->image ) ? esc_url_raw( $item->object->attachments[0]->image->url ) : false, |
| 206 | 'large_url' => isset( $item->object->attachments[0]->fullImage ) ? esc_url_raw( $item->object->attachments[0]->fullImage->url ) : false, |
| 207 | 'type' => 'image', |
| 208 | ), |
| 209 | ), |
| 210 | ); |
| 211 | |
| 212 | $posts[] = array( |
| 213 | 'post' => $post, |
| 214 | 'post_meta' => $post_meta, |
| 215 | 'term_name' => $term, |
| 216 | ); |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | return $posts; |
| 221 | } |
| 222 | |
| 223 | /** |
| 224 | * Updates the _newest_activity_date setting with the timestamp of the most recent |
| 225 | * @mvc Model |
| 226 | * |
| 227 | * @param string $hashtag |
| 228 | */ |
| 229 | protected static function update_newest_activity_date( $hashtag ) { |
| 230 | $latest_post = self::get_latest_hashtagged_post( self::POST_TYPE_SLUG, $hashtag ); |
| 231 | |
| 232 | if ( isset( $latest_post->ID ) ) { |
| 233 | $settings = TGGRSettings::get_instance()->settings; |
| 234 | $settings[ __CLASS__ ]['_newest_activity_date'] = strtotime( $latest_post->post_date_gmt . ' GMT' ); |
| 235 | TGGRSettings::get_instance()->settings = $settings; |
| 236 | } |
| 237 | } |
| 238 | |
| 239 | /** |
| 240 | * Gathers the data that the media-item view will need |
| 241 | * @mvc Model |
| 242 | * |
| 243 | * @param WP_Post $post |
| 244 | * |
| 245 | * @return array |
| 246 | */ |
| 247 | public function get_item_view_data( $post ) { |
| 248 | $postmeta = get_post_custom( $post->ID ); |
| 249 | $necessary_data = array( |
| 250 | 'source_id' => $postmeta['source_id'][0], |
| 251 | 'post_permalink' => $postmeta['post_permalink'][0], |
| 252 | 'author_name' => $postmeta['author_name'][0], |
| 253 | 'author_url' => $postmeta['author_url'][0], |
| 254 | 'author_image_url' => $postmeta['author_image_url'][0], |
| 255 | 'media' => isset( $postmeta['media'][0] ) ? maybe_unserialize( $postmeta['media'][0] ) : array(), |
| 256 | 'logo_url' => plugins_url( 'images/source-logos/facebook.png', __DIR__ ), |
| 257 | 'css_classes' => self::get_css_classes( $post->ID, $postmeta['author_name'][0] ), |
| 258 | 'show_excerpt' => self::show_excerpt( $post ), |
| 259 | ); |
| 260 | |
| 261 | return $necessary_data; |
| 262 | } |
| 263 | } // end TGGRSourceFacebook |
| 264 | } |