From c705a6c0807c782b71cccd5f71cdb796a3c9c98e Mon Sep 17 00:00:00 2001 From: Aldo Latino Date: Mon, 1 Jun 2020 12:10:27 +0200 Subject: [PATCH] First commit --- TwitterOAuth/Exception/TwitterException.php | 19 + TwitterOAuth/TwitterOAuth.php | 593 ++++++++++++++++++ aldolat-twitter.php | 105 ++++ includes/aldolat-twitter-functions.php | 75 +++ .../aldolat-twitter-widget-form-functions.php | 159 +++++ includes/class-aldolat-twitter-widget.php | 243 +++++++ includes/class-aldolat-twitter.php | 127 ++++ index.php | 10 + readme.txt | 15 + uninstall.php | 25 + 10 files changed, 1371 insertions(+) create mode 100644 TwitterOAuth/Exception/TwitterException.php create mode 100644 TwitterOAuth/TwitterOAuth.php create mode 100644 aldolat-twitter.php create mode 100644 includes/aldolat-twitter-functions.php create mode 100644 includes/aldolat-twitter-widget-form-functions.php create mode 100644 includes/class-aldolat-twitter-widget.php create mode 100644 includes/class-aldolat-twitter.php create mode 100644 index.php create mode 100644 readme.txt create mode 100644 uninstall.php diff --git a/TwitterOAuth/Exception/TwitterException.php b/TwitterOAuth/Exception/TwitterException.php new file mode 100644 index 0000000..ca63954 --- /dev/null +++ b/TwitterOAuth/Exception/TwitterException.php @@ -0,0 +1,19 @@ + + * @copyright 2013 + */ + +namespace TwitterOAuth\Exception; + +class TwitterException extends \Exception +{ + public function __toString() + { + return "Twitter API Response: [{$this->code}] {$this->message} (" . __CLASS__ . ") "; + } +} diff --git a/TwitterOAuth/TwitterOAuth.php b/TwitterOAuth/TwitterOAuth.php new file mode 100644 index 0000000..af96f70 --- /dev/null +++ b/TwitterOAuth/TwitterOAuth.php @@ -0,0 +1,593 @@ + + * @copyright 2013 + */ + +namespace TwitterOAuth; + +use TwitterOAuth\Exception\TwitterException; + +class TwitterOAuth +{ + protected $url = 'https://api.twitter.com/1.1/'; + + protected $auth_url = 'https://api.twitter.com/'; + + protected $outputFormats = array('text', 'json', 'array', 'object'); + + protected $defaultFormat = 'object'; + + protected $config = array(); + + protected $call = ''; + + protected $method = 'GET'; + + protected $getParams = array(); + + protected $postParams = array(); + + protected $encoded_bearer_credentials = null; + + protected $bearer_access_token = null; + + protected $headers = null; + + protected $response = null; + + /** + * Prepare a new conection with Twitter API via OAuth + * + * The application ``consumer_key`` and ``consumer_key_secret`` are required + * for most actions, unless when using application-only authentication with a bearer-token. + * The ``oauth_token`` and ``oauth_token_secret`` are required for user type actions. + * + * @param array $config Configuration array with OAuth access data + */ + public function __construct(array $config) + { + $required = array( + 'consumer_key' => '', + 'consumer_secret' => '', + ); + + if (count(array_intersect_key($required, $config)) !== count($required)) { + throw new \Exception('Missing parameters in configuration array'); + } + + if (!isset($config['output_format']) || !in_array($config['output_format'], $this->outputFormats)) { + $config['output_format'] = $this->defaultFormat; + } + + $this->config = $config; + + unset($required, $config); + } + + /** + * Send a GET call to Twitter API via OAuth + * + * @param string $call Twitter resource string + * @param array $getParams GET parameters to send + * @return mixed Output with selected format + */ + public function get($call, array $getParams = null) + { + $this->call = $call; + + $this->method = 'GET'; + $this->resetParams(); + + if ($getParams !== null && is_array($getParams)) { + $this->getParams = $getParams; + } + + return $this->sendRequest(); + } + + /** + * Send a POST call to Twitter API via OAuth + * + * @param string $call Twitter resource string + * @param array $postParams POST parameters to send + * @param array $getParams GET parameters to send + * @return mixed Output with selected format + */ + public function post($call, array $postParams = null, array $getParams = null) + { + $this->call = $call; + + $this->method = 'POST'; + $this->resetParams(); + + if ($postParams !== null && is_array($postParams)) { + $this->postParams = $postParams; + } + + if ($getParams !== null && is_array($getParams)) { + $this->getParams = $getParams; + } + + return $this->sendRequest(); + } + + protected function resetParams() { + $this->headers = null; + $this->response = null; + + $this->postParams = array(); + $this->getParams = array(); + } + + /** + * Returns raw response body + * + * @return string Single string with encoded values + */ + public function getResponse() { + return $this->response; + } + + /** + * Returns response headers as array. + * This can be useful to avoid extra requests for rate-limit info + * x-rate-limit-limit (max request per period) + * x-rate-limit-remaining (remaining this period) + * x-rate-limit-reset (start of next period, UTC timestamp) + * + * @return array with http_code and header lines + */ + public function getHeaders() { + return $this->headers; + } + + /** + * Converting parameters array to a single string with encoded values + * + * @param array $params Input parameters + * @return string Single string with encoded values + */ + protected function getParams(array $params) + { + $r = ''; + + ksort($params); + + foreach ($params as $key => $value) { + $r .= '&' . $key . '=' . rawurlencode($value); + } + + unset($params, $key, $value); + + return trim($r, '&'); + } + + /** + * Getting full URL from a Twitter resource + * + * @param bool $withParams If true then parameters will be outputted + * @return string Full URL + */ + protected function getUrl($withParams = false) + { + $getParams = ''; + + if ($withParams === true) { + $getParams = $this->getParams($this->getParams); + + if (!empty($getParams)) { + $getParams = '?' . $getParams; + } + } + + if ($this->encoded_bearer_credentials && !$this->bearer_access_token) { + $url = $this->auth_url . $this->call; + } else { + $url = $this->url . $this->call . '.json' . $getParams; + } + + return $url; + } + + /** + * Getting OAuth parameters to be used in request headers + * + * @return array OAuth parameters + */ + protected function getOauthParameters() + { + $time = time(); + + return array( + 'oauth_consumer_key' => $this->config['consumer_key'], + 'oauth_nonce' => trim(base64_encode($time), '='), + 'oauth_signature_method' => 'HMAC-SHA1', + 'oauth_timestamp' => $time, + 'oauth_token' => $this->config['oauth_token'], + 'oauth_version' => '1.0' + ); + } + + /** + * Converting all parameters arrays to a single string with encoded values + * + * @return string Single string with encoded values + */ + protected function getRequestString() + { + $params = array_merge($this->getParams, $this->postParams, $this->getOauthParameters()); + + $params = $this->getParams($params); + + return rawurlencode($params); + } + + /** + * Getting OAuth signature base string + * + * @return string OAuth signature base string + */ + protected function getSignatureBaseString() + { + $method = strtoupper($this->method); + + $url = rawurlencode($this->getUrl()); + + return $method . '&' . $url . '&' . $this->getRequestString(); + } + + /** + * Getting a signing key + * + * @return string Signing key + */ + protected function getSigningKey() + { + return $this->config['consumer_secret'] . '&' . $this->config['oauth_token_secret']; + } + + /** + * Calculating the signature + * + * @return string Signature + */ + protected function calculateSignature() + { + return base64_encode(hash_hmac('sha1', $this->getSignatureBaseString(), $this->getSigningKey(), true)); + } + + /** + * Converting OAuth parameters array to a single string with encoded values + * + * @return string Single string with encoded values + */ + protected function getOauthString() + { + // User-keys check moved here for app-only token support + $required = array( + 'oauth_token' => '', + 'oauth_token_secret' => '' + ); + if (count(array_intersect_key($required, $this->config)) !== count($required)) { + throw new \Exception('Missing parameters in configuration array'); + } + + $oauth = array_merge($this->getOauthParameters(), array('oauth_signature' => $this->calculateSignature())); + + ksort($oauth); + + $values = array(); + + foreach ($oauth as $key => $value) { + $values[] = $key . '="' . rawurlencode($value) . '"'; + } + + $oauth = implode(', ', $values); + + unset($values, $key, $value); + + return $oauth; + } + + /** + * Building request HTTP headers + * + * @return array HTTP headers + */ + protected function buildRequestHeader() + { + if ($this->encoded_bearer_credentials) { + if ($this->bearer_access_token) { + return array( + "Authorization: Bearer " . $this->bearer_access_token + ); + } else { + return array( + "Authorization: Basic " . $this->encoded_bearer_credentials, + "Content-Type: application/x-www-form-urlencoded;charset=UTF-8" + ); + } + } else { + // OAuth headers + return array( + 'Authorization: OAuth ' . $this->getOauthString(), + 'Expect:' + ); + } + } + + /** + * Processing Twitter Exceptions in case of error + * + * @param string $type Depends of response format (array|object) + * @param mixed $ex Exceptions + * @throws Exception\TwitterException + */ + protected function processExceptions($type, $ex) + { + switch ($type) { + case 'array': + foreach ($ex['errors'] as $error) { + throw new TwitterException($error['message'], $error['code']); + } + + break; + + default: + foreach ($ex->errors as $error) { + throw new TwitterException($error->message, $error->code); + } + } + + unset($type, $ex, $error); + } + + /** + * Outputs the response in the selected format + * + * @param string $response + * @return mixed + */ + protected function processOutput($response) + { + $format = $this->config['output_format']; + + switch ($format) { + case 'text': + if (substr($response, 2, 6) == 'errors') { + $response = json_decode($response); + + $this->processExceptions('object', $response); + } + + break; + + case 'json': + if (!headers_sent()) { + header('Cache-Control: no-cache, must-revalidate'); + header('Expires: Tue, 19 May 1981 00:00:00 GMT'); + header('Content-type: application/json'); + } + + if (substr($response, 2, 6) == 'errors') { + $response = json_decode($response); + + $this->processExceptions('object', $response); + } + + break; + + case 'array': + $response = json_decode($response, true); + + if (isset($response['errors'])) { + $this->processExceptions('array', $response); + } + + break; + + default: + $response = json_decode($response); + + if (isset($response->errors)) { + $this->processExceptions('object', $response); + } + } + + unset($format); + + return $response; + } + + /** + * Process curl headers to array + */ + protected function processCurlHeaders($headerContent) + { + $this->headers = array(); + + // Split the string on every "double" new line (multiple headers). + $arrRequests = explode("\r\n\r\n", $headerContent); + + // Loop of response headers. The "count() -1" is to + // skip an empty row for the extra line break before the body of the response. + for ($index = 0; $foo = count($arrRequests) -1, $index < $foo; $index++) { + + foreach (explode("\r\n", $arrRequests[$index]) as $i => $line) + { + if ($i === 0) + $this->headers[$index]['http_code'] = $line; + else + { + list ($key, $value) = explode(': ', $line); + $this->headers[$index][$key] = $value; + } + } + } + } + + + /** + * Send GET or POST requests to Twitter API + * + * @throws Exception\TwitterException + * @return mixed Response output + */ + protected function sendRequest() + { + $url = $this->getUrl(true); + + $header = $this->buildRequestHeader(); + + $options = array( + CURLOPT_URL => $url, + CURLOPT_HEADER => true, + CURLOPT_HTTPHEADER => $header, + CURLOPT_RETURNTRANSFER => true, + CURLOPT_SSL_VERIFYPEER => false, + ); + + if (!empty($this->postParams)) { + $options[CURLOPT_POST] = count($this->postParams); + $options[CURLOPT_POSTFIELDS] = $this->getParams($this->postParams); + } + + $c = curl_init(); + + curl_setopt_array($c, $options); + + $response = curl_exec($c); + + if ($n = curl_errno($c)) { + throw new \Exception("cURL error ($n) : ".curl_error($c)); + } + + $header_size = curl_getinfo($c, CURLINFO_HEADER_SIZE); + $headers = substr($response, 0, $header_size); + $this->processCurlHeaders($headers); + + $this->response = substr($response, $header_size); + + curl_close($c); + + unset($response, $options, $c); + + if (!in_array($this->response[0], array('{', '['))) { + throw new TwitterException("($url) ".str_replace(array("\n", "\r", "\t"), '', strip_tags($this->response)), 0); + } + + return $this->processOutput($this->response); + } + + + /** + * Set an Application-only bearer-token + * + * When set, API-requests will use the app-token + * instead of OAuth consumer keys. + * https://dev.twitter.com/docs/auth/application-only-auth + * + * @throws Exception + * @param string $token bearer-token + */ + public function setBearerToken($token = null) + { + if (empty($token)) { + throw new \Exception('Token invalid (empty)'); + } + + $this->generateEncodedBearerCredentials(); + $this->bearer_access_token = $token; + } + + /** + * Get an application-only token from consumer keys + * + * @return string Returns access-token on success + */ + public function getBearerToken() + { + $this->generateEncodedBearerCredentials(); + + $this->post('oauth2/token', array('grant_type' => 'client_credentials')); + + $this->bearer_access_token = $this->processTokenResponse('oauth2/token'); + + return $this->bearer_access_token; + } + + /** + * Revoke / invalidate an application-only token + * + * @param string $token Bearer-token + * @throws Exception + * @return string Returns the same token on success + */ + public function invalidateBearerToken($token = null) + { + if (empty($token)) { + throw new \Exception('Token invalid (empty)'); + } + + $this->generateEncodedBearerCredentials(); + + $this->post("oauth2/invalidate_token", array("access_token" => rawurldecode($token))); + + $return_token = $this->processTokenResponse('oauth2/invalidate_token'); + + return $return_token; + } + + /** + * Generates basic authorization credentials for token request + * + */ + protected function generateEncodedBearerCredentials() + { + $this->bearer_access_token = null; + $this->encoded_bearer_credentials = null; + + $bearer_credentials = urlencode($this->config['consumer_key']) . ":" . urlencode($this->config['consumer_secret']); + + $this->encoded_bearer_credentials = base64_encode($bearer_credentials); + } + + /** + * Process oauth2 response, returns the bearer-access-token + * + * @param string $response + * @throws Exception\TwitterException + * @return mixed + */ + protected function processTokenResponse($path) + { + // json-decode raw response (as object) + $response = json_decode($this->response); + + $token = false; + + switch ($path) { + case 'oauth2/token': + if (isset($response->token_type) && $response->token_type == 'bearer') { + $token = $response->access_token; + } + break; + + case 'oauth2/invalidate_token': + if (isset($response->access_token)) { + $token = $response->access_token; + } + break; + } + + unset($response); + + return $token; + } + +} diff --git a/aldolat-twitter.php b/aldolat-twitter.php new file mode 100644 index 0000000..90876ad --- /dev/null +++ b/aldolat-twitter.php @@ -0,0 +1,105 @@ +. + */ + +/** + * Prevent direct access to this file. + * + * @since 0.0.1 + */ +if ( ! defined( 'WPINC' ) ) { + exit( 'No script kiddies please!' ); +} + +/** + * Launch Aldolat Twitter. + * + * @since 0.0.1 + */ +add_action( 'plugins_loaded', 'aldolat_twitter_setup' ); + +/** + * Setup the plugin and fire the necessary files. + * + * @since 0.0.1 + */ +function aldolat_twitter_setup() { + /* + * Define the version of the plugin. + */ + define( 'ALDOLAT_TWITTER_PLUGIN_VERSION', '0.0.1' ); + + /* + * Load the translation. + * + * @since 0.0.1 + */ + load_plugin_textdomain( 'aldolat-twitter', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' ); + + /* + * Include all necessary PHP files. + * + * @since 0.0.1 + */ + // Load the TwitterOAuth library. + require_once plugin_dir_path( __FILE__ ) . 'TwitterOAuth/TwitterOAuth.php'; + require_once plugin_dir_path( __FILE__ ) . 'TwitterOAuth/Exception/TwitterException.php'; + // Load the class for Twitter. + require_once plugin_dir_path( __FILE__ ) . 'includes/class-aldolat-twitter.php'; + // Load the init functions. + require_once plugin_dir_path( __FILE__ ) . 'includes/aldolat-twitter-functions.php'; + // Load the widget's form functions. + require_once plugin_dir_path( __FILE__ ) . 'includes/aldolat-twitter-widget-form-functions.php'; + // Load the widget's PHP file. + require_once plugin_dir_path( __FILE__ ) . 'includes/class-aldolat-twitter-widget.php'; + + /* + * Load Aldolat Twitter's widgets. + * + * @since 0.0.1 + */ + add_action( 'widgets_init', 'aldolat_twitter_load_widget' ); +} + +/* + * CODE IS POETRY + */ diff --git a/includes/aldolat-twitter-functions.php b/includes/aldolat-twitter-functions.php new file mode 100644 index 0000000..7a5f9d9 --- /dev/null +++ b/includes/aldolat-twitter-functions.php @@ -0,0 +1,75 @@ + esc_html__( 'My tweets', 'pinboard-bookmarks' ), + 'intro_text' => '', + 'screen_name' => '', + 'count' => 3, + 'exclude_replies' => true, + 'consumer_key' => '', + 'consumer_secret' => '', + 'oauth_token' => '', + 'oauth_token_secret' => '', + 'widget_id' => '', + ); + + return $defaults; +} + +/** + * Register the widget. + * + * @since 0.0.1 + */ +function aldolat_twitter_load_widget() { + register_widget( 'Aldolat_Twitter_Widget' ); +} + +function aldolat_twitter_get_tweets( $args ) { + $html = ''; + + $feed = get_transient( 'aldolat-twitter-tweets' ); + + if ( false === $feed ) { + $twitter_getter = new Aldolat_Twitter( $args ); + $html = $twitter_getter->fetch(); + set_transient( 'aldolat-twitter-tweets', $html, 5 * MINUTE_IN_SECONDS ); + } else { + $html = $feed; + } + + return $html; +} + +function aldolat_twitter_tweets( $args ) { + echo aldolat_twitter_get_tweets( $args ); +} diff --git a/includes/aldolat-twitter-widget-form-functions.php b/includes/aldolat-twitter-widget-form-functions.php new file mode 100644 index 0000000..c024f67 --- /dev/null +++ b/includes/aldolat-twitter-widget-form-functions.php @@ -0,0 +1,159 @@ +' . wp_kses_post( $label ) . ''; +} + +/** + * Create a form text input to be used in the widget panel. + * + * @since 1.12 + * @param string $label The label to display. + * @param string $id The id of the label. + * @param string $name The name of the input form. + * @param string $value The values of the input form. + * @param string $placeholder The HTML placeholder for the input form. + * @param string $comment An optional comment to display. It is displayed below the input form. + * @param string $style An optional inline style. + * @param string $class An optional CSS class. + * @uses aldolat_twitter_form_label + */ +function aldolat_twitter_form_input_text( $label, $id, $name, $value, $placeholder = '', $comment = '', $style = '', $class = '' ) { + $class = rtrim( 'widefat pinboard-bookmarks-input ' . $class ); + + if ( $style ) { + echo '

'; + } else { + echo '

'; + } + + aldolat_twitter_form_label( $label, $id ); + + ?> + + ' . wp_kses_post( $comment ) . ''; + } + + echo '

'; +} + +/** + * Create a form textarea to be used in the widget panel. + * + * @param string $label The label to display. + * @param string $id The id of the label. + * @param string $name The name of the textarea form. + * @param string $text The text to display. + * @param string $placeholder The HTML placeholder for the input form. + * @param string $comment An optional comment to display. It is displayed below the textarea form. + * @param string $style An optional inline style. + * @since 1.12 + */ +function aldolat_twitter_form_textarea( $label, $id, $name, $text, $placeholder = '', $comment = '', $style = '' ) { + echo '

'; + aldolat_twitter_form_label( $label, $id ); + + ?> + + ' . wp_kses_post( $comment ) . ''; + } + echo '

