twitter-timeline.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  1. <?php
  2. /*
  3. * Based on Evolution Twitter Timeline
  4. * (https://wordpress.org/extend/plugins/evolution-twitter-timeline/)
  5. * For details on Twitter Timelines see:
  6. * - https://twitter.com/settings/widgets
  7. * - https://dev.twitter.com/docs/embedded-timelines
  8. */
  9. /**
  10. * Register the widget for use in Appearance -> Widgets
  11. */
  12. add_action( 'widgets_init', 'jetpack_twitter_timeline_widget_init' );
  13. function jetpack_twitter_timeline_widget_init() {
  14. register_widget( 'Jetpack_Twitter_Timeline_Widget' );
  15. }
  16. class Jetpack_Twitter_Timeline_Widget extends WP_Widget {
  17. /**
  18. * Register widget with WordPress.
  19. */
  20. public function __construct() {
  21. parent::__construct(
  22. 'twitter_timeline',
  23. /** This filter is documented in modules/widgets/facebook-likebox.php */
  24. apply_filters( 'jetpack_widget_name', esc_html__( 'Twitter Timeline', 'jetpack' ) ),
  25. array(
  26. 'classname' => 'widget_twitter_timeline',
  27. 'description' => __( 'Display an official Twitter Embedded Timeline widget.', 'jetpack' ),
  28. 'customize_selective_refresh' => true,
  29. )
  30. );
  31. if ( is_active_widget( false, false, $this->id_base ) || is_active_widget( false, false, 'monster' ) || is_customize_preview() ) {
  32. add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
  33. }
  34. add_action( 'admin_enqueue_scripts', array( $this, 'admin_scripts' ) );
  35. }
  36. /**
  37. * Enqueue scripts.
  38. */
  39. public function enqueue_scripts() {
  40. wp_enqueue_script( 'jetpack-twitter-timeline' );
  41. }
  42. /**
  43. * Enqueue Twitter's widget library.
  44. *
  45. * @deprecated
  46. */
  47. public function library() {
  48. _deprecated_function( __METHOD__, '4.0.0' );
  49. wp_print_scripts( array( 'jetpack-twitter-timeline' ) );
  50. }
  51. /**
  52. * Enqueue script to improve admin UI
  53. */
  54. public function admin_scripts( $hook ) {
  55. // This is still 'widgets.php' when managing widgets via the Customizer.
  56. if ( 'widgets.php' === $hook ) {
  57. wp_enqueue_script(
  58. 'twitter-timeline-admin',
  59. Jetpack::get_file_url_for_environment(
  60. '_inc/build/widgets/twitter-timeline-admin.min.js',
  61. 'modules/widgets/twitter-timeline-admin.js'
  62. )
  63. );
  64. }
  65. }
  66. /**
  67. * Front-end display of widget.
  68. *
  69. * @see WP_Widget::widget()
  70. *
  71. * @param array $args Widget arguments.
  72. * @param array $instance Saved values from database.
  73. */
  74. public function widget( $args, $instance ) {
  75. // Twitter deprecated `data-widget-id` on 2018-05-25,
  76. // with cease support deadline on 2018-07-27.
  77. if ( isset( $instance['type'] ) && 'widget-id' === $instance['type'] ) {
  78. if ( current_user_can( 'edit_theme_options' ) ) {
  79. echo $args['before_widget'];
  80. echo $args['before_title'] . esc_html__( 'Twitter Timeline', 'jetpack' ) . $args['after_title'];
  81. echo '<p>' . esc_html__( "The Twitter Timeline widget can't display tweets based on searches or hashtags. To display a simple list of tweets instead, change the Widget ID to a Twitter username. Otherwise, delete this widget.", 'jetpack' ) . '</p>';
  82. echo '<p>' . esc_html__( '(Only administrators will see this message.)', 'jetpack' ) . '</p>';
  83. echo $args['after_widget'];
  84. }
  85. return;
  86. }
  87. $instance['lang'] = substr( strtoupper( get_locale() ), 0, 2 );
  88. echo $args['before_widget'];
  89. $title = isset( $instance['title'] ) ? $instance['title'] : '';
  90. /** This filter is documented in core/src/wp-includes/default-widgets.php */
  91. $title = apply_filters( 'widget_title', $title );
  92. if ( ! empty( $title ) ) {
  93. echo $args['before_title'] . $title . $args['after_title'];
  94. }
  95. if ( isset( $instance['type'] ) && 'widget-id' === $instance['type'] && current_user_can( 'edit_theme_options' ) ) {
  96. echo '<p>' . esc_html__( 'As of July 27, 2018, the Twitter Timeline widget will no longer display tweets based on searches or hashtags. To display a simple list of tweets instead, change the Widget ID to a Twitter username.', 'jetpack' ) . '</p>';
  97. echo '<p>' . esc_html__( '(Only administrators will see this message.)', 'jetpack' ) . '</p>';
  98. }
  99. // Start tag output
  100. // This tag is transformed into the widget markup by Twitter's
  101. // widgets.js code
  102. echo '<a class="twitter-timeline"';
  103. $data_attribs = array(
  104. 'width',
  105. 'height',
  106. 'theme',
  107. 'link-color',
  108. 'border-color',
  109. 'tweet-limit',
  110. 'lang'
  111. );
  112. foreach ( $data_attribs as $att ) {
  113. if ( ! empty( $instance[ $att ] ) && ! is_array( $instance[ $att ] ) ) {
  114. echo ' data-' . esc_attr( $att ) . '="' . esc_attr( $instance[ $att ] ) . '"';
  115. }
  116. }
  117. /** This filter is documented in modules/shortcodes/tweet.php */
  118. $partner = apply_filters( 'jetpack_twitter_partner_id', 'jetpack' );
  119. if ( ! empty( $partner ) ) {
  120. echo ' data-partner="' . esc_attr( $partner ) . '"';
  121. }
  122. if ( ! empty( $instance['chrome'] ) && is_array( $instance['chrome'] ) ) {
  123. echo ' data-chrome="' . esc_attr( join( ' ', $instance['chrome'] ) ) . '"';
  124. }
  125. $type = ( isset( $instance['type'] ) ? $instance['type'] : '' );
  126. $widget_id = ( isset( $instance['widget-id'] ) ? $instance['widget-id'] : '' );
  127. switch ( $type ) {
  128. case 'profile':
  129. echo ' href="https://twitter.com/' . esc_attr( $widget_id ) . '"';
  130. break;
  131. case 'widget-id':
  132. default:
  133. echo ' data-widget-id="' . esc_attr( $widget_id ) . '"';
  134. break;
  135. }
  136. echo ' href="https://twitter.com/' . esc_attr( $widget_id ) . '"';
  137. // End tag output
  138. echo '>';
  139. $timeline_placeholder = __( 'My Tweets', 'jetpack' );
  140. /**
  141. * Filter the Timeline placeholder text.
  142. *
  143. * @module widgets
  144. *
  145. * @since 3.4.0
  146. *
  147. * @param string $timeline_placeholder Timeline placeholder text.
  148. */
  149. $timeline_placeholder = apply_filters( 'jetpack_twitter_timeline_placeholder', $timeline_placeholder );
  150. echo esc_html( $timeline_placeholder ) . '</a>';
  151. // End tag output
  152. echo $args['after_widget'];
  153. /** This action is documented in modules/widgets/gravatar-profile.php */
  154. do_action( 'jetpack_stats_extra', 'widget_view', 'twitter_timeline' );
  155. }
  156. /**
  157. * Sanitize widget form values as they are saved.
  158. *
  159. * @see WP_Widget::update()
  160. *
  161. * @param array $new_instance Values just sent to be saved.
  162. * @param array $old_instance Previously saved values from database.
  163. *
  164. * @return array Updated safe values to be saved.
  165. */
  166. public function update( $new_instance, $old_instance ) {
  167. $instance = array();
  168. $instance['title'] = sanitize_text_field( $new_instance['title'] );
  169. $width = (int) $new_instance['width'];
  170. if ( $width ) {
  171. // From publish.twitter.com: 220 <= width <= 1200
  172. $instance['width'] = min( max( $width, 220 ), 1200 );
  173. } else {
  174. $instance['width'] = '';
  175. }
  176. $height = (int) $new_instance['height'];
  177. if ( $height ) {
  178. // From publish.twitter.com: height >= 200
  179. $instance['height'] = max( $height, 200 );
  180. } else {
  181. $instance['height'] = '';
  182. }
  183. $tweet_limit = (int) $new_instance['tweet-limit'];
  184. if ( $tweet_limit ) {
  185. $instance['tweet-limit'] = min( max( $tweet_limit, 1 ), 20 );
  186. /**
  187. * A timeline with a specified limit is expanded to the height of those Tweets.
  188. * The specified height value no longer applies, so reject the height value
  189. * when a valid limit is set: a widget attempting to save both limit 5 and
  190. * height 400 would be saved with just limit 5.
  191. */
  192. $instance['height'] = '';
  193. } else {
  194. $instance['tweet-limit'] = null;
  195. }
  196. // If they entered something that might be a full URL, try to parse it out
  197. if ( is_string( $new_instance['widget-id'] ) ) {
  198. if ( preg_match(
  199. '#https?://twitter\.com/settings/widgets/(\d+)#s',
  200. $new_instance['widget-id'],
  201. $matches
  202. ) ) {
  203. $new_instance['widget-id'] = $matches[1];
  204. }
  205. }
  206. $instance['widget-id'] = sanitize_text_field( $new_instance['widget-id'] );
  207. $hex_regex = '/#([a-f]|[A-F]|[0-9]){3}(([a-f]|[A-F]|[0-9]){3})?\b/';
  208. foreach ( array( 'link-color', 'border-color' ) as $color ) {
  209. $new_color = sanitize_text_field( $new_instance[ $color ] );
  210. if ( preg_match( $hex_regex, $new_color ) ) {
  211. $instance[ $color ] = $new_color;
  212. }
  213. }
  214. $instance['type'] = 'profile';
  215. $instance['theme'] = 'light';
  216. if ( in_array( $new_instance['theme'], array( 'light', 'dark' ) ) ) {
  217. $instance['theme'] = $new_instance['theme'];
  218. }
  219. $instance['chrome'] = array();
  220. $chrome_settings = array(
  221. 'noheader',
  222. 'nofooter',
  223. 'noborders',
  224. 'transparent',
  225. 'noscrollbar',
  226. );
  227. if ( isset( $new_instance['chrome'] ) ) {
  228. foreach ( $new_instance['chrome'] as $chrome ) {
  229. if ( in_array( $chrome, $chrome_settings ) ) {
  230. $instance['chrome'][] = $chrome;
  231. }
  232. }
  233. }
  234. return $instance;
  235. }
  236. /**
  237. * Returns a link to the documentation for a feature of this widget on
  238. * Jetpack or WordPress.com.
  239. */
  240. public function get_docs_link( $hash = '' ) {
  241. if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
  242. $base_url = 'https://support.wordpress.com/widgets/twitter-timeline-widget/';
  243. } else {
  244. $base_url = 'https://jetpack.com/support/extra-sidebar-widgets/twitter-timeline-widget/';
  245. }
  246. return '<a href="' . $base_url . $hash . '" target="_blank">( ? )</a>';
  247. }
  248. /**
  249. * Back end widget form.
  250. *
  251. * @see WP_Widget::form()
  252. *
  253. * @param array $instance Previously saved values from database.
  254. */
  255. public function form( $instance ) {
  256. $defaults = array(
  257. 'title' => esc_html__( 'Follow me on Twitter', 'jetpack' ),
  258. 'width' => '',
  259. 'height' => '400',
  260. 'type' => 'profile',
  261. 'widget-id' => '',
  262. 'link-color' => '#f96e5b',
  263. 'border-color' => '#e8e8e8',
  264. 'theme' => 'light',
  265. 'chrome' => array(),
  266. 'tweet-limit' => null,
  267. );
  268. $instance = wp_parse_args( (array) $instance, $defaults );
  269. if ( 'widget-id' === $instance['type'] ) {
  270. $instance['widget-id'] = '';
  271. }
  272. $instance['type'] = 'profile';
  273. ?>
  274. <p>
  275. <label for="<?php echo $this->get_field_id( 'title' ); ?>">
  276. <?php esc_html_e( 'Title:', 'jetpack' ); ?>
  277. </label>
  278. <input
  279. class="widefat"
  280. id="<?php echo $this->get_field_id( 'title' ); ?>"
  281. name="<?php echo $this->get_field_name( 'title' ); ?>"
  282. type="text"
  283. value="<?php echo esc_attr( $instance['title'] ); ?>"
  284. />
  285. </p>
  286. <p>
  287. <label for="<?php echo $this->get_field_id( 'width' ); ?>">
  288. <?php esc_html_e( 'Maximum Width (px; 220 to 1200):', 'jetpack' ); ?>
  289. </label>
  290. <input
  291. class="widefat"
  292. id="<?php echo $this->get_field_id( 'width' ); ?>"
  293. name="<?php echo $this->get_field_name( 'width' ); ?>"
  294. type="number" min="220" max="1200"
  295. value="<?php echo esc_attr( $instance['width'] ); ?>"
  296. />
  297. </p>
  298. <p>
  299. <label for="<?php echo $this->get_field_id( 'height' ); ?>">
  300. <?php esc_html_e( 'Height (px; at least 200):', 'jetpack' ); ?>
  301. </label>
  302. <input
  303. class="widefat"
  304. id="<?php echo $this->get_field_id( 'height' ); ?>"
  305. name="<?php echo $this->get_field_name( 'height' ); ?>"
  306. type="number" min="200"
  307. value="<?php echo esc_attr( $instance['height'] ); ?>"
  308. />
  309. </p>
  310. <p>
  311. <label for="<?php echo $this->get_field_id( 'tweet-limit' ); ?>">
  312. <?php esc_html_e( '# of Tweets Shown (1 to 20):', 'jetpack' ); ?>
  313. </label>
  314. <input
  315. class="widefat"
  316. id="<?php echo $this->get_field_id( 'tweet-limit' ); ?>"
  317. name="<?php echo $this->get_field_name( 'tweet-limit' ); ?>"
  318. type="number" min="1" max="20"
  319. value="<?php echo esc_attr( $instance['tweet-limit'] ); ?>"
  320. />
  321. </p>
  322. <p class="jetpack-twitter-timeline-widget-id-container">
  323. <label for="<?php echo $this->get_field_id( 'widget-id' ); ?>">
  324. <?php esc_html_e( 'Twitter Username:', 'jetpack' ); ?>
  325. <?php echo $this->get_docs_link( '#twitter-username' ); ?>
  326. </label>
  327. <input
  328. class="widefat"
  329. id="<?php echo $this->get_field_id( 'widget-id' ); ?>"
  330. name="<?php echo $this->get_field_name( 'widget-id' ); ?>"
  331. type="text"
  332. value="<?php echo esc_attr( $instance['widget-id'] ); ?>"
  333. />
  334. </p>
  335. <p>
  336. <label for="<?php echo $this->get_field_id( 'chrome-noheader' ); ?>">
  337. <?php esc_html_e( 'Layout Options:', 'jetpack' ); ?>
  338. </label>
  339. <br />
  340. <input
  341. type="checkbox"<?php checked( in_array( 'noheader', $instance['chrome'] ) ); ?>
  342. id="<?php echo $this->get_field_id( 'chrome-noheader' ); ?>"
  343. name="<?php echo $this->get_field_name( 'chrome' ); ?>[]"
  344. value="noheader"
  345. />
  346. <label for="<?php echo $this->get_field_id( 'chrome-noheader' ); ?>">
  347. <?php esc_html_e( 'No Header', 'jetpack' ); ?>
  348. </label>
  349. <br />
  350. <input
  351. type="checkbox"<?php checked( in_array( 'nofooter', $instance['chrome'] ) ); ?>
  352. id="<?php echo $this->get_field_id( 'chrome-nofooter' ); ?>"
  353. name="<?php echo $this->get_field_name( 'chrome' ); ?>[]"
  354. value="nofooter"
  355. />
  356. <label for="<?php echo $this->get_field_id( 'chrome-nofooter' ); ?>">
  357. <?php esc_html_e( 'No Footer', 'jetpack' ); ?>
  358. </label>
  359. <br />
  360. <input
  361. type="checkbox"<?php checked( in_array( 'noborders', $instance['chrome'] ) ); ?>
  362. id="<?php echo $this->get_field_id( 'chrome-noborders' ); ?>"
  363. name="<?php echo $this->get_field_name( 'chrome' ); ?>[]"
  364. value="noborders"
  365. />
  366. <label for="<?php echo $this->get_field_id( 'chrome-noborders' ); ?>">
  367. <?php esc_html_e( 'No Borders', 'jetpack' ); ?>
  368. </label>
  369. <br />
  370. <input
  371. type="checkbox"<?php checked( in_array( 'noscrollbar', $instance['chrome'] ) ); ?>
  372. id="<?php echo $this->get_field_id( 'chrome-noscrollbar' ); ?>"
  373. name="<?php echo $this->get_field_name( 'chrome' ); ?>[]"
  374. value="noscrollbar"
  375. />
  376. <label for="<?php echo $this->get_field_id( 'chrome-noscrollbar' ); ?>">
  377. <?php esc_html_e( 'No Scrollbar', 'jetpack' ); ?>
  378. </label>
  379. <br />
  380. <input
  381. type="checkbox"<?php checked( in_array( 'transparent', $instance['chrome'] ) ); ?>
  382. id="<?php echo $this->get_field_id( 'chrome-transparent' ); ?>"
  383. name="<?php echo $this->get_field_name( 'chrome' ); ?>[]"
  384. value="transparent"
  385. />
  386. <label for="<?php echo $this->get_field_id( 'chrome-transparent' ); ?>">
  387. <?php esc_html_e( 'Transparent Background', 'jetpack' ); ?>
  388. </label>
  389. </p>
  390. <p>
  391. <label for="<?php echo $this->get_field_id( 'link-color' ); ?>">
  392. <?php _e( 'Link Color (hex):', 'jetpack' ); ?>
  393. </label>
  394. <input
  395. class="widefat"
  396. id="<?php echo $this->get_field_id( 'link-color' ); ?>"
  397. name="<?php echo $this->get_field_name( 'link-color' ); ?>"
  398. type="text"
  399. value="<?php echo esc_attr( $instance['link-color'] ); ?>"
  400. />
  401. </p>
  402. <p>
  403. <label for="<?php echo $this->get_field_id( 'border-color' ); ?>">
  404. <?php _e( 'Border Color (hex):', 'jetpack' ); ?>
  405. </label>
  406. <input
  407. class="widefat"
  408. id="<?php echo $this->get_field_id( 'border-color' ); ?>"
  409. name="<?php echo $this->get_field_name( 'border-color' ); ?>"
  410. type="text"
  411. value="<?php echo esc_attr( $instance['border-color'] ); ?>"
  412. />
  413. </p>
  414. <p>
  415. <label for="<?php echo $this->get_field_id( 'theme' ); ?>">
  416. <?php _e( 'Timeline Theme:', 'jetpack' ); ?>
  417. </label>
  418. <select
  419. name="<?php echo $this->get_field_name( 'theme' ); ?>"
  420. id="<?php echo $this->get_field_id( 'theme' ); ?>"
  421. class="widefat"
  422. >
  423. <option value="light"<?php selected( $instance['theme'], 'light' ); ?>>
  424. <?php esc_html_e( 'Light', 'jetpack' ); ?>
  425. </option>
  426. <option value="dark"<?php selected( $instance['theme'], 'dark' ); ?>>
  427. <?php esc_html_e( 'Dark', 'jetpack' ); ?>
  428. </option>
  429. </select>
  430. </p>
  431. <?php
  432. }
  433. }