[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-admin/includes/ -> class-wp-press-this.php (source)

   1  <?php
   2  /**
   3   * Press This class and display functionality
   4   *
   5   * @package WordPress
   6   * @subpackage Press_This
   7   * @since 4.2.0
   8   */
   9  
  10  /**
  11   * Press This class.
  12   *
  13   * @since 4.2.0
  14   */
  15  class WP_Press_This {
  16      // Used to trigger the bookmarklet update notice.
  17      const VERSION = 8;
  18      public $version = 8;
  19  
  20      private $images = array();
  21  
  22      private $embeds = array();
  23  
  24      private $domain = '';
  25  
  26      /**
  27       * Constructor.
  28       *
  29       * @since 4.2.0
  30       */
  31  	public function __construct() {}
  32  
  33      /**
  34       * App and site settings data, including i18n strings for the client-side.
  35       *
  36       * @since 4.2.0
  37       *
  38       * @return array Site settings.
  39       */
  40  	public function site_settings() {
  41          return array(
  42              /**
  43               * Filters whether or not Press This should redirect the user in the parent window upon save.
  44               *
  45               * @since 4.2.0
  46               *
  47               * @param bool $redirect Whether to redirect in parent window or not. Default false.
  48               */
  49              'redirInParent' => apply_filters( 'press_this_redirect_in_parent', false ),
  50          );
  51      }
  52  
  53      /**
  54       * Get the source's images and save them locally, for posterity, unless we can't.
  55       *
  56       * @since 4.2.0
  57       *
  58       * @param int    $post_id Post ID.
  59       * @param string $content Optional. Current expected markup for Press This. Expects slashed. Default empty.
  60       * @return string New markup with old image URLs replaced with the local attachment ones if swapped.
  61       */
  62  	public function side_load_images( $post_id, $content = '' ) {
  63          $content = wp_unslash( $content );
  64  
  65          if ( preg_match_all( '/<img [^>]+>/', $content, $matches ) && current_user_can( 'upload_files' ) ) {
  66              foreach ( (array) $matches[0] as $image ) {
  67                  // This is inserted from our JS so HTML attributes should always be in double quotes.
  68                  if ( ! preg_match( '/src="([^"]+)"/', $image, $url_matches ) ) {
  69                      continue;
  70                  }
  71  
  72                  $image_src = $url_matches[1];
  73  
  74                  // Don't try to sideload a file without a file extension, leads to WP upload error.
  75                  if ( ! preg_match( '/[^\?]+\.(?:jpe?g|jpe|gif|png)(?:\?|$)/i', $image_src ) ) {
  76                      continue;
  77                  }
  78  
  79                  // Sideload image, which gives us a new image src.
  80                  $new_src = media_sideload_image( $image_src, $post_id, null, 'src' );
  81  
  82                  if ( ! is_wp_error( $new_src ) ) {
  83                      // Replace the POSTED content <img> with correct uploaded ones.
  84                      // Need to do it in two steps so we don't replace links to the original image if any.
  85                      $new_image = str_replace( $image_src, $new_src, $image );
  86                      $content = str_replace( $image, $new_image, $content );
  87                  }
  88              }
  89          }
  90  
  91          // Expected slashed
  92          return wp_slash( $content );
  93      }
  94  
  95      /**
  96       * Ajax handler for saving the post as draft or published.
  97       *
  98       * @since 4.2.0
  99       */
 100  	public function save_post() {
 101          if ( empty( $_POST['post_ID'] ) || ! $post_id = (int) $_POST['post_ID'] ) {
 102              wp_send_json_error( array( 'errorMessage' => __( 'Missing post ID.' ) ) );
 103          }
 104  
 105          if ( empty( $_POST['_wpnonce'] ) || ! wp_verify_nonce( $_POST['_wpnonce'], 'update-post_' . $post_id ) ||
 106              ! current_user_can( 'edit_post', $post_id ) ) {
 107  
 108              wp_send_json_error( array( 'errorMessage' => __( 'Invalid post.' ) ) );
 109          }
 110  
 111          $post_data = array(
 112              'ID'            => $post_id,
 113              'post_title'    => ( ! empty( $_POST['post_title'] ) ) ? sanitize_text_field( trim( $_POST['post_title'] ) ) : '',
 114              'post_content'  => ( ! empty( $_POST['post_content'] ) ) ? trim( $_POST['post_content'] ) : '',
 115              'post_type'     => 'post',
 116              'post_status'   => 'draft',
 117              'post_format'   => ( ! empty( $_POST['post_format'] ) ) ? sanitize_text_field( $_POST['post_format'] ) : '',
 118          );
 119  
 120          // Only accept categories if the user actually can assign
 121          $category_tax = get_taxonomy( 'category' );
 122          if ( current_user_can( $category_tax->cap->assign_terms ) ) {
 123              $post_data['post_category'] = ( ! empty( $_POST['post_category'] ) ) ? $_POST['post_category'] : array();
 124          }
 125  
 126          // Only accept taxonomies if the user can actually assign
 127          if ( ! empty( $_POST['tax_input'] ) ) {
 128              $tax_input = $_POST['tax_input'];
 129              foreach ( $tax_input as $tax => $_ti ) {
 130                  $tax_object = get_taxonomy( $tax );
 131                  if ( ! $tax_object || ! current_user_can( $tax_object->cap->assign_terms ) ) {
 132                      unset( $tax_input[ $tax ] );
 133                  }
 134              }
 135  
 136              $post_data['tax_input'] = $tax_input;
 137          }
 138  
 139          // Toggle status to pending if user cannot actually publish
 140          if ( ! empty( $_POST['post_status'] ) && 'publish' === $_POST['post_status'] ) {
 141              if ( current_user_can( 'publish_posts' ) ) {
 142                  $post_data['post_status'] = 'publish';
 143              } else {
 144                  $post_data['post_status'] = 'pending';
 145              }
 146          }
 147  
 148          $post_data['post_content'] = $this->side_load_images( $post_id, $post_data['post_content'] );
 149  
 150          /**
 151           * Filters the post data of a Press This post before saving/updating.
 152           *
 153           * The {@see 'side_load_images'} action has already run at this point.
 154           *
 155           * @since 4.5.0
 156           *
 157           * @param array $post_data The post data.
 158           */
 159          $post_data = apply_filters( 'press_this_save_post', $post_data );
 160  
 161          $updated = wp_update_post( $post_data, true );
 162  
 163          if ( is_wp_error( $updated ) ) {
 164              wp_send_json_error( array( 'errorMessage' => $updated->get_error_message() ) );
 165          } else {
 166              if ( isset( $post_data['post_format'] ) ) {
 167                  if ( current_theme_supports( 'post-formats', $post_data['post_format'] ) ) {
 168                      set_post_format( $post_id, $post_data['post_format'] );
 169                  } elseif ( $post_data['post_format'] ) {
 170                      set_post_format( $post_id, false );
 171                  }
 172              }
 173  
 174              $forceRedirect = false;
 175  
 176              if ( 'publish' === get_post_status( $post_id ) ) {
 177                  $redirect = get_post_permalink( $post_id );
 178              } elseif ( isset( $_POST['pt-force-redirect'] ) && $_POST['pt-force-redirect'] === 'true' ) {
 179                  $forceRedirect = true;
 180                  $redirect = get_edit_post_link( $post_id, 'js' );
 181              } else {
 182                  $redirect = false;
 183              }
 184  
 185              /**
 186               * Filters the URL to redirect to when Press This saves.
 187               *
 188               * @since 4.2.0
 189               *
 190               * @param string $url     Redirect URL. If `$status` is 'publish', this will be the post permalink.
 191               *                        Otherwise, the default is false resulting in no redirect.
 192               * @param int    $post_id Post ID.
 193               * @param string $status  Post status.
 194               */
 195              $redirect = apply_filters( 'press_this_save_redirect', $redirect, $post_id, $post_data['post_status'] );
 196  
 197              if ( $redirect ) {
 198                  wp_send_json_success( array( 'redirect' => $redirect, 'force' => $forceRedirect ) );
 199              } else {
 200                  wp_send_json_success( array( 'postSaved' => true ) );
 201              }
 202          }
 203      }
 204  
 205      /**
 206       * Ajax handler for adding a new category.
 207       *
 208       * @since 4.2.0
 209       */
 210  	public function add_category() {
 211          if ( false === wp_verify_nonce( $_POST['new_cat_nonce'], 'add-category' ) ) {
 212              wp_send_json_error();
 213          }
 214  
 215          $taxonomy = get_taxonomy( 'category' );
 216  
 217          if ( ! current_user_can( $taxonomy->cap->edit_terms ) || empty( $_POST['name'] ) ) {
 218              wp_send_json_error();
 219          }
 220  
 221          $parent = isset( $_POST['parent'] ) && (int) $_POST['parent'] > 0 ? (int) $_POST['parent'] : 0;
 222          $names = explode( ',', $_POST['name'] );
 223          $added = $data = array();
 224  
 225          foreach ( $names as $cat_name ) {
 226              $cat_name = trim( $cat_name );
 227              $cat_nicename = sanitize_title( $cat_name );
 228  
 229              if ( empty( $cat_nicename ) ) {
 230                  continue;
 231              }
 232  
 233              // @todo Find a more performant way to check existence, maybe get_term() with a separate parent check.
 234              if ( term_exists( $cat_name, $taxonomy->name, $parent ) ) {
 235                  if ( count( $names ) === 1 ) {
 236                      wp_send_json_error( array( 'errorMessage' => __( 'This category already exists.' ) ) );
 237                  } else {
 238                      continue;
 239                  }
 240              }
 241  
 242              $cat_id = wp_insert_term( $cat_name, $taxonomy->name, array( 'parent' => $parent ) );
 243  
 244              if ( is_wp_error( $cat_id ) ) {
 245                  continue;
 246              } elseif ( is_array( $cat_id ) ) {
 247                  $cat_id = $cat_id['term_id'];
 248              }
 249  
 250              $added[] = $cat_id;
 251          }
 252  
 253          if ( empty( $added ) ) {
 254              wp_send_json_error( array( 'errorMessage' => __( 'This category cannot be added. Please change the name and try again.' ) ) );
 255          }
 256  
 257          foreach ( $added as $new_cat_id ) {
 258              $new_cat = get_category( $new_cat_id );
 259  
 260              if ( is_wp_error( $new_cat ) ) {
 261                  wp_send_json_error( array( 'errorMessage' => __( 'Error while adding the category. Please try again later.' ) ) );
 262              }
 263  
 264              $data[] = array(
 265                  'term_id' => $new_cat->term_id,
 266                  'name' => $new_cat->name,
 267                  'parent' => $new_cat->parent,
 268              );
 269          }
 270          wp_send_json_success( $data );
 271      }
 272  
 273      /**
 274       * Downloads the source's HTML via server-side call for the given URL.
 275       *
 276       * @since 4.2.0
 277       *
 278       * @param string $url URL to scan.
 279       * @return string Source's HTML sanitized markup
 280       */
 281  	public function fetch_source_html( $url ) {
 282          if ( empty( $url ) ) {
 283              return new WP_Error( 'invalid-url', __( 'A valid URL was not provided.' ) );
 284          }
 285  
 286          $remote_url = wp_safe_remote_get( $url, array(
 287              'timeout' => 30,
 288              // Use an explicit user-agent for Press This
 289              'user-agent' => 'Press This (WordPress/' . get_bloginfo( 'version' ) . '); ' . get_bloginfo( 'url' )
 290          ) );
 291  
 292          if ( is_wp_error( $remote_url ) ) {
 293              return $remote_url;
 294          }
 295  
 296          $allowed_elements = array(
 297              'img' => array(
 298                  'src'      => true,
 299                  'width'    => true,
 300                  'height'   => true,
 301              ),
 302              'iframe' => array(
 303                  'src'      => true,
 304              ),
 305              'link' => array(
 306                  'rel'      => true,
 307                  'itemprop' => true,
 308                  'href'     => true,
 309              ),
 310              'meta' => array(
 311                  'property' => true,
 312                  'name'     => true,
 313                  'content'  => true,
 314              )
 315          );
 316  
 317          $source_content = wp_remote_retrieve_body( $remote_url );
 318          $source_content = wp_kses( $source_content, $allowed_elements );
 319  
 320          return $source_content;
 321      }
 322  
 323      /**
 324       * Utility method to limit an array to 50 values.
 325       *
 326       * @ignore
 327       * @since 4.2.0
 328       *
 329       * @param array $value Array to limit.
 330       * @return array Original array if fewer than 50 values, limited array, empty array otherwise.
 331       */
 332  	private function _limit_array( $value ) {
 333          if ( is_array( $value ) ) {
 334              if ( count( $value ) > 50 ) {
 335                  return array_slice( $value, 0, 50 );
 336              }
 337  
 338              return $value;
 339          }
 340  
 341          return array();
 342      }
 343  
 344      /**
 345       * Utility method to limit the length of a given string to 5,000 characters.
 346       *
 347       * @ignore
 348       * @since 4.2.0
 349       *
 350       * @param string $value String to limit.
 351       * @return bool|int|string If boolean or integer, that value. If a string, the original value
 352       *                         if fewer than 5,000 characters, a truncated version, otherwise an
 353       *                         empty string.
 354       */
 355  	private function _limit_string( $value ) {
 356          $return = '';
 357  
 358          if ( is_numeric( $value ) || is_bool( $value ) ) {
 359              $return = $value;
 360          } else if ( is_string( $value ) ) {
 361              if ( mb_strlen( $value ) > 5000 ) {
 362                  $return = mb_substr( $value, 0, 5000 );
 363              } else {
 364                  $return = $value;
 365              }
 366  
 367              $return = html_entity_decode( $return, ENT_QUOTES, 'UTF-8' );
 368              $return = sanitize_text_field( trim( $return ) );
 369          }
 370  
 371          return $return;
 372      }
 373  
 374      /**
 375       * Utility method to limit a given URL to 2,048 characters.
 376       *
 377       * @ignore
 378       * @since 4.2.0
 379       *
 380       * @param string $url URL to check for length and validity.
 381       * @return string Escaped URL if of valid length (< 2048) and makeup. Empty string otherwise.
 382       */
 383  	private function _limit_url( $url ) {
 384          if ( ! is_string( $url ) ) {
 385              return '';
 386          }
 387  
 388          // HTTP 1.1 allows 8000 chars but the "de-facto" standard supported in all current browsers is 2048.
 389          if ( strlen( $url ) > 2048 ) {
 390              return ''; // Return empty rather than a truncated/invalid URL
 391          }
 392  
 393          // Does not look like a URL.
 394          if ( ! preg_match( '/^([!#$&-;=?-\[\]_a-z~]|%[0-9a-fA-F]{2})+$/', $url ) ) {
 395              return '';
 396          }
 397  
 398          // If the URL is root-relative, prepend the protocol and domain name
 399          if ( $url && $this->domain && preg_match( '%^/[^/]+%', $url ) ) {
 400              $url = $this->domain . $url;
 401          }
 402  
 403          // Not absolute or protocol-relative URL.
 404          if ( ! preg_match( '%^(?:https?:)?//[^/]+%', $url ) ) {
 405              return '';
 406          }
 407  
 408          return esc_url_raw( $url, array( 'http', 'https' ) );
 409      }
 410  
 411      /**
 412       * Utility method to limit image source URLs.
 413       *
 414       * Excluded URLs include share-this type buttons, loaders, spinners, spacers, WordPress interface images,
 415       * tiny buttons or thumbs, mathtag.com or quantserve.com images, or the WordPress.com stats gif.
 416       *
 417       * @ignore
 418       * @since 4.2.0
 419       *
 420       * @param string $src Image source URL.
 421       * @return string If not matched an excluded URL type, the original URL, empty string otherwise.
 422       */
 423  	private function _limit_img( $src ) {
 424          $src = $this->_limit_url( $src );
 425  
 426          if ( preg_match( '!/ad[sx]?/!i', $src ) ) {
 427              // Ads
 428              return '';
 429          } else if ( preg_match( '!(/share-?this[^.]+?\.[a-z0-9]{3,4})(\?.*)?$!i', $src ) ) {
 430              // Share-this type button
 431              return '';
 432          } else if ( preg_match( '!/(spinner|loading|spacer|blank|rss)\.(gif|jpg|png)!i', $src ) ) {
 433              // Loaders, spinners, spacers
 434              return '';
 435          } else if ( preg_match( '!/([^./]+[-_])?(spinner|loading|spacer|blank)s?([-_][^./]+)?\.[a-z0-9]{3,4}!i', $src ) ) {
 436              // Fancy loaders, spinners, spacers
 437              return '';
 438          } else if ( preg_match( '!([^./]+[-_])?thumb[^.]*\.(gif|jpg|png)$!i', $src ) ) {
 439              // Thumbnails, too small, usually irrelevant to context
 440              return '';
 441          } else if ( false !== stripos( $src, '/wp-includes/' ) ) {
 442              // Classic WordPress interface images
 443              return '';
 444          } else if ( preg_match( '![^\d]\d{1,2}x\d+\.(gif|jpg|png)$!i', $src ) ) {
 445              // Most often tiny buttons/thumbs (< 100px wide)
 446              return '';
 447          } else if ( preg_match( '!/pixel\.(mathtag|quantserve)\.com!i', $src ) ) {
 448              // See mathtag.com and https://www.quantcast.com/how-we-do-it/iab-standard-measurement/how-we-collect-data/
 449              return '';
 450          } else if ( preg_match( '!/[gb]\.gif(\?.+)?$!i', $src ) ) {
 451              // WordPress.com stats gif
 452              return '';
 453          }
 454  
 455          return $src;
 456      }
 457  
 458      /**
 459       * Limit embed source URLs to specific providers.
 460       *
 461       * Not all core oEmbed providers are supported. Supported providers include YouTube, Vimeo,
 462       * Daily Motion, SoundCloud, and Twitter.
 463       *
 464       * @ignore
 465       * @since 4.2.0
 466       *
 467       * @param string $src Embed source URL.
 468       * @return string If not from a supported provider, an empty string. Otherwise, a reformatted embed URL.
 469       */
 470  	private function _limit_embed( $src ) {
 471          $src = $this->_limit_url( $src );
 472  
 473          if ( empty( $src ) )
 474              return '';
 475  
 476          if ( preg_match( '!//(m|www)\.youtube\.com/(embed|v)/([^?]+)\?.+$!i', $src, $src_matches ) ) {
 477              // Embedded Youtube videos (www or mobile)
 478              $src = 'https://www.youtube.com/watch?v=' . $src_matches[3];
 479          } else if ( preg_match( '!//player\.vimeo\.com/video/([\d]+)([?/].*)?$!i', $src, $src_matches ) ) {
 480              // Embedded Vimeo iframe videos
 481              $src = 'https://vimeo.com/' . (int) $src_matches[1];
 482          } else if ( preg_match( '!//vimeo\.com/moogaloop\.swf\?clip_id=([\d]+)$!i', $src, $src_matches ) ) {
 483              // Embedded Vimeo Flash videos
 484              $src = 'https://vimeo.com/' . (int) $src_matches[1];
 485          } else if ( preg_match( '!//(www\.)?dailymotion\.com/embed/video/([^/?]+)([/?].+)?!i', $src, $src_matches ) ) {
 486              // Embedded Daily Motion videos
 487              $src = 'https://www.dailymotion.com/video/' . $src_matches[2];
 488          } else {
 489              $oembed = _wp_oembed_get_object();
 490  
 491              if ( ! $oembed->get_provider( $src, array( 'discover' => false ) ) ) {
 492                  $src = '';
 493              }
 494          }
 495  
 496          return $src;
 497      }
 498  
 499      /**
 500       * Process a meta data entry from the source.
 501       *
 502       * @ignore
 503       * @since 4.2.0
 504       *
 505       * @param string $meta_name  Meta key name.
 506       * @param mixed  $meta_value Meta value.
 507       * @param array  $data       Associative array of source data.
 508       * @return array Processed data array.
 509       */
 510  	private function _process_meta_entry( $meta_name, $meta_value, $data ) {
 511          if ( preg_match( '/:?(title|description|keywords|site_name)$/', $meta_name ) ) {
 512              $data['_meta'][ $meta_name ] = $meta_value;
 513          } else {
 514              switch ( $meta_name ) {
 515                  case 'og:url':
 516                  case 'og:video':
 517                  case 'og:video:secure_url':
 518                      $meta_value = $this->_limit_embed( $meta_value );
 519  
 520                      if ( ! isset( $data['_embeds'] ) ) {
 521                          $data['_embeds'] = array();
 522                      }
 523  
 524                      if ( ! empty( $meta_value ) && ! in_array( $meta_value, $data['_embeds'] ) ) {
 525                          $data['_embeds'][] = $meta_value;
 526                      }
 527  
 528                      break;
 529                  case 'og:image':
 530                  case 'og:image:secure_url':
 531                  case 'twitter:image0:src':
 532                  case 'twitter:image0':
 533                  case 'twitter:image:src':
 534                  case 'twitter:image':
 535                      $meta_value = $this->_limit_img( $meta_value );
 536  
 537                      if ( ! isset( $data['_images'] ) ) {
 538                          $data['_images'] = array();
 539                      }
 540  
 541                      if ( ! empty( $meta_value ) && ! in_array( $meta_value, $data['_images'] ) ) {
 542                          $data['_images'][] = $meta_value;
 543                      }
 544  
 545                      break;
 546              }
 547          }
 548  
 549          return $data;
 550      }
 551  
 552      /**
 553       * Fetches and parses _meta, _images, and _links data from the source.
 554       *
 555       * @since 4.2.0
 556       *
 557       * @param string $url  URL to scan.
 558       * @param array  $data Optional. Existing data array if you have one. Default empty array.
 559       * @return array New data array.
 560       */
 561  	public function source_data_fetch_fallback( $url, $data = array() ) {
 562          if ( empty( $url ) ) {
 563              return array();
 564          }
 565  
 566          // Download source page to tmp file.
 567          $source_content = $this->fetch_source_html( $url );
 568          if ( is_wp_error( $source_content ) ) {
 569              return array( 'errors' => $source_content->get_error_messages() );
 570          }
 571  
 572          // Fetch and gather <meta> data first, so discovered media is offered 1st to user.
 573          if ( empty( $data['_meta'] ) ) {
 574              $data['_meta'] = array();
 575          }
 576  
 577          if ( preg_match_all( '/<meta [^>]+>/', $source_content, $matches ) ) {
 578              $items = $this->_limit_array( $matches[0] );
 579  
 580              foreach ( $items as $value ) {
 581                  if ( preg_match( '/(property|name)="([^"]+)"[^>]+content="([^"]+)"/', $value, $new_matches ) ) {
 582                      $meta_name  = $this->_limit_string( $new_matches[2] );
 583                      $meta_value = $this->_limit_string( $new_matches[3] );
 584  
 585                      // Sanity check. $key is usually things like 'title', 'description', 'keywords', etc.
 586                      if ( strlen( $meta_name ) > 100 ) {
 587                          continue;
 588                      }
 589  
 590                      $data = $this->_process_meta_entry( $meta_name, $meta_value, $data );
 591                  }
 592              }
 593          }
 594  
 595          // Fetch and gather <img> data.
 596          if ( empty( $data['_images'] ) ) {
 597              $data['_images'] = array();
 598          }
 599  
 600          if ( preg_match_all( '/<img [^>]+>/', $source_content, $matches ) ) {
 601              $items = $this->_limit_array( $matches[0] );
 602  
 603              foreach ( $items as $value ) {
 604                  if ( ( preg_match( '/width=(\'|")(\d+)\\1/i', $value, $new_matches ) && $new_matches[2] < 256 ) ||
 605                      ( preg_match( '/height=(\'|")(\d+)\\1/i', $value, $new_matches ) && $new_matches[2] < 128 ) ) {
 606  
 607                      continue;
 608                  }
 609  
 610                  if ( preg_match( '/src=(\'|")([^\'"]+)\\1/i', $value, $new_matches ) ) {
 611                      $src = $this->_limit_img( $new_matches[2] );
 612                      if ( ! empty( $src ) && ! in_array( $src, $data['_images'] ) ) {
 613                          $data['_images'][] = $src;
 614                      }
 615                  }
 616              }
 617          }
 618  
 619          // Fetch and gather <iframe> data.
 620          if ( empty( $data['_embeds'] ) ) {
 621              $data['_embeds'] = array();
 622          }
 623  
 624          if ( preg_match_all( '/<iframe [^>]+>/', $source_content, $matches ) ) {
 625              $items = $this->_limit_array( $matches[0] );
 626  
 627              foreach ( $items as $value ) {
 628                  if ( preg_match( '/src=(\'|")([^\'"]+)\\1/', $value, $new_matches ) ) {
 629                      $src = $this->_limit_embed( $new_matches[2] );
 630  
 631                      if ( ! empty( $src ) && ! in_array( $src, $data['_embeds'] ) ) {
 632                          $data['_embeds'][] = $src;
 633                      }
 634                  }
 635              }
 636          }
 637  
 638          // Fetch and gather <link> data.
 639          if ( empty( $data['_links'] ) ) {
 640              $data['_links'] = array();
 641          }
 642  
 643          if ( preg_match_all( '/<link [^>]+>/', $source_content, $matches ) ) {
 644              $items = $this->_limit_array( $matches[0] );
 645  
 646              foreach ( $items as $value ) {
 647                  if ( preg_match( '/rel=["\'](canonical|shortlink|icon)["\']/i', $value, $matches_rel ) && preg_match( '/href=[\'"]([^\'" ]+)[\'"]/i', $value, $matches_url ) ) {
 648                      $rel = $matches_rel[1];
 649                      $url = $this->_limit_url( $matches_url[1] );
 650  
 651                      if ( ! empty( $url ) && empty( $data['_links'][ $rel ] ) ) {
 652                          $data['_links'][ $rel ] = $url;
 653                      }
 654                  }
 655              }
 656          }
 657  
 658          return $data;
 659      }
 660  
 661      /**
 662       * Handles backward-compat with the legacy version of Press This by supporting its query string params.
 663       *
 664       * @since 4.2.0
 665       *
 666       * @return array
 667       */
 668  	public function merge_or_fetch_data() {
 669          // Get data from $_POST and $_GET, as appropriate ($_POST > $_GET), to remain backward compatible.
 670          $data = array();
 671  
 672          // Only instantiate the keys we want. Sanity check and sanitize each one.
 673          foreach ( array( 'u', 's', 't', 'v' ) as $key ) {
 674              if ( ! empty( $_POST[ $key ] ) ) {
 675                  $value = wp_unslash( $_POST[ $key ] );
 676              } else if ( ! empty( $_GET[ $key ] ) ) {
 677                  $value = wp_unslash( $_GET[ $key ] );
 678              } else {
 679                  continue;
 680              }
 681  
 682              if ( 'u' === $key ) {
 683                  $value = $this->_limit_url( $value );
 684  
 685                  if ( preg_match( '%^(?:https?:)?//[^/]+%i', $value, $domain_match ) ) {
 686                      $this->domain = $domain_match[0];
 687                  }
 688              } else {
 689                  $value = $this->_limit_string( $value );
 690              }
 691  
 692              if ( ! empty( $value ) ) {
 693                  $data[ $key ] = $value;
 694              }
 695          }
 696  
 697          /**
 698           * Filters whether to enable in-source media discovery in Press This.
 699           *
 700           * @since 4.2.0
 701           *
 702           * @param bool $enable Whether to enable media discovery.
 703           */
 704          if ( apply_filters( 'enable_press_this_media_discovery', true ) ) {
 705              /*
 706               * If no title, _images, _embed, and _meta was passed via $_POST, fetch data from source as fallback,
 707               * making PT fully backward compatible with the older bookmarklet.
 708               */
 709              if ( empty( $_POST ) && ! empty( $data['u'] ) ) {
 710                  if ( isset( $_GET['_wpnonce'] ) && wp_verify_nonce( $_GET['_wpnonce'], 'scan-site' ) ) {
 711                      $data = $this->source_data_fetch_fallback( $data['u'], $data );
 712                  } else {
 713                      $data['errors'] = 'missing nonce';
 714                  }
 715              } else {
 716                  foreach ( array( '_images', '_embeds' ) as $type ) {
 717                      if ( empty( $_POST[ $type ] ) ) {
 718                          continue;
 719                      }
 720  
 721                      $data[ $type ] = array();
 722                      $items = $this->_limit_array( $_POST[ $type ] );
 723  
 724                      foreach ( $items as $key => $value ) {
 725                          if ( $type === '_images' ) {
 726                              $value = $this->_limit_img( wp_unslash( $value ) );
 727                          } else {
 728                              $value = $this->_limit_embed( wp_unslash( $value ) );
 729                          }
 730  
 731                          if ( ! empty( $value ) ) {
 732                              $data[ $type ][] = $value;
 733                          }
 734                      }
 735                  }
 736  
 737                  foreach ( array( '_meta', '_links' ) as $type ) {
 738                      if ( empty( $_POST[ $type ] ) ) {
 739                          continue;
 740                      }
 741  
 742                      $data[ $type ] = array();
 743                      $items = $this->_limit_array( $_POST[ $type ] );
 744  
 745                      foreach ( $items as $key => $value ) {
 746                          // Sanity check. These are associative arrays, $key is usually things like 'title', 'description', 'keywords', etc.
 747                          if ( empty( $key ) || strlen( $key ) > 100 ) {
 748                              continue;
 749                          }
 750  
 751                          if ( $type === '_meta' ) {
 752                              $value = $this->_limit_string( wp_unslash( $value ) );
 753  
 754                              if ( ! empty( $value ) ) {
 755                                  $data = $this->_process_meta_entry( $key, $value, $data );
 756                              }
 757                          } else {
 758                              if ( in_array( $key, array( 'canonical', 'shortlink', 'icon' ), true ) ) {
 759                                  $data[ $type ][ $key ] = $this->_limit_url( wp_unslash( $value ) );
 760                              }
 761                          }
 762                      }
 763                  }
 764              }
 765  
 766              // Support passing a single image src as `i`
 767              if ( ! empty( $_REQUEST['i'] ) && ( $img_src = $this->_limit_img( wp_unslash( $_REQUEST['i'] ) ) ) ) {
 768                  if ( empty( $data['_images'] ) ) {
 769                      $data['_images'] = array( $img_src );
 770                  } elseif ( ! in_array( $img_src, $data['_images'], true ) ) {
 771                      array_unshift( $data['_images'], $img_src );
 772                  }
 773              }
 774          }
 775  
 776          /**
 777           * Filters the Press This data array.
 778           *
 779           * @since 4.2.0
 780           *
 781           * @param array $data Press This Data array.
 782           */
 783          return apply_filters( 'press_this_data', $data );
 784      }
 785  
 786      /**
 787       * Adds another stylesheet inside TinyMCE.
 788       *
 789       * @since 4.2.0
 790       *
 791       * @param string $styles URL to editor stylesheet.
 792       * @return string Possibly modified stylesheets list.
 793       */
 794  	public function add_editor_style( $styles ) {
 795          if ( ! empty( $styles ) ) {
 796              $styles .= ',';
 797          }
 798  
 799          $press_this = admin_url( 'css/press-this-editor.css' );
 800          if ( is_rtl() ) {
 801              $press_this = str_replace( '.css', '-rtl.css', $press_this );
 802          }
 803  
 804          return $styles . $press_this;
 805      }
 806  
 807      /**
 808       * Outputs the post format selection HTML.
 809       *
 810       * @since 4.2.0
 811       *
 812       * @param WP_Post $post Post object.
 813       */
 814  	public function post_formats_html( $post ) {
 815          if ( current_theme_supports( 'post-formats' ) && post_type_supports( $post->post_type, 'post-formats' ) ) {
 816              $post_formats = get_theme_support( 'post-formats' );
 817  
 818              if ( is_array( $post_formats[0] ) ) {
 819                  $post_format = get_post_format( $post->ID );
 820  
 821                  if ( ! $post_format ) {
 822                      $post_format = '0';
 823                  }
 824  
 825                  // Add in the current one if it isn't there yet, in case the current theme doesn't support it.
 826                  if ( $post_format && ! in_array( $post_format, $post_formats[0] ) ) {
 827                      $post_formats[0][] = $post_format;
 828                  }
 829  
 830                  ?>
 831                  <div id="post-formats-select">
 832                  <fieldset><legend class="screen-reader-text"><?php _e( 'Post Formats' ); ?></legend>
 833                      <input type="radio" name="post_format" class="post-format" id="post-format-0" value="0" <?php checked( $post_format, '0' ); ?> />
 834                      <label for="post-format-0" class="post-format-icon post-format-standard"><?php echo get_post_format_string( 'standard' ); ?></label>
 835                      <?php
 836  
 837                      foreach ( $post_formats[0] as $format ) {
 838                          $attr_format = esc_attr( $format );
 839                          ?>
 840                          <br />
 841                          <input type="radio" name="post_format" class="post-format" id="post-format-<?php echo $attr_format; ?>" value="<?php echo $attr_format; ?>" <?php checked( $post_format, $format ); ?> />
 842                          <label for="post-format-<?php echo $attr_format ?>" class="post-format-icon post-format-<?php echo $attr_format; ?>"><?php echo esc_html( get_post_format_string( $format ) ); ?></label>
 843                          <?php
 844                       }
 845  
 846                       ?>
 847                  </fieldset>
 848                  </div>
 849                  <?php
 850              }
 851          }
 852      }
 853  
 854      /**
 855       * Outputs the categories HTML.
 856       *
 857       * @since 4.2.0
 858       *
 859       * @param WP_Post $post Post object.
 860       */
 861  	public function categories_html( $post ) {
 862          $taxonomy = get_taxonomy( 'category' );
 863  
 864          // Bail if user cannot assign terms
 865          if ( ! current_user_can( $taxonomy->cap->assign_terms ) ) {
 866              return;
 867          }
 868  
 869          // Only show "add" if user can edit terms
 870          if ( current_user_can( $taxonomy->cap->edit_terms ) ) {
 871              ?>
 872              <button type="button" class="add-cat-toggle button-link" aria-expanded="false">
 873                  <span class="dashicons dashicons-plus"></span><span class="screen-reader-text"><?php _e( 'Toggle add category' ); ?></span>
 874              </button>
 875              <div class="add-category is-hidden">
 876                  <label class="screen-reader-text" for="new-category"><?php echo $taxonomy->labels->add_new_item; ?></label>
 877                  <input type="text" id="new-category" class="add-category-name" placeholder="<?php echo esc_attr( $taxonomy->labels->new_item_name ); ?>" value="" aria-required="true">
 878                  <label class="screen-reader-text" for="new-category-parent"><?php echo $taxonomy->labels->parent_item_colon; ?></label>
 879                  <div class="postform-wrapper">
 880                      <?php
 881                      wp_dropdown_categories( array(
 882                          'taxonomy'         => 'category',
 883                          'hide_empty'       => 0,
 884                          'name'             => 'new-category-parent',
 885                          'orderby'          => 'name',
 886                          'hierarchical'     => 1,
 887                          'show_option_none' => '&mdash; ' . $taxonomy->labels->parent_item . ' &mdash;'
 888                      ) );
 889                      ?>
 890                  </div>
 891                  <button type="button" class="add-cat-submit"><?php _e( 'Add' ); ?></button>
 892              </div>
 893              <?php
 894  
 895          }
 896          ?>
 897          <div class="categories-search-wrapper">
 898              <input id="categories-search" type="search" class="categories-search" placeholder="<?php esc_attr_e( 'Search categories by name' ) ?>">
 899              <label for="categories-search">
 900                  <span class="dashicons dashicons-search"></span><span class="screen-reader-text"><?php _e( 'Search categories' ); ?></span>
 901              </label>
 902          </div>
 903          <div aria-label="<?php esc_attr_e( 'Categories' ); ?>">
 904              <ul class="categories-select">
 905                  <?php wp_terms_checklist( $post->ID, array( 'taxonomy' => 'category', 'list_only' => true ) ); ?>
 906              </ul>
 907          </div>
 908          <?php
 909      }
 910  
 911      /**
 912       * Outputs the tags HTML.
 913       *
 914       * @since 4.2.0
 915       *
 916       * @param WP_Post $post Post object.
 917       */
 918  	public function tags_html( $post ) {
 919          $taxonomy              = get_taxonomy( 'post_tag' );
 920          $user_can_assign_terms = current_user_can( $taxonomy->cap->assign_terms );
 921          $esc_tags              = get_terms_to_edit( $post->ID, 'post_tag' );
 922  
 923          if ( ! $esc_tags || is_wp_error( $esc_tags ) ) {
 924              $esc_tags = '';
 925          }
 926  
 927          ?>
 928          <div class="tagsdiv" id="post_tag">
 929              <div class="jaxtag">
 930              <input type="hidden" name="tax_input[post_tag]" class="the-tags" value="<?php echo $esc_tags; // escaped in get_terms_to_edit() ?>">
 931               <?php
 932  
 933              if ( $user_can_assign_terms ) {
 934                  ?>
 935                  <div class="ajaxtag hide-if-no-js">
 936                      <label class="screen-reader-text" for="new-tag-post_tag"><?php _e( 'Tags' ); ?></label>
 937                      <p>
 938                          <input type="text" id="new-tag-post_tag" name="newtag[post_tag]" class="newtag form-input-tip" size="16" autocomplete="off" value="" aria-describedby="new-tag-desc" />
 939                          <button type="button" class="tagadd"><?php _e( 'Add' ); ?></button>
 940                      </p>
 941                  </div>
 942                  <p class="howto" id="new-tag-desc">
 943                      <?php echo $taxonomy->labels->separate_items_with_commas; ?>
 944                  </p>
 945                  <?php
 946              }
 947  
 948              ?>
 949              </div>
 950              <ul class="tagchecklist" role="list"></ul>
 951          </div>
 952          <?php
 953  
 954          if ( $user_can_assign_terms ) {
 955              ?>
 956              <button type="button" class="button-link tagcloud-link" id="link-post_tag" aria-expanded="false"><?php echo $taxonomy->labels->choose_from_most_used; ?></button>
 957              <?php
 958          }
 959      }
 960  
 961      /**
 962       * Get a list of embeds with no duplicates.
 963       *
 964       * @since 4.2.0
 965       *
 966       * @param array $data The site's data.
 967       * @return array Embeds selected to be available.
 968       */
 969  	public function get_embeds( $data ) {
 970          $selected_embeds = array();
 971  
 972          // Make sure to add the Pressed page if it's a valid oembed itself
 973          if ( ! empty ( $data['u'] ) && $this->_limit_embed( $data['u'] ) ) {
 974              $data['_embeds'][] = $data['u'];
 975          }
 976  
 977          if ( ! empty( $data['_embeds'] ) ) {
 978              foreach ( $data['_embeds'] as $src ) {
 979                  $prot_relative_src = preg_replace( '/^https?:/', '', $src );
 980  
 981                  if ( in_array( $prot_relative_src, $this->embeds ) ) {
 982                      continue;
 983                  }
 984  
 985                  $selected_embeds[] = $src;
 986                  $this->embeds[] = $prot_relative_src;
 987              }
 988          }
 989  
 990          return $selected_embeds;
 991      }
 992  
 993      /**
 994       * Get a list of images with no duplicates.
 995       *
 996       * @since 4.2.0
 997       *
 998       * @param array $data The site's data.
 999       * @return array
1000       */
1001  	public function get_images( $data ) {
1002          $selected_images = array();
1003  
1004          if ( ! empty( $data['_images'] ) ) {
1005              foreach ( $data['_images'] as $src ) {
1006                  if ( false !== strpos( $src, 'gravatar.com' ) ) {
1007                      $src = preg_replace( '%http://[\d]+\.gravatar\.com/%', 'https://secure.gravatar.com/', $src );
1008                  }
1009  
1010                  $prot_relative_src = preg_replace( '/^https?:/', '', $src );
1011  
1012                  if ( in_array( $prot_relative_src, $this->images ) ||
1013                      ( false !== strpos( $src, 'avatar' ) && count( $this->images ) > 15 ) ) {
1014                      // Skip: already selected or some type of avatar and we've already gathered more than 15 images.
1015                      continue;
1016                  }
1017  
1018                  $selected_images[] = $src;
1019                  $this->images[] = $prot_relative_src;
1020              }
1021          }
1022  
1023          return $selected_images;
1024      }
1025  
1026      /**
1027       * Gets the source page's canonical link, based on passed location and meta data.
1028       *
1029       * @since 4.2.0
1030       *
1031        * @param array $data The site's data.
1032       * @return string Discovered canonical URL, or empty
1033       */
1034  	public function get_canonical_link( $data ) {
1035          $link = '';
1036  
1037          if ( ! empty( $data['_links']['canonical'] ) ) {
1038              $link = $data['_links']['canonical'];
1039          } elseif ( ! empty( $data['u'] ) ) {
1040              $link = $data['u'];
1041          } elseif ( ! empty( $data['_meta'] ) ) {
1042              if ( ! empty( $data['_meta']['twitter:url'] ) ) {
1043                  $link = $data['_meta']['twitter:url'];
1044              } else if ( ! empty( $data['_meta']['og:url'] ) ) {
1045                  $link = $data['_meta']['og:url'];
1046              }
1047          }
1048  
1049          if ( empty( $link ) && ! empty( $data['_links']['shortlink'] ) ) {
1050              $link = $data['_links']['shortlink'];
1051          }
1052  
1053          return $link;
1054      }
1055  
1056      /**
1057       * Gets the source page's site name, based on passed meta data.
1058       *
1059       * @since 4.2.0
1060       *
1061       * @param array $data The site's data.
1062       * @return string Discovered site name, or empty
1063       */
1064  	public function get_source_site_name( $data ) {
1065          $name = '';
1066  
1067          if ( ! empty( $data['_meta'] ) ) {
1068              if ( ! empty( $data['_meta']['og:site_name'] ) ) {
1069                  $name = $data['_meta']['og:site_name'];
1070              } else if ( ! empty( $data['_meta']['application-name'] ) ) {
1071                  $name = $data['_meta']['application-name'];
1072              }
1073          }
1074  
1075          return $name;
1076      }
1077  
1078      /**
1079       * Gets the source page's title, based on passed title and meta data.
1080       *
1081       * @since 4.2.0
1082       *
1083       * @param array $data The site's data.
1084       * @return string Discovered page title, or empty
1085       */
1086  	public function get_suggested_title( $data ) {
1087          $title = '';
1088  
1089          if ( ! empty( $data['t'] ) ) {
1090              $title = $data['t'];
1091          } elseif ( ! empty( $data['_meta'] ) ) {
1092              if ( ! empty( $data['_meta']['twitter:title'] ) ) {
1093                  $title = $data['_meta']['twitter:title'];
1094              } else if ( ! empty( $data['_meta']['og:title'] ) ) {
1095                  $title = $data['_meta']['og:title'];
1096              } else if ( ! empty( $data['_meta']['title'] ) ) {
1097                  $title = $data['_meta']['title'];
1098              }
1099          }
1100  
1101          return $title;
1102      }
1103  
1104      /**
1105       * Gets the source page's suggested content, based on passed data (description, selection, etc).
1106       *
1107       * Features a blockquoted excerpt, as well as content attribution, if any.
1108       *
1109       * @since 4.2.0
1110       *
1111       * @param array $data The site's data.
1112       * @return string Discovered content, or empty
1113       */
1114  	public function get_suggested_content( $data ) {
1115          $content = $text = '';
1116  
1117          if ( ! empty( $data['s'] ) ) {
1118              $text = $data['s'];
1119          } else if ( ! empty( $data['_meta'] ) ) {
1120              if ( ! empty( $data['_meta']['twitter:description'] ) ) {
1121                  $text = $data['_meta']['twitter:description'];
1122              } else if ( ! empty( $data['_meta']['og:description'] ) ) {
1123                  $text = $data['_meta']['og:description'];
1124              } else if ( ! empty( $data['_meta']['description'] ) ) {
1125                  $text = $data['_meta']['description'];
1126              }
1127  
1128              // If there is an ellipsis at the end, the description is very likely auto-generated. Better to ignore it.
1129              if ( $text && substr( $text, -3 ) === '...' ) {
1130                  $text = '';
1131              }
1132          }
1133  
1134          $default_html = array( 'quote' => '', 'link' => '', 'embed' => '' );
1135  
1136          if ( ! empty( $data['u'] ) && $this->_limit_embed( $data['u'] ) ) {
1137              $default_html['embed'] = '<p>[embed]' . $data['u'] . '[/embed]</p>';
1138  
1139              if ( ! empty( $data['s'] ) ) {
1140                  // If the user has selected some text, do quote it.
1141                  $default_html['quote'] = '<blockquote>%1$s</blockquote>';
1142              }
1143          } else {
1144              $default_html['quote'] = '<blockquote>%1$s</blockquote>';
1145              $default_html['link'] = '<p>' . _x( 'Source:', 'Used in Press This to indicate where the content comes from.' ) .
1146                  ' <em><a href="%1$s">%2$s</a></em></p>';
1147          }
1148  
1149          /**
1150           * Filters the default HTML tags used in the suggested content for the editor.
1151           *
1152           * The HTML strings use printf format. After filtering the content is added at the specified places with `sprintf()`.
1153           *
1154           * @since 4.2.0
1155           *
1156           * @param array $default_html Associative array with three possible keys:
1157           *                                - 'quote' where %1$s is replaced with the site description or the selected content.
1158           *                                - 'link' where %1$s is link href, %2$s is link text, usually the source page title.
1159           *                                - 'embed' which contains an [embed] shortcode when the source page offers embeddable content.
1160           * @param array $data         Associative array containing the data from the source page.
1161           */
1162          $default_html = apply_filters( 'press_this_suggested_html', $default_html, $data );
1163  
1164          if ( ! empty( $default_html['embed'] ) ) {
1165              $content .= $default_html['embed'];
1166          }
1167  
1168          // Wrap suggested content in the specified HTML.
1169          if ( ! empty( $default_html['quote'] ) && $text ) {
1170              $content .= sprintf( $default_html['quote'], $text );
1171          }
1172  
1173          // Add source attribution if there is one available.
1174          if ( ! empty( $default_html['link'] ) ) {
1175              $title = $this->get_suggested_title( $data );
1176              $url = $this->get_canonical_link( $data );
1177  
1178              if ( ! $title ) {
1179                  $title = $this->get_source_site_name( $data );
1180              }
1181  
1182              if ( $url && $title ) {
1183                  $content .= sprintf( $default_html['link'], $url, $title );
1184              }
1185          }
1186  
1187          return $content;
1188      }
1189  
1190      /**
1191       * Serves the app's base HTML, which in turns calls the load script.
1192       *
1193       * @since 4.2.0
1194       *
1195       * @global WP_Locale $wp_locale
1196       * @global bool      $is_IE
1197       */
1198  	public function html() {
1199          global $wp_locale;
1200  
1201          $wp_version = get_bloginfo( 'version' );
1202  
1203          // Get data, new (POST) and old (GET).
1204          $data = $this->merge_or_fetch_data();
1205  
1206          $post_title = $this->get_suggested_title( $data );
1207  
1208          $post_content = $this->get_suggested_content( $data );
1209  
1210          // Get site settings array/data.
1211          $site_settings = $this->site_settings();
1212  
1213          // Pass the images and embeds
1214          $images = $this->get_images( $data );
1215          $embeds = $this->get_embeds( $data );
1216  
1217          $site_data = array(
1218              'v' => ! empty( $data['v'] ) ? $data['v'] : '',
1219              'u' => ! empty( $data['u'] ) ? $data['u'] : '',
1220              'hasData' => ! empty( $data ) && ! isset( $data['errors'] ),
1221          );
1222  
1223          if ( ! empty( $images ) ) {
1224              $site_data['_images'] = $images;
1225          }
1226  
1227          if ( ! empty( $embeds ) ) {
1228              $site_data['_embeds'] = $embeds;
1229          }
1230  
1231          // Add press-this-editor.css and remove theme's editor-style.css, if any.
1232          remove_editor_styles();
1233  
1234          add_filter( 'mce_css', array( $this, 'add_editor_style' ) );
1235  
1236          if ( ! empty( $GLOBALS['is_IE'] ) ) {
1237              @header( 'X-UA-Compatible: IE=edge' );
1238          }
1239  
1240          @header( 'Content-Type: ' . get_option( 'html_type' ) . '; charset=' . get_option( 'blog_charset' ) );
1241  
1242  ?>
1243  <!DOCTYPE html>
1244  <!--[if IE 7]>         <html class="lt-ie9 lt-ie8" <?php language_attributes(); ?>> <![endif]-->
1245  <!--[if IE 8]>         <html class="lt-ie9" <?php language_attributes(); ?>> <![endif]-->
1246  <!--[if gt IE 8]><!--> <html <?php language_attributes(); ?>> <!--<![endif]-->
1247  <head>
1248      <meta http-equiv="Content-Type" content="<?php echo esc_attr( get_bloginfo( 'html_type' ) ); ?>; charset=<?php echo esc_attr( get_option( 'blog_charset' ) ); ?>" />
1249      <meta name="viewport" content="width=device-width">
1250      <title><?php esc_html_e( 'Press This!' ) ?></title>
1251  
1252      <script>
1253          window.wpPressThisData   = <?php echo wp_json_encode( $site_data ); ?>;
1254          window.wpPressThisConfig = <?php echo wp_json_encode( $site_settings ); ?>;
1255      </script>
1256  
1257      <script type="text/javascript">
1258          var ajaxurl = '<?php echo esc_js( admin_url( 'admin-ajax.php', 'relative' ) ); ?>',
1259              pagenow = 'press-this',
1260              typenow = 'post',
1261              adminpage = 'press-this-php',
1262              thousandsSeparator = '<?php echo addslashes( $wp_locale->number_format['thousands_sep'] ); ?>',
1263              decimalPoint = '<?php echo addslashes( $wp_locale->number_format['decimal_point'] ); ?>',
1264              isRtl = <?php echo (int) is_rtl(); ?>;
1265      </script>
1266  
1267      <?php
1268          /*
1269           * $post->ID is needed for the embed shortcode so we can show oEmbed previews in the editor.
1270           * Maybe find a way without it.
1271           */
1272          $post = get_default_post_to_edit( 'post', true );
1273          $post_ID = (int) $post->ID;
1274  
1275          wp_enqueue_media( array( 'post' => $post_ID ) );
1276          wp_enqueue_style( 'press-this' );
1277          wp_enqueue_script( 'press-this' );
1278          wp_enqueue_script( 'json2' );
1279          wp_enqueue_script( 'editor' );
1280  
1281          $categories_tax   = get_taxonomy( 'category' );
1282          $show_categories  = current_user_can( $categories_tax->cap->assign_terms ) || current_user_can( $categories_tax->cap->edit_terms );
1283  
1284          $tag_tax          = get_taxonomy( 'post_tag' );
1285          $show_tags        = current_user_can( $tag_tax->cap->assign_terms );
1286  
1287          $supports_formats = false;
1288          $post_format      = 0;
1289  
1290          if ( current_theme_supports( 'post-formats' ) && post_type_supports( $post->post_type, 'post-formats' ) ) {
1291              $supports_formats = true;
1292  
1293              if ( ! ( $post_format = get_post_format( $post_ID ) ) ) {
1294                  $post_format = 0;
1295              }
1296          }
1297  
1298          /** This action is documented in wp-admin/admin-header.php */
1299          do_action( 'admin_enqueue_scripts', 'press-this.php' );
1300  
1301          /** This action is documented in wp-admin/admin-header.php */
1302          do_action( 'admin_print_styles-press-this.php' );
1303  
1304          /** This action is documented in wp-admin/admin-header.php */
1305          do_action( 'admin_print_styles' );
1306  
1307          /** This action is documented in wp-admin/admin-header.php */
1308          do_action( 'admin_print_scripts-press-this.php' );
1309  
1310          /** This action is documented in wp-admin/admin-header.php */
1311          do_action( 'admin_print_scripts' );
1312  
1313          /** This action is documented in wp-admin/admin-header.php */
1314          do_action( 'admin_head-press-this.php' );
1315  
1316          /** This action is documented in wp-admin/admin-header.php */
1317          do_action( 'admin_head' );
1318      ?>
1319  </head>
1320  <?php
1321  
1322      $admin_body_class  = 'press-this';
1323      $admin_body_class .= ( is_rtl() ) ? ' rtl' : '';
1324      $admin_body_class .= ' branch-' . str_replace( array( '.', ',' ), '-', floatval( $wp_version ) );
1325      $admin_body_class .= ' version-' . str_replace( '.', '-', preg_replace( '/^([.0-9]+).*/', '$1', $wp_version ) );
1326      $admin_body_class .= ' admin-color-' . sanitize_html_class( get_user_option( 'admin_color' ), 'fresh' );
1327      $admin_body_class .= ' locale-' . sanitize_html_class( strtolower( str_replace( '_', '-', get_user_locale() ) ) );
1328  
1329      /** This filter is documented in wp-admin/admin-header.php */
1330      $admin_body_classes = apply_filters( 'admin_body_class', '' );
1331  
1332  ?>
1333  <body class="wp-admin wp-core-ui <?php echo $admin_body_classes . ' ' . $admin_body_class; ?>">
1334      <div id="adminbar" class="adminbar">
1335          <h1 id="current-site" class="current-site">
1336              <a class="current-site-link" href="<?php echo esc_url( home_url( '/' ) ); ?>" target="_blank" rel="home">
1337                  <span class="dashicons dashicons-wordpress"></span>
1338                  <span class="current-site-name"><?php bloginfo( 'name' ); ?></span>
1339              </a>
1340          </h1>
1341          <button type="button" class="options button-link closed">
1342              <span class="dashicons dashicons-tag on-closed"></span>
1343              <span class="screen-reader-text on-closed"><?php _e( 'Show post options' ); ?></span>
1344              <span aria-hidden="true" class="on-open"><?php _e( 'Done' ); ?></span>
1345              <span class="screen-reader-text on-open"><?php _e( 'Hide post options' ); ?></span>
1346          </button>
1347      </div>
1348  
1349      <div id="scanbar" class="scan">
1350          <form method="GET">
1351              <label for="url-scan" class="screen-reader-text"><?php _e( 'Scan site for content' ); ?></label>
1352              <input type="url" name="u" id="url-scan" class="scan-url" value="<?php echo esc_attr( $site_data['u'] ) ?>" placeholder="<?php esc_attr_e( 'Enter a URL to scan' ) ?>" />
1353              <input type="submit" name="url-scan-submit" id="url-scan-submit" class="scan-submit" value="<?php esc_attr_e( 'Scan' ) ?>" />
1354              <?php wp_nonce_field( 'scan-site' ); ?>
1355          </form>
1356      </div>
1357  
1358      <form id="pressthis-form" method="post" action="post.php" autocomplete="off">
1359          <input type="hidden" name="post_ID" id="post_ID" value="<?php echo $post_ID; ?>" />
1360          <input type="hidden" name="action" value="press-this-save-post" />
1361          <input type="hidden" name="post_status" id="post_status" value="draft" />
1362          <input type="hidden" name="wp-preview" id="wp-preview" value="" />
1363          <input type="hidden" name="post_title" id="post_title" value="" />
1364          <input type="hidden" name="pt-force-redirect" id="pt-force-redirect" value="" />
1365          <?php
1366  
1367          wp_nonce_field( 'update-post_' . $post_ID, '_wpnonce', false );
1368          wp_nonce_field( 'add-category', '_ajax_nonce-add-category', false );
1369  
1370          ?>
1371  
1372      <div class="wrapper">
1373          <div class="editor-wrapper">
1374              <div class="alerts" role="alert" aria-live="assertive" aria-relevant="all" aria-atomic="true">
1375                  <?php
1376  
1377                  if ( isset( $data['v'] ) && $this->version > $data['v'] ) {
1378                      ?>
1379                      <p class="alert is-notice">
1380                          <?php printf( __( 'You should upgrade <a href="%s" target="_blank">your bookmarklet</a> to the latest version!' ), admin_url( 'tools.php' ) ); ?>
1381                      </p>
1382                      <?php
1383                  }
1384  
1385                  ?>
1386              </div>
1387  
1388              <div id="app-container" class="editor">
1389                  <span id="title-container-label" class="post-title-placeholder" aria-hidden="true"><?php _e( 'Post title' ); ?></span>
1390                  <h2 id="title-container" class="post-title" contenteditable="true" spellcheck="true" aria-label="<?php esc_attr_e( 'Post title' ); ?>" tabindex="0"><?php echo esc_html( $post_title ); ?></h2>
1391  
1392                  <div class="media-list-container">
1393                      <div class="media-list-inner-container">
1394                          <h2 class="screen-reader-text"><?php _e( 'Suggested media' ); ?></h2>
1395                          <ul class="media-list"></ul>
1396                      </div>
1397                  </div>
1398  
1399                  <?php
1400                  wp_editor( $post_content, 'pressthis', array(
1401                      'drag_drop_upload' => true,
1402                      'editor_height'    => 600,
1403                      'media_buttons'    => false,
1404                      'textarea_name'    => 'post_content',
1405                      'teeny'            => true,
1406                      'tinymce'          => array(
1407                          'resize'                => false,
1408                          'wordpress_adv_hidden'  => false,
1409                          'add_unload_trigger'    => false,
1410                          'statusbar'             => false,
1411                          'autoresize_min_height' => 600,
1412                          'wp_autoresize_on'      => true,
1413                          'plugins'               => 'lists,media,paste,tabfocus,fullscreen,wordpress,wpautoresize,wpeditimage,wpgallery,wplink,wptextpattern,wpview',
1414                          'toolbar1'              => 'bold,italic,bullist,numlist,blockquote,link,unlink',
1415                          'toolbar2'              => 'undo,redo',
1416                      ),
1417                      'quicktags' => array(
1418                          'buttons' => 'strong,em,link,block,del,ins,img,ul,ol,li,code,more',
1419                      ),
1420                  ) );
1421  
1422                  ?>
1423              </div>
1424          </div>
1425  
1426          <div class="options-panel-back is-hidden" tabindex="-1"></div>
1427          <div class="options-panel is-off-screen is-hidden" tabindex="-1">
1428              <div class="post-options">
1429  
1430                  <?php if ( $supports_formats ) : ?>
1431                      <button type="button" class="post-option">
1432                          <span class="dashicons dashicons-admin-post"></span>
1433                          <span class="post-option-title"><?php _ex( 'Format', 'post format' ); ?></span>
1434                          <span class="post-option-contents" id="post-option-post-format"><?php echo esc_html( get_post_format_string( $post_format ) ); ?></span>
1435                          <span class="dashicons post-option-forward"></span>
1436                      </button>
1437                  <?php endif; ?>
1438  
1439                  <?php if ( $show_categories ) : ?>
1440                      <button type="button" class="post-option">
1441                          <span class="dashicons dashicons-category"></span>
1442                          <span class="post-option-title"><?php _e( 'Categories' ); ?></span>
1443                          <span class="dashicons post-option-forward"></span>
1444                      </button>
1445                  <?php endif; ?>
1446  
1447                  <?php if ( $show_tags ) : ?>
1448                      <button type="button" class="post-option">
1449                          <span class="dashicons dashicons-tag"></span>
1450                          <span class="post-option-title"><?php _e( 'Tags' ); ?></span>
1451                          <span class="dashicons post-option-forward"></span>
1452                      </button>
1453                  <?php endif; ?>
1454              </div>
1455  
1456              <?php if ( $supports_formats ) : ?>
1457                  <div class="setting-modal is-off-screen is-hidden">
1458                      <button type="button" class="modal-close">
1459                          <span class="dashicons post-option-back"></span>
1460                          <span class="setting-title" aria-hidden="true"><?php _ex( 'Format', 'post format' ); ?></span>
1461                          <span class="screen-reader-text"><?php _e( 'Back to post options' ) ?></span>
1462                      </button>
1463                      <?php $this->post_formats_html( $post ); ?>
1464                  </div>
1465              <?php endif; ?>
1466  
1467              <?php if ( $show_categories ) : ?>
1468                  <div class="setting-modal is-off-screen is-hidden">
1469                      <button type="button" class="modal-close">
1470                          <span class="dashicons post-option-back"></span>
1471                          <span class="setting-title" aria-hidden="true"><?php _e( 'Categories' ); ?></span>
1472                          <span class="screen-reader-text"><?php _e( 'Back to post options' ) ?></span>
1473                      </button>
1474                      <?php $this->categories_html( $post ); ?>
1475                  </div>
1476              <?php endif; ?>
1477  
1478              <?php if ( $show_tags ) : ?>
1479                  <div class="setting-modal tags is-off-screen is-hidden">
1480                      <button type="button" class="modal-close">
1481                          <span class="dashicons post-option-back"></span>
1482                          <span class="setting-title" aria-hidden="true"><?php _e( 'Tags' ); ?></span>
1483                          <span class="screen-reader-text"><?php _e( 'Back to post options' ) ?></span>
1484                      </button>
1485                      <?php $this->tags_html( $post ); ?>
1486                  </div>
1487              <?php endif; ?>
1488          </div><!-- .options-panel -->
1489      </div><!-- .wrapper -->
1490  
1491      <div class="press-this-actions">
1492          <div class="pressthis-media-buttons">
1493              <button type="button" class="insert-media" data-editor="pressthis">
1494                  <span class="dashicons dashicons-admin-media"></span>
1495                  <span class="screen-reader-text"><?php _e( 'Add Media' ); ?></span>
1496              </button>
1497          </div>
1498          <div class="post-actions">
1499              <span class="spinner">&nbsp;</span>
1500              <div class="split-button">
1501                  <div class="split-button-head">
1502                      <button type="button" class="publish-button split-button-primary" aria-live="polite">
1503                          <span class="publish"><?php echo ( current_user_can( 'publish_posts' ) ) ? __( 'Publish' ) : __( 'Submit for Review' ); ?></span>
1504                          <span class="saving-draft"><?php _e( 'Saving&hellip;' ); ?></span>
1505                      </button><button type="button" class="split-button-toggle" aria-haspopup="true" aria-expanded="false">
1506                          <i class="dashicons dashicons-arrow-down-alt2"></i>
1507                          <span class="screen-reader-text"><?php _e('More actions'); ?></span>
1508                      </button>
1509                  </div>
1510                  <ul class="split-button-body">
1511                      <li><button type="button" class="button-link draft-button split-button-option"><?php _e( 'Save Draft' ); ?></button></li>
1512                      <li><button type="button" class="button-link standard-editor-button split-button-option"><?php _e( 'Standard Editor' ); ?></button></li>
1513                      <li><button type="button" class="button-link preview-button split-button-option"><?php _e( 'Preview' ); ?></button></li>
1514                  </ul>
1515              </div>
1516          </div>
1517      </div>
1518      </form>
1519  
1520      <?php
1521      /** This action is documented in wp-admin/admin-footer.php */
1522      do_action( 'admin_footer', '' );
1523  
1524      /** This action is documented in wp-admin/admin-footer.php */
1525      do_action( 'admin_print_footer_scripts-press-this.php' );
1526  
1527      /** This action is documented in wp-admin/admin-footer.php */
1528      do_action( 'admin_print_footer_scripts' );
1529  
1530      /** This action is documented in wp-admin/admin-footer.php */
1531      do_action( 'admin_footer-press-this.php' );
1532      ?>
1533  </body>
1534  </html>
1535  <?php
1536          die();
1537      }
1538  }


Generated: Sun Sep 24 01:00:03 2017 Cross-referenced by PHPXref 0.7.1