'; +} + +/** + * Create a form checkbox to be used in the widget panel. + * + * @param string $label The label to display. + * @param string $id The id of the label. + * @param string $name The name of the checkbox form. + * @param string $checked If the option is checked. + * @param string $comment An optional comment to display. It is displayed below the checkbox form. + * @param string $class An optional CSS class. + * @since 1.12 + */ +function aldolat_twitter_form_checkbox( $label, $id, $name, $checked, $comment = '', $class = '' ) { + $class = rtrim( 'checkbox pinboard-bookmarks-checkbox ' . $class ); + ?> +

+ id="" name="" /> +   + + +
+ +

+ +

+ +   + + +
+ +

+ 'aldolat_twitter_widget', + 'description' => esc_html__( 'Publish your tweets in your blog', 'aldolat-twitter' ), + ); + $control_ops = array( + 'width' => 350, + 'id_base' => 'aldolat_twitter_widget', + ); + + parent::__construct( + 'aldolat_twitter_widget', + esc_html__( 'Aldolat Twitter', 'aldolat-twitter' ), + $widget_ops, + $control_ops + ); + } + + /** + * Front-end display of widget. + * + * @see WP_Widget::widget() + * + * @param array $args Widget arguments. + * $args contains: + * $args['name']; + * $args['id']; + * $args['description']; + * $args['class']; + * $args['before_widget']; + * $args['after_widget']; + * $args['before_title']; + * $args['after_title']; + * $args['widget_id']; + * $args['widget_name']. + * @param array $instance Saved values from database. + */ + public function widget( $args, $instance ) { + $instance = wp_parse_args( $instance, aldolat_twitter_get_defaults() ); + + echo "\n" . '' . "\n"; + + echo $args['before_widget']; + + if ( ! empty( $instance['title'] ) ) { + echo $args['before_title'] . apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base ) . $args['after_title']; + } + + $params = array( + 'consumer_key' => $instance['consumer_key'], + 'consumer_secret' => $instance['consumer_secret'], + 'oauth_token' => $instance['oauth_token'], + 'oauth_token_secret' => $instance['oauth_token_secret'], + 'screen_name' => $instance['screen_name'], + 'count' => $instance['count'], + 'exclude_replies' => $instance['exclude_replies'], + ); + aldolat_twitter_tweets( $params ); + + echo $args['after_widget']; + + echo "\n" . '' . "\n\n"; + } + + /** + * Sanitize widget form values as they are saved. + * + * @see WP_Widget::update() + * + * @param array $new_instance Values just sent to be saved. + * @param array $old_instance Previously saved values from database. + * + * @return array Updated safe values to be saved. + */ + public function update( $new_instance, $old_instance ) { + $instance = (array) $old_instance; + + $instance['title'] = sanitize_text_field( $new_instance['title'] ); + $instance['intro_text'] = wp_kses_post( $new_instance['intro_text'] ); + $instance['screen_name'] = preg_replace( '([^a-zA-Z0-9\-_])', '', sanitize_text_field( $new_instance['screen_name'] ) ); + + $instance['count'] = absint( sanitize_text_field( $new_instance['count'] ) ); + if ( 0 === $instance['count'] || '' === $instance['count'] || ! is_numeric( $instance['count'] ) ) { + $instance['count'] = 3; + } + + $instance['exclude_replies'] = isset( $new_instance['exclude_replies'] ) ? true : false; + $instance['consumer_key'] = sanitize_text_field( $new_instance['consumer_key'] ); + $instance['consumer_secret'] = sanitize_text_field( $new_instance['consumer_secret'] ); + $instance['oauth_token'] = sanitize_text_field( $new_instance['oauth_token'] ); + $instance['oauth_token_secret'] = sanitize_text_field( $new_instance['oauth_token_secret'] ); + + // This option is stored only for debug purposes. + $instance['widget_id'] = $this->id; + + return $instance; + } + + /** + * Back-end widget form. + * + * @see WP_Widget::form() + * + * @param array $instance Previously saved values from database. + */ + public function form( $instance ) { + $instance = wp_parse_args( (array) $instance, aldolat_twitter_get_defaults() ); + ?> + +
+ +

+ +

+ +

+ +

+ + get_field_id( 'title' ), + $this->get_field_name( 'title' ), + esc_attr( $instance['title'] ), + esc_html__( 'My bookmarks on Pinboard', 'aldolat-twitter' ) + ); + ?> + +

+ + get_field_id( 'intro_text' ), + $this->get_field_name( 'intro_text' ), + $instance['intro_text'], + esc_html__( 'These are my bookmarks on Pinboard about Italian recipes.', 'aldolat-twitter' ), + esc_html__( 'You can use some HTML, as you would do when writing a post.', 'aldolat-twitter' ), + $style = 'resize: vertical; height: 80px;' + ); + ?> + +

+ + get_field_id( 'screen_name' ), + $this->get_field_name( 'screen_name' ), + esc_attr( $instance['screen_name'] ), + esc_html__( 'username', 'aldolat-twitter' ), + esc_html__( 'This is the only mandatory option.', 'aldolat-twitter' ) + ); + + // Number of items. + pinboard_bookmarks_form_input_text( + esc_html__( 'Number of items:', 'aldolat-twitter' ), + $this->get_field_id( 'count' ), + $this->get_field_name( 'count' ), + esc_attr( $instance['count'] ), + '3' + ); + + // Random order. + pinboard_bookmarks_form_checkbox( + esc_html__( 'Esclude replies', 'aldolat-twitter' ), + $this->get_field_id( 'exclude_replies' ), + $this->get_field_name( 'exclude_replies' ), + $instance['exclude_replies'] + ); + ?> + +

+ + get_field_id( 'consumer_key' ), + $this->get_field_name( 'consumer_key' ), + esc_attr( $instance['consumer_key'] ) + ); + // Consumer secret. + pinboard_bookmarks_form_input_text( + esc_html__( 'Consumer secret:', 'aldolat-twitter' ), + $this->get_field_id( 'consumer_secret' ), + $this->get_field_name( 'consumer_secret' ), + esc_attr( $instance['consumer_secret'] ) + ); + // Oauth token. + pinboard_bookmarks_form_input_text( + esc_html__( 'Oauth token:', 'aldolat-twitter' ), + $this->get_field_id( 'oauth_token' ), + $this->get_field_name( 'oauth_token' ), + esc_attr( $instance['oauth_token'] ) + ); + // Oauth token secret. + pinboard_bookmarks_form_input_text( + esc_html__( 'Oauth token secret:', 'aldolat-twitter' ), + $this->get_field_id( 'oauth_token_secret' ), + $this->get_field_name( 'oauth_token_secret' ), + esc_attr( $instance['oauth_token_secret'] ) + ); + ?> +
+ '', + 'consumer_secret' => '', + 'oauth_token' => '', + 'oauth_token_secret' => '', + 'screen_name' => '', + 'count' => '', + 'exclude_replies' => '', + ); + wp_parse_args( $args, $defaults ); + + $settings = array( + 'consumer_key' => $args['consumer_key'], + 'consumer_secret' => $args['consumer_secret'], + 'oauth_token' => $args['oauth_token'], + 'oauth_token_secret' => $args['oauth_token_secret'], + 'output_format' => 'text', + ); + + $this->connection = new TwitterOAuth( $settings ); + $this->screen_name = $args['screen_name']; + $this->count = $args['count']; + $this->exclude_replies = $args['exclude_replies']; + } + + private function relative_time( $t ) { + $new_tweet_time = strtotime( $t ); + return human_time_diff( $new_tweet_time, time() ); + } + + private function format( $tweet ) { + $tweet_text = $tweet->text; + $tweet_entities = array(); + + foreach ( $tweet->entities->urls as $url ) { + $tweet_entities[] = array( + 'type' => 'url', + 'curText' => substr( $tweet_text, $url->indices[0], ( $url->indices[1] - $url->indices[0] ) ), + 'newText' => '' . $url->display_url . '', + ); + } + + foreach ( $tweet->entities->user_mentions as $mention ) { + $string = substr( $tweet_text, $mention->indices[0], ( $mention->indices[1] - $mention->indices[0] ) ); + $tweet_entities[] = array( + 'type' => 'mention', + 'curText' => substr( $tweet_text, $mention->indices[0], ( $mention->indices[1] - $mention->indices[0] ) ), + 'newText' => '' . $string . '' + ); + } + + foreach ( $tweet->entities->hashtags as $tag ) { + $string = substr( $tweet_text, $tag->indices[0], ( $tag->indices[1] - $tag->indices[0] ) ); + $tweet_entities[] = array( + 'type' => 'hashtag', + 'curText' => substr( $tweet_text, $tag->indices[0], ( $tag->indices[1] - $tag->indices[0] ) ), + 'newText' => '' . $string . '' + ); + } + + foreach ( $tweet_entities as $entity ) { + $tweet_text = str_replace( $entity['curText'], $entity['newText'], $tweet_text ); + } + + return $tweet_text; + } + + public function fetch() { + $params = array( + 'screen_name' => $this->screen_name, + 'count' => $this->count, + 'exclude_replies' => $this->exclude_replies, + ); + + $html = '
'; + + $resp = $this->connection->get( 'statuses/user_timeline', $params ); + $tweets = json_decode( $resp ); + + foreach ( $tweets as $tweet ) { + $html .= '
'; + $html .= ''; + $html .= '
' . $this->format( $tweet ) . '
'; + $html .= '
'; + } + + $html .= '
'; + + return $html; + } +} diff --git a/index.php b/index.php new file mode 100644 index 0000000..4223de4 --- /dev/null +++ b/index.php @@ -0,0 +1,10 @@ +