Instant Articles for WP - Version 3.1

Version Description

  • New on-boarding flow wizard
  • Automattic URL claiming
  • Submit for review from wizard
  • Improved transformation rules
  • Option to submit only articles without warnings
  • Jetpack compatibility
  • Added Jetpack carousel rules
  • Compatibility layer for Get The Image plugin
  • Fix for relative URL checking
  • Fix for missing subtitles
  • Fix for double call of wpautop
  • Fix for loadHTML warnings
  • Fix for get_cover_media function
  • Fix to prevent publishing of password protected posts

props diegoquinteiro everton-rosario gemedet jacobarriola menzow rinatkhaziev srtfisher

Download this release

Release Info

Developer philipjohn
Plugin Icon 128x128 Instant Articles for WP
Version 3.1
Comparing to
See all releases

Code changes from version 3.0.1 to 3.1

Files changed (84) hide show
  1. README.md +46 -0
  2. assets/banner-1544x500.jpg +0 -0
  3. assets/banner-772x250.jpg +0 -0
  4. assets/check@2x.png +0 -0
  5. assets/connect@2x.png +0 -0
  6. assets/customize@2x.png +0 -0
  7. assets/icon-128x128.jpg +0 -0
  8. assets/icon-256x256.jpg +0 -0
  9. assets/icon-fb-login.png +0 -0
  10. assets/key@2x.png +0 -0
  11. assets/pencil.png +0 -0
  12. assets/screenshot-1.png +0 -0
  13. class-instant-articles-post.php +49 -35
  14. class-instant-articles-publisher.php +22 -3
  15. compat.php +12 -0
  16. compat/class-instant-articles-get-the-image.php +27 -0
  17. compat/class-instant-articles-jetpack.php +10 -0
  18. compat/get-the-image-rules-configuration.json +13 -0
  19. compat/jetpack-rules-configuration.json +91 -0
  20. css/instant-articles-wizard.css +417 -0
  21. facebook-instant-articles.php +34 -11
  22. js/instant-articles-settings.js +1 -18
  23. js/instant-articles-wizard.js +136 -0
  24. meta-box/class-instant-articles-meta-box.php +2 -2
  25. readme.txt +19 -1
  26. rules-configuration.json +157 -32
  27. vendor/autoload.php +1 -1
  28. vendor/composer/autoload_real.php +7 -7
  29. vendor/composer/autoload_static.php +4 -4
  30. vendor/composer/installed.json +17 -16
  31. vendor/facebook/facebook-instant-articles-sdk-php/.gitignore +0 -4
  32. vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Client/Client.php +38 -0
  33. vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Client/Helper.php +1 -1
  34. vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Elements/InstantArticle.php +45 -1
  35. vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Elements/Interactive.php +1 -1
  36. vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Parser/instant-articles-rules.json +16 -0
  37. vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Transformer/Getters/GetterFactory.php +3 -0
  38. vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Transformer/Getters/JSONGetter.php +63 -0
  39. vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Transformer/Rules/Compat/JetpackSlideshowRule.php +77 -0
  40. vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Transformer/Rules/ConfigurationSelectorRule.php +1 -1
  41. vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Transformer/Rules/H1Rule.php +1 -1
  42. vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Transformer/Rules/H2Rule.php +1 -1
  43. vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Transformer/Rules/ImageRule.php +20 -1
  44. vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Transformer/Rules/InteractiveInsideParagraphRule.php +119 -0
  45. vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Transformer/Rules/InteractiveRule.php +1 -1
  46. vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Transformer/Rules/Rule.php +2 -3
  47. vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Transformer/Warnings/validator_warning_messages.ini +1 -1
  48. vendor/facebook/facebook-instant-articles-sdk-php/tests/Facebook/InstantArticles/Client/ClientTest.php +193 -0
  49. vendor/facebook/facebook-instant-articles-sdk-php/tests/Facebook/InstantArticles/Client/HelperTest.php +1 -1
  50. vendor/facebook/facebook-instant-articles-sdk-php/tests/Facebook/InstantArticles/Elements/InteractiveTest.php +49 -27
  51. vendor/facebook/facebook-instant-articles-sdk-php/tests/Facebook/InstantArticles/Transformer/CMS/WPTransformerTest.php +3 -2
  52. vendor/facebook/facebook-instant-articles-sdk-php/tests/Facebook/InstantArticles/Transformer/CMS/wp-ia.xml +116 -1
  53. vendor/facebook/facebook-instant-articles-sdk-php/tests/Facebook/InstantArticles/Transformer/CMS/wp-rules.json +325 -12
  54. vendor/facebook/facebook-instant-articles-sdk-php/tests/Facebook/InstantArticles/Transformer/CMS/wp.html +132 -11
  55. vendor/facebook/facebook-instant-articles-sdk-php/tests/Facebook/InstantArticles/Transformer/Example/simple-ia.html +5 -0
  56. vendor/facebook/facebook-instant-articles-sdk-php/tests/Facebook/InstantArticles/Transformer/Example/simple.html +3 -0
  57. vendor/facebook/facebook-instant-articles-sdk-php/tests/Facebook/InstantArticles/Transformer/TransformerTest.php +1 -1
  58. vendor/facebook/php-sdk-v4/src/Facebook/Facebook.php +2 -2
  59. vendor/facebook/php-sdk-v4/src/Facebook/FacebookResponse.php +1 -1
  60. vendor/facebook/php-sdk-v4/src/Facebook/Helpers/FacebookRedirectLoginHelper.php +10 -1
  61. vendor/facebook/php-sdk-v4/src/Facebook/PseudoRandomString/PseudoRandomStringGeneratorTrait.php +0 -1
  62. vendor/facebook/php-sdk-v4/src/Facebook/Url/FacebookUrlDetectionHandler.php +2 -1
  63. vendor/facebook/php-sdk-v4/src/Facebook/autoload.php +2 -0
  64. wizard/class-instant-articles-invalid-wizard-transition-exception.php +19 -0
  65. wizard/class-instant-articles-option-ads.php +245 -0
  66. wizard/class-instant-articles-option-analytics.php +144 -0
  67. wizard/class-instant-articles-option-fb-app.php +65 -0
  68. wizard/class-instant-articles-option-fb-page.php +77 -0
  69. wizard/class-instant-articles-option-publishing.php +121 -0
  70. wizard/class-instant-articles-option-styles.php +45 -0
  71. wizard/class-instant-articles-option.php +490 -0
  72. wizard/class-instant-articles-wizard-fb-helper.php +212 -0
  73. wizard/class-instant-articles-wizard-review-submission.php +96 -0
  74. wizard/class-instant-articles-wizard-state.php +352 -0
  75. wizard/class-instant-articles-wizard.php +295 -0
  76. wizard/state_machine.txt +26 -0
  77. wizard/templates/advanced-template.php +31 -0
  78. wizard/templates/all-steps-wizard.php +288 -0
  79. wizard/templates/app-setup-template.php +115 -0
  80. wizard/templates/overview-template.php +43 -0
  81. wizard/templates/page-selection-template.php +63 -0
  82. wizard/templates/review-submission-template.php +156 -0
  83. wizard/templates/style-selection-template.php +66 -0
  84. wizard/templates/wizard-template.php +112 -0
README.md CHANGED
@@ -1,7 +1,12 @@
 
 
1
  # Instant Articles for WP
2
 
3
  Enable [Instant Articles for Facebook](https://developers.facebook.com/docs/instant-articles) on your WordPress site.
4
 
 
 
 
5
  ## Description
6
 
7
  This plugin adds support for Instant Articles for Facebook, which is a new way for publishers to distribute fast, interactive stories on Facebook. Instant Articles are preloaded in the Facebook mobile app so they load instantly.
@@ -62,3 +67,44 @@ By default it is set to `instant-articles` which usually will give you a feed UR
62
  **How do I flush the rewrite rules after changing the feed slug?**
63
 
64
  Usually simply visiting the permalinks settings page in the WordPress dashboard will do the trick (/wp-admin/options-permalink.php)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [![Build Status](https://travis-ci.org/Automattic/facebook-instant-articles-wp.svg?branch=master)](https://travis-ci.org/Automattic/facebook-instant-articles-wp)
2
+
3
  # Instant Articles for WP
4
 
5
  Enable [Instant Articles for Facebook](https://developers.facebook.com/docs/instant-articles) on your WordPress site.
6
 
7
+ ## Plugin activity
8
+ [![Throughput Graph](https://graphs.waffle.io/Automattic/facebook-instant-articles-wp/throughput.svg)](https://waffle.io/Automattic/facebook-instant-articles-wp/metrics/throughput)
9
+
10
  ## Description
11
 
12
  This plugin adds support for Instant Articles for Facebook, which is a new way for publishers to distribute fast, interactive stories on Facebook. Instant Articles are preloaded in the Facebook mobile app so they load instantly.
67
  **How do I flush the rewrite rules after changing the feed slug?**
68
 
69
  Usually simply visiting the permalinks settings page in the WordPress dashboard will do the trick (/wp-admin/options-permalink.php)
70
+
71
+ ## Development Environment
72
+ You are more than welcome to help us to make this plugin even better!
73
+
74
+ ### Setup SDK and WP to use the Github code
75
+ Most of development and debugging we use the master or any feature branch for the development of WP plugin.
76
+ Sometimes it needs specific or newest version of the SDK to match all the new features that are release candidates to every new release of SDK and WP plugin.
77
+
78
+ The SDK is also under development @ GitHub: <https://github.com/facebook/facebook-instant-articles-sdk-php>
79
+
80
+ #### Pre-requisites
81
+ - Have PHP installed (you can install with homebrew)
82
+ - Have WebServer installed (you can use MAMP)
83
+ - Have WP installed (downloading from wordpress.org)
84
+
85
+ #### Setup
86
+ Clone both repositories into your developer folder (ex: ~/instant-articles).
87
+ ```
88
+ git clone git@github.com:Automattic/facebook-instant-articles-wp.git
89
+ git clone git@github.com:facebook/facebook-instant-articles-sdk-php.git
90
+ ```
91
+
92
+ Build both source folders:
93
+ ```
94
+ cd facebook-instant-articles-sdk-php
95
+ composer install
96
+
97
+ cd ../facebook-instant-articles-wp
98
+ composer install
99
+ ```
100
+
101
+ Now remove the build from your WordPress, so it will include the code you've just built.
102
+ ```
103
+ rm -rf vendor/facebook/facebook-instant-articles-sdk-php
104
+ ln -s ~/facebook-instant-articles-sdk-php vendor/facebook/facebook-instant-articles-sdk-php
105
+ ```
106
+
107
+ and now you can create a link inside your /wp-content/plugins to your folder
108
+ ```
109
+ ln -s ~/facebook-instant-articles-wp wp-content/plugins/facebook-instant-articles-wp
110
+ ```
assets/banner-1544x500.jpg ADDED
Binary file
assets/banner-772x250.jpg ADDED
Binary file
assets/check@2x.png ADDED
Binary file
assets/connect@2x.png ADDED
Binary file
assets/customize@2x.png ADDED
Binary file
assets/icon-128x128.jpg ADDED
Binary file
assets/icon-256x256.jpg ADDED
Binary file
assets/icon-fb-login.png ADDED
Binary file
assets/key@2x.png ADDED
Binary file
assets/pencil.png ADDED
Binary file
assets/screenshot-1.png ADDED
Binary file
class-instant-articles-post.php CHANGED
@@ -14,6 +14,7 @@ use Facebook\InstantArticles\Elements\Ad;
14
  use Facebook\InstantArticles\Elements\Analytics;
15
  use Facebook\InstantArticles\Elements\Author;
16
  use Facebook\InstantArticles\Elements\Image;
 
17
  use Facebook\InstantArticles\Elements\Caption;
18
  use Facebook\InstantArticles\Elements\Footer;
19
  use Facebook\InstantArticles\Transformer\Transformer;
@@ -84,6 +85,14 @@ class Instant_Articles_Post {
84
  public function get_the_title() {
85
  $title = $this->_post->post_title;
86
 
 
 
 
 
 
 
 
 
87
  /**
88
  * Filter the post title for use in instant articles.
89
  *
@@ -244,12 +253,12 @@ class Instant_Articles_Post {
244
  // If post is draft, clone it to get the eventual permalink,
245
  // see http://wordpress.stackexchange.com/a/42988.
246
  if ( in_array( $this->_post->post_status, array( 'draft', 'pending', 'auto-draft' ), true ) ) {
247
- $post_clone = clone $this->_post;
248
- $post_clone->post_status = 'published';
249
- $post_clone->post_name = sanitize_title( $post_clone->post_name ? $post_clone->post_name : $post_clone->post_title, $post_clone->ID );
250
- $url = get_permalink( $post_clone );
251
  } else {
252
- $url = get_permalink( $this->_post );
253
  }
254
 
255
  return $url;
@@ -311,6 +320,11 @@ class Instant_Articles_Post {
311
  * @since 0.1
312
  * @param string $content The current post content.
313
  */
 
 
 
 
 
314
  $content = apply_filters( 'the_content', $content );
315
 
316
  // Maybe cleanup some globals after us?
@@ -319,15 +333,12 @@ class Instant_Articles_Post {
319
  wp_reset_postdata();
320
  }
321
 
322
- // Some people choose to disable wpautop. Due to the Instant Articles spec, we really want it in!
323
- $content = wpautop( $content );
324
-
325
  // Remove hyperlinks beginning with a # as they cause errors on Facebook (from http://wordpress.stackexchange.com/a/227332/19528)
326
- preg_match_all( '!<a[^>]*? href=[\'"]#[^<]+</a>!i', $content, $matches );
327
- foreach ( $matches[0] as $link ) {
328
- $content = str_replace( $link, strip_tags($link), $content );
329
- }
330
-
331
  /**
332
  * Filter the post content for Instant Articles.
333
  *
@@ -481,12 +492,12 @@ class Instant_Articles_Post {
481
  * Get the cover media.
482
  *
483
  * @since 0.1
484
- * @return array
485
  */
486
  public function get_cover_media() {
487
 
488
- $cover_media = new stdClass;
489
- $cover_media->type = 'none';
490
 
491
  // If someone else is handling this, let them. Otherwise fall back to us trying to use the featured image.
492
  if ( has_filter( 'instant_articles_cover_media' ) ) {
@@ -494,16 +505,18 @@ class Instant_Articles_Post {
494
  * Filter the cover media.
495
  *
496
  * @since 0.1
497
- * @param stdClass $cover_media The cover media object.
498
  * @param int $post_id The current post ID.
499
  */
500
  $cover_media = apply_filters( 'instant_articles_cover_media', $cover_media, $this->_post->ID );
501
  } else {
 
502
  $featured_image_data = $this->get_the_featured_image();
503
  if ( isset( $featured_image_data['src'] ) && strlen( $featured_image_data['src'] ) ) {
504
- $cover_media->type = 'image';
505
- $cover_media->src = $featured_image_data['src'];
506
- $cover_media->caption = $featured_image_data['caption'];
 
507
  }
508
  }
509
 
@@ -574,11 +587,11 @@ class Instant_Articles_Post {
574
  public function to_instant_article() {
575
 
576
  /**
577
- * Fires before the instant article is rendered.
578
- *
579
- * @since 0.1
580
- * @param Instant_Article_Post $instant_article_post The instant article post.
581
- */
582
  do_action( 'instant_articles_before_transform_post', $this );
583
 
584
  // Get time zone configured in WordPress. Default to UTC if no time zone configured.
@@ -621,10 +634,16 @@ class Instant_Articles_Post {
621
  $title = $this->get_the_title();
622
  if ( $title ) {
623
  $document = new DOMDocument();
 
624
  $document->loadHTML( '<?xml encoding="' . $blog_charset . '" ?><h1>' . $title . '</h1>' );
 
625
  $transformer->transform( $header, $document );
626
  }
627
 
 
 
 
 
628
  $authors = $this->get_the_authors();
629
  foreach ( $authors as $author ) {
630
  $author_obj = Author::create();
@@ -643,17 +662,12 @@ class Instant_Articles_Post {
643
  if ( $kicker ) {
644
  $header->withKicker( $kicker );
645
  }
646
- $cover = $this->get_the_featured_image();
647
- if ( $cover['src'] ) {
648
- $image = Image::create()->withURL( $cover['src'] );
649
- if ( isset( $cover['caption'] ) && strlen( $cover['caption'] ) > 0 ) {
650
- $document = new DOMDocument();
651
- $document->loadHTML( '<?xml encoding="' . $blog_charset . '" ?><h1>' . $cover['caption'] . '</h1>' );
652
- $image->withCaption( $transformer->transform( Caption::create(), $document ) );
653
- }
654
 
655
- $header->withCover( $image );
 
 
656
  }
 
657
  $this->instant_article =
658
  InstantArticle::create()
659
  ->withCanonicalUrl( $this->get_canonical_url() )
@@ -687,7 +701,7 @@ class Instant_Articles_Post {
687
  foreach ( $scripts as $script ){
688
  $src = $script->getAttribute( 'src' );
689
  $explode_src = parse_url( $src );
690
- if ( is_array( $explode_src ) && empty( $explode_src['scheme'] ) ) {
691
  $src = 'https://' . $explode_src['host'] . $explode_src['path'];
692
  }
693
  $script->setAttribute( 'src' , $src );
14
  use Facebook\InstantArticles\Elements\Analytics;
15
  use Facebook\InstantArticles\Elements\Author;
16
  use Facebook\InstantArticles\Elements\Image;
17
+ use Facebook\InstantArticles\Elements\Video;
18
  use Facebook\InstantArticles\Elements\Caption;
19
  use Facebook\InstantArticles\Elements\Footer;
20
  use Facebook\InstantArticles\Transformer\Transformer;
85
  public function get_the_title() {
86
  $title = $this->_post->post_title;
87
 
88
+ /**
89
+ * Apply the default filter 'the_title' for the post tite.
90
+ *
91
+ * @since 3.1
92
+ * @param string $title The current post title.
93
+ */
94
+ $title = apply_filters( 'the_title', $title);
95
+
96
  /**
97
  * Filter the post title for use in instant articles.
98
  *
253
  // If post is draft, clone it to get the eventual permalink,
254
  // see http://wordpress.stackexchange.com/a/42988.
255
  if ( in_array( $this->_post->post_status, array( 'draft', 'pending', 'auto-draft' ), true ) ) {
256
+ $post_clone = clone $this->_post;
257
+ $post_clone->post_status = 'published';
258
+ $post_clone->post_name = sanitize_title( $post_clone->post_name ? $post_clone->post_name : $post_clone->post_title, $post_clone->ID );
259
+ $url = get_permalink( $post_clone );
260
  } else {
261
+ $url = get_permalink( $this->_post );
262
  }
263
 
264
  return $url;
320
  * @since 0.1
321
  * @param string $content The current post content.
322
  */
323
+
324
+ // Some people choose to disable wpautop. Due to the Instant Articles spec, we really want it in!
325
+ if ( ! has_filter( 'the_content', 'wpautop' ) )
326
+ add_filter( 'the_content', 'wpautop' );
327
+
328
  $content = apply_filters( 'the_content', $content );
329
 
330
  // Maybe cleanup some globals after us?
333
  wp_reset_postdata();
334
  }
335
 
 
 
 
336
  // Remove hyperlinks beginning with a # as they cause errors on Facebook (from http://wordpress.stackexchange.com/a/227332/19528)
337
+ preg_match_all( '!<a[^>]*? href=[\'"]#[^<]+</a>!i', $content, $matches );
338
+ foreach ( $matches[0] as $link ) {
339
+ $content = str_replace( $link, strip_tags($link), $content );
340
+ }
341
+
342
  /**
343
  * Filter the post content for Instant Articles.
344
  *
492
  * Get the cover media.
493
  *
494
  * @since 0.1
495
+ * @return Image|Video
496
  */
497
  public function get_cover_media() {
498
 
499
+ $cover_media = Image::create();
500
+
501
 
502
  // If someone else is handling this, let them. Otherwise fall back to us trying to use the featured image.
503
  if ( has_filter( 'instant_articles_cover_media' ) ) {
505
  * Filter the cover media.
506
  *
507
  * @since 0.1
508
+ * @param Image $cover_media The cover media object.
509
  * @param int $post_id The current post ID.
510
  */
511
  $cover_media = apply_filters( 'instant_articles_cover_media', $cover_media, $this->_post->ID );
512
  } else {
513
+
514
  $featured_image_data = $this->get_the_featured_image();
515
  if ( isset( $featured_image_data['src'] ) && strlen( $featured_image_data['src'] ) ) {
516
+ $cover_media = Image::create()->withURL($featured_image_data['src']);
517
+ if( isset( $featured_image_data['caption'] ) && strlen( $featured_image_data['caption'] )) {
518
+ $cover_media->withCaption(Caption::create()->withTitle($featured_image_data['caption']));
519
+ }
520
  }
521
  }
522
 
587
  public function to_instant_article() {
588
 
589
  /**
590
+ * Fires before the instant article is rendered.
591
+ *
592
+ * @since 0.1
593
+ * @param Instant_Article_Post $instant_article_post The instant article post.
594
+ */
595
  do_action( 'instant_articles_before_transform_post', $this );
596
 
597
  // Get time zone configured in WordPress. Default to UTC if no time zone configured.
634
  $title = $this->get_the_title();
635
  if ( $title ) {
636
  $document = new DOMDocument();
637
+ libxml_use_internal_errors(true);
638
  $document->loadHTML( '<?xml encoding="' . $blog_charset . '" ?><h1>' . $title . '</h1>' );
639
+ libxml_use_internal_errors(false);
640
  $transformer->transform( $header, $document );
641
  }
642
 
643
+ if ( $this->has_subtitle() ) {
644
+ $header->withSubTitle ( $this->get_the_subtitle() ) ;
645
+ }
646
+
647
  $authors = $this->get_the_authors();
648
  foreach ( $authors as $author ) {
649
  $author_obj = Author::create();
662
  if ( $kicker ) {
663
  $header->withKicker( $kicker );
664
  }
 
 
 
 
 
 
 
 
665
 
666
+ $cover = $this->get_cover_media();
667
+ if ( $cover->getUrl() ) {
668
+ $header->withCover( $cover );
669
  }
670
+
671
  $this->instant_article =
672
  InstantArticle::create()
673
  ->withCanonicalUrl( $this->get_canonical_url() )
701
  foreach ( $scripts as $script ){
702
  $src = $script->getAttribute( 'src' );
703
  $explode_src = parse_url( $src );
704
+ if ( is_array( $explode_src ) && empty( $explode_src['scheme'] ) && ! empty( $explode_src['host'] ) && ! empty( $explode_src['path'] ) ) {
705
  $src = 'https://' . $explode_src['host'] . $explode_src['path'];
706
  }
707
  $script->setAttribute( 'src' , $src );
class-instant-articles-publisher.php CHANGED
@@ -89,16 +89,35 @@ class Instant_Articles_Publisher {
89
  $dev_mode
90
  );
91
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  if ( $dev_mode ) {
93
- $take_live = false;
94
  } else {
95
  // Any publish status other than 'publish' means draft for the Instant Article.
96
- $take_live = true;
97
  }
98
 
99
  try {
100
  // Import the article.
101
- $submission_id = $client->importArticle( $article, $take_live );
102
  update_post_meta( $post_id, self::SUBMISSION_ID_KEY, $submission_id );
103
  } catch ( Exception $e ) {
104
  // Try without taking live for pages not yet reviewed.
89
  $dev_mode
90
  );
91
 
92
+ // Don't publish posts with password protection
93
+ if ( post_password_required( $post ) ) {
94
+ // Unpublishes if already published and from now on it started to have password protection
95
+ $client->removeArticle( $article->getCanonicalURL() );
96
+ delete_post_meta( $post_id, self::SUBMISSION_ID_KEY );
97
+ return;
98
+ }
99
+
100
+ // Don't process if contains warnings and blocker flag for transformation warnings is turned on.
101
+ if ( count( $adapter->transformer->getWarnings() ) > 0
102
+ && isset( $publishing_settings['block_publish_with_warnings'] )
103
+ && $publishing_settings['block_publish_with_warnings'] ) {
104
+
105
+ // Unpublishes if already published
106
+ $client->removeArticle( $article->getCanonicalURL() );
107
+ delete_post_meta( $post_id, self::SUBMISSION_ID_KEY );
108
+ return;
109
+ }
110
+
111
  if ( $dev_mode ) {
112
+ $published = false;
113
  } else {
114
  // Any publish status other than 'publish' means draft for the Instant Article.
115
+ $published = true;
116
  }
117
 
118
  try {
119
  // Import the article.
120
+ $submission_id = $client->importArticle( $article, $published );
121
  update_post_meta( $post_id, self::SUBMISSION_ID_KEY, $submission_id );
122
  } catch ( Exception $e ) {
123
  // Try without taking live for pages not yet reviewed.
compat.php CHANGED
@@ -7,6 +7,11 @@
7
  * @package default
8
  */
9
 
 
 
 
 
 
10
  // Load compat layer for Co-Authors Plus.
11
  if ( function_exists( 'get_coauthors' ) && ! defined( 'CAP_IA_COMPAT' ) ) {
12
  include( dirname( __FILE__ ) . '/compat/class-instant-articles-co-authors-plus.php' );
@@ -34,3 +39,10 @@ if ( defined( 'JETPACK__VERSION' ) ) {
34
  $jp = new Instant_Articles_Jetpack;
35
  $jp->init();
36
  }
 
 
 
 
 
 
 
7
  * @package default
8
  */
9
 
10
+ if ( !function_exists( 'is_plugin_active' ) ) {
11
+ require_once ( ABSPATH . '/wp-admin/includes/plugin.php' );
12
+ }
13
+
14
+
15
  // Load compat layer for Co-Authors Plus.
16
  if ( function_exists( 'get_coauthors' ) && ! defined( 'CAP_IA_COMPAT' ) ) {
17
  include( dirname( __FILE__ ) . '/compat/class-instant-articles-co-authors-plus.php' );
39
  $jp = new Instant_Articles_Jetpack;
40
  $jp->init();
41
  }
42
+
43
+ // Load support for Get The Image plugin
44
+ if ( is_plugin_active( 'get-the-image/get-the-image.php' ) ) {
45
+ include( dirname( __FILE__ ) . '/compat/class-instant-articles-get-the-image.php' );
46
+ $gti = new Instant_Articles_Get_The_Image;
47
+ $gti->init();
48
+ }
compat/class-instant-articles-get-the-image.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Support class for Get The Image plugin
5
+ *
6
+ * @since 3.0.2+
7
+ *
8
+ */
9
+ class Instant_Articles_Get_The_Image {
10
+
11
+ /**
12
+ * Init the compat layer
13
+ *
14
+ */
15
+ function init() {
16
+ add_filter( 'instant_articles_transformer_rules_loaded', array( 'Instant_Articles_Get_The_Image', 'transformer_loaded' ) );
17
+ }
18
+
19
+ public static function transformer_loaded( $transformer ) {
20
+ // Appends more rules to transformer
21
+ $file_path = plugin_dir_path( __FILE__ ) . 'get-the-image-rules-configuration.json';
22
+ $configuration = file_get_contents( $file_path );
23
+ $transformer->loadRules( $configuration );
24
+
25
+ return $transformer;
26
+ }
27
+ }
compat/class-instant-articles-jetpack.php CHANGED
@@ -15,6 +15,7 @@ class Instant_Articles_Jetpack {
15
  function init() {
16
  $this->_fix_youtube_embed();
17
  $this->_fix_facebook_embed();
 
18
  }
19
 
20
  /**
@@ -80,4 +81,13 @@ class Instant_Articles_Jetpack {
80
 
81
  return '<figure class="op-social"><iframe><script src="https://connect.facebook.net/' . $locale . '/sdk.js#xfbml=1&amp;version=v2.6" async></script><div class="fb-post" data-href="' . esc_url( $url ) . '"></div></iframe></figure>';
82
  }
 
 
 
 
 
 
 
 
 
83
  }
15
  function init() {
16
  $this->_fix_youtube_embed();
17
  $this->_fix_facebook_embed();
18
+ add_filter( 'instant_articles_transformer_rules_loaded', array( 'Instant_Articles_Jetpack', 'transformer_loaded' ) );
19
  }
20
 
21
  /**
81
 
82
  return '<figure class="op-social"><iframe><script src="https://connect.facebook.net/' . $locale . '/sdk.js#xfbml=1&amp;version=v2.6" async></script><div class="fb-post" data-href="' . esc_url( $url ) . '"></div></iframe></figure>';
83
  }
84
+
85
+ public static function transformer_loaded( $transformer ) {
86
+ // Appends more rules to transformer
87
+ $file_path = plugin_dir_path( __FILE__ ) . 'jetpack-rules-configuration.json';
88
+ $configuration = file_get_contents( $file_path );
89
+ $transformer->loadRules( $configuration );
90
+
91
+ return $transformer;
92
+ }
93
  }
compat/get-the-image-rules-configuration.json ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "rules": [{
3
+ "class": "ImageInsideParagraphRule",
4
+ "selector": "figure.wp-caption",
5
+ "properties": {
6
+ "image.url": {
7
+ "type": "string",
8
+ "selector": "img",
9
+ "attribute": "src"
10
+ }
11
+ }
12
+ }]
13
+ }
compat/jetpack-rules-configuration.json ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "rules": [{
3
+ "class" : "IgnoreRule",
4
+ "selector" : "p.jetpack-slideshow-noscript"
5
+ },
6
+ {
7
+ "class": "CaptionRule",
8
+ "selector" : "div.wp-caption-text"
9
+ },
10
+ {
11
+ "class" : "PassThroughRule",
12
+ "selector" : "div.gallery-row"
13
+ },
14
+ {
15
+ "class" : "PassThroughRule",
16
+ "selector" : "div.tiled-gallery p"
17
+ },
18
+ {
19
+ "class" : "PassThroughRule",
20
+ "selector" : "div.gallery-row p"
21
+ },
22
+ {
23
+ "class" : "PassThroughRule",
24
+ "selector" : "div.gallery-group p"
25
+ },
26
+ {
27
+ "class" : "PassThroughRule",
28
+ "selector" : "div.gallery-group"
29
+ },
30
+ {
31
+ "class": "ImageRule",
32
+ "selector" : "div.wp-caption",
33
+ "properties" : {
34
+ "image.url" : {
35
+ "type" : "string",
36
+ "selector" : "img",
37
+ "attribute": "src"
38
+ }
39
+ }
40
+ },
41
+ {
42
+ "class": "SlideshowImageRule",
43
+ "selector" : "div.tiled-gallery-item",
44
+ "properties" : {
45
+ "image.url" : {
46
+ "type" : "string",
47
+ "selector" : "img",
48
+ "attribute": "data-orig-file"
49
+ },
50
+ "caption.title" : {
51
+ "type" : "string",
52
+ "selector" : "div.tiled-gallery-caption"
53
+ }
54
+ }
55
+ },
56
+ {
57
+ "class": "SlideshowRule",
58
+ "selector" : "div.tiled-gallery"
59
+ },
60
+ {
61
+ "class": "SlideshowImageRule",
62
+ "selector" : "dl.gallery-item",
63
+ "properties" : {
64
+ "image.url" : {
65
+ "type" : "string",
66
+ "selector" : "a",
67
+ "attribute": "href"
68
+ },
69
+ "caption.title" : {
70
+ "type" : "string",
71
+ "selector" : "dd.wp-caption-text"
72
+ }
73
+ }
74
+ },
75
+ {
76
+ "class": "Compat\\JetpackSlideshowRule",
77
+ "selector" : "div.jetpack-slideshow",
78
+ "properties": {
79
+ "jetpack.data-gallery": {
80
+ "type": "json",
81
+ "selector": "div.jetpack-slideshow",
82
+ "attribute": "data-gallery"
83
+ }
84
+ }
85
+ },
86
+ {
87
+ "class": "CaptionRule",
88
+ "selector" : "div.tiled-gallery-caption"
89
+ }
90
+ ]
91
+ }
css/instant-articles-wizard.css ADDED
@@ -0,0 +1,417 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #instant_articles_wizard a {
2
+ color: #4080ff;
3
+ text-decoration: none;
4
+ font-weight: bold;
5
+ }
6
+ #instant_articles_wizard a:hover {
7
+ text-decoration: underline;
8
+ }
9
+
10
+ .instant-articles-card {
11
+ background: white;
12
+ border: solid 1px #dddfe2;
13
+ border-radius: 5px;
14
+ margin: 20px 20px 0 0;
15
+ overflow: hidden;
16
+ }
17
+ .instant-articles-card-success {
18
+ border: solid 1px #42b72a;
19
+ }
20
+ .instant-articles-card-fail {
21
+ border: solid 1px #eb4145;
22
+ }
23
+ .instant-articles-card-collapsed {
24
+ border: solid 1px #42b72a;
25
+ padding-bottom: 0;
26
+ }
27
+ .instant-articles-card-collapsed .instant-articles-card-content {
28
+ display: none;
29
+ }
30
+ .instant-articles-card-content-box h3 {
31
+ text-align: center;
32
+ margin-top: 40px;
33
+ margin-bottom: 40px;
34
+ }
35
+ .instant-articles-card-content-box h4 {
36
+ text-align: center;
37
+ margin-top: 15px;
38
+ margin-bottom: -5px;
39
+ color: #9ca0a7;
40
+ }
41
+ .instant-articles-card-steps {
42
+ margin: 20px auto;
43
+ overflow: hidden;
44
+ width: 660px;
45
+ }
46
+ .instant-articles-card-step {
47
+ position: relative;
48
+ float: left;
49
+ display: inline-block;
50
+ width: 140px;
51
+ margin: 0px 10px;
52
+ }
53
+ .instant-articles-card-step img {
54
+ display: block;
55
+ width: 60px;
56
+ margin: 0 auto;
57
+ }
58
+ .instant-articles-card-step p {
59
+ text-align: center;
60
+ }
61
+ .instant-articles-card-bullet-bar {
62
+ margin: 80px auto 20px auto;
63
+ min-height: 50px;
64
+ overflow: hidden;
65
+ width: 660px;
66
+ }
67
+ .instant-articles-card-bullet-step {
68
+ position: relative;
69
+ float: left;
70
+ display: inline-block;
71
+ width: 140px;
72
+ margin: 0 10px;
73
+ }
74
+ .instant-articles-card-bullet-step p {
75
+ text-align: center;
76
+ color: #9ca0a7;
77
+ font-size: 0.95em;
78
+ margin: 0.7em 0;
79
+ display: none;
80
+ }
81
+ .instant-articles-card-bullet-step h4 {
82
+ text-align: center;
83
+ color: #9ca0a7;
84
+ font-size: 0.95em;
85
+ margin-top: 0.7em;
86
+ margin-bottom: -0.5em;
87
+ }
88
+ .instant-articles-card-bullet {
89
+ display: block;
90
+ width: 15px;
91
+ height: 15px;
92
+ background-color: #d8d8d8;
93
+ border-radius: 10px;
94
+ margin: 0 auto;
95
+ color: white;
96
+ font-size: 0.55em;
97
+ font-weight: lighter;
98
+ text-align: center;
99
+ line-height: 15px;
100
+ }
101
+ .instant-articles-card-bullet-path {
102
+ position: absolute;
103
+ top: 0;
104
+ margin-top: 7px;
105
+ right: -84px;
106
+ width: 146px;
107
+ height: 2px;
108
+ background-color: #d8d8d8;
109
+ }
110
+ .instant-articles-card-bullet-step-completed p,
111
+ .instant-articles-card-bullet-step-completed h4 {
112
+ color: #69c456;
113
+ }
114
+ .instant-articles-card-bullet-step-completed .instant-articles-card-bullet {
115
+ background-color: #69c456;
116
+ }
117
+ .instant-articles-card-bullet-step-completed .instant-articles-card-bullet::before {
118
+ content: '✔';
119
+ }
120
+ .instant-articles-card-bullet-step-current p,
121
+ .instant-articles-card-bullet-step-current h4 {
122
+ display: block;
123
+ color: #4080ff;
124
+ }
125
+ .instant-articles-card-bullet-step-current .instant-articles-card-bullet {
126
+ background-color: #4080ff;
127
+ }
128
+ .instant-articles-card-title {
129
+ background: #f6f7f9;
130
+ border-radius: 5px 5px 0 0;
131
+ border-bottom: solid 1px #dddfe2;
132
+ height: 55px;
133
+ line-height: 55px;
134
+ position: relative;
135
+ }
136
+ .instant-articles-card-collapsed .instant-articles-card-title {
137
+ border-bottom: none;
138
+ background: #f6faf1;
139
+ }
140
+ .instant-articles-card-success .instant-articles-card-title {
141
+ border-bottom: solid 1px #42b72a;
142
+ background: #f6faf1;
143
+ }
144
+ .instant-articles-card-fail .instant-articles-card-title {
145
+ border-bottom: solid 1px #eb4145;
146
+ background: #fcebe8;
147
+ }
148
+ .instant-articles-card-title h3 {
149
+ margin: 0 20px 0 20px;
150
+ display: inline-block;
151
+ vertical-align: middle;
152
+ }
153
+ .instant-articles-card-title-right {
154
+ margin: 0 20px 0 20px;
155
+ display: inline-block;
156
+ vertical-align: middle;
157
+ position: absolute;
158
+ right: 0px;
159
+ font-size: 1.3em;
160
+ color: #acafb6;
161
+ }
162
+ .instant-articles-card-collapsed .instant-articles-card-title-right > * {
163
+ display: inline-block;
164
+ vertical-align: middle;
165
+ }
166
+ .instant-articles-card-title-right > * {
167
+ display: none;
168
+ }
169
+ .instant-articles-card-title-right img {
170
+ width: 40px;
171
+ height: 40px;
172
+ }
173
+ .instant-articles-card-title-step {
174
+ display: inline-block;
175
+ }
176
+ .instant-articles-card-title-checkmark {
177
+ color: #42b72a;
178
+ font-size: 0.9em;
179
+ }
180
+ .instant-articles-card-collapsed .instant-articles-card-title-step {
181
+ display: none;
182
+ }
183
+ .instant-articles-card-title-label {
184
+ font-weight: bold;
185
+ font-size: .8em;
186
+ color: #4b4f56;
187
+ }
188
+ .instant-articles-card-title-value {
189
+ font-size: .8em;
190
+ color: #373b42;
191
+ }
192
+ .instant-articles-card-title-edit {
193
+ background: url('/wp-content/plugins/facebook-instant-articles-wp/assets/pencil.png');
194
+ background-size: 13px 13px;
195
+ background-repeat: no-repeat;
196
+ width: 15px;
197
+ height: 14px;
198
+ }
199
+ .instant-articles-card-content {
200
+ margin: 20px;
201
+ }
202
+ .instant-articles-card-content-box {
203
+ width: 50%;
204
+ float: left;
205
+ box-sizing: border-box;
206
+ }
207
+ .instant-articles-card-content-box label {
208
+ font-weight: bold;
209
+ color: #4b4f56;
210
+ font-size: 1em;
211
+ }
212
+ .instant-articles-card-content-box p {
213
+ color: #9ca0a7;
214
+ font-size: 1em;
215
+ margin: 0.7em 0;
216
+ }
217
+ .instant-articles-card-content-box ol {
218
+ margin-left: 0px;
219
+ padding-left: 0px;
220
+ list-style: decimal inside none;
221
+ }
222
+ .instant-articles-card-content-box li {
223
+ color: #9ca0a7;
224
+ font-size: 1em;
225
+ margin: 0.7em 0;
226
+ }
227
+ .instant-articles-page-img {
228
+ width: 40px;
229
+ height: 40px;
230
+ background-color: black;
231
+ border: 1px solid #dddfe2;
232
+ margin-left: 5px;
233
+ }
234
+ .instant-articles-wizard-page-selection li {
235
+ margin: 0px -20px;
236
+ padding: 10px 20px;
237
+ cursor: pointer;
238
+ }
239
+ .instant-articles-radio-selected {
240
+ background-color: #f6f7f9;
241
+ }
242
+ .instant-articles-wizard-page-selection li > label {
243
+ font-weight: normal;
244
+ color: #9ca0a7;
245
+ margin-left: 10px;
246
+ }
247
+ .instant-articles-wizard-page-selection li > label span {
248
+ display: none;
249
+ }
250
+ .instant-articles-wizard-page-selection li.instant-articles-radio-selected > label > span {
251
+ display: inline-block;
252
+ margin-left: 30px;
253
+ }
254
+ .instant-articles-wizard-page-selection li.instant-articles-radio-selected > label > span.page-enabled {
255
+ font-weight: bold;
256
+ color: #69c456;
257
+ }
258
+ .instant-articles-wizard-page-selection li > * {
259
+ display:inline-block;
260
+ vertical-align:middle;
261
+ cursor: pointer;
262
+ }
263
+ .instant-articles-card-content-right {
264
+ padding-left: 10px;
265
+ }
266
+ .instant-articles-card-content-left {
267
+ padding-right: 10px;
268
+ }
269
+ .instant-articles-card-content-full {
270
+ float: none;
271
+ width: 100%;
272
+ }
273
+ .instant-articles-card-content-full .instant-articles-card-title-edit {
274
+ display: inline-block;
275
+ }
276
+ .instant-articles-card-content-full hr {
277
+ margin: 20px -20px 20px -20px;
278
+ }
279
+ .instant-articles-label {
280
+ display: block;
281
+ }
282
+ .instant-articles-input-text {
283
+ display: block;
284
+ border-radius: 4px;
285
+ margin-top: 5px;
286
+ margin-bottom: 10px;
287
+ }
288
+ .instant-articles-button {
289
+ display: inline-block;
290
+ border-radius: 4px;
291
+ background-color: #4267b2;
292
+ color: #fff !important;
293
+ text-decoration: none !important;
294
+ padding: 8px 20px;
295
+ margin-top: 10px;
296
+ border: none;
297
+ line-height: 25px;
298
+ vertical-align: middle;
299
+ cursor: pointer;
300
+ }
301
+ a.instant-articles-button {
302
+ padding: 5px 20px;
303
+ }
304
+ .instant-articles-button:hover,
305
+ .instant-articles-button:active,
306
+ .instant-articles-button:focus {
307
+ color: #fff;
308
+ }
309
+ .instant-articles-button label {
310
+ color: white;
311
+ font-weight: normal;
312
+ font-size: 1em;
313
+ margin-top: -2px;
314
+ display: inline-block;
315
+ height: 20px;
316
+ float: left;
317
+ }
318
+ .instant-articles-button-highlight {
319
+ background-color: #42b72a;
320
+ }
321
+ .instant-articles-button-disabled {
322
+ opacity: 0.4;
323
+ cursor: default !important;
324
+ }
325
+ .instant-articles-button-disabled * {
326
+ cursor: default !important;
327
+ }
328
+ .instant-articles-button-centered {
329
+ margin: 0 auto;
330
+ display: block;
331
+ }
332
+ .instant-articles-button-disabled:focus,
333
+ .instant-articles-button-disabled:active {
334
+ box-shadow: none;
335
+ outline: none;
336
+ }
337
+ .instant-articles-button-icon-facebook {
338
+ background: url('/wp-content/plugins/facebook-instant-articles-wp/assets/icon-fb-login.png');
339
+ background-size: 18px 18px;
340
+ background-repeat: no-repeat;
341
+ display: inline-block;
342
+ width: 18px;
343
+ height: 18px;
344
+ margin: 0 10px 0 -12px;
345
+ float: left;
346
+ }
347
+ .instant-articles-card-title-link {
348
+ text-decoration: none;
349
+ font-size: .7em;
350
+ font-weight: bold;
351
+ }
352
+ .instant-articles-advanced-settings {
353
+ text-decoration: none;
354
+ font-weight: bold;
355
+ margin-top: 20px;
356
+ display: block;
357
+ }
358
+ .instant-articles-advanced-settings:active,
359
+ .instant-articles-advanced-settings:focus {
360
+ outline: none;
361
+ box-shadow: none;
362
+ }
363
+
364
+ .instant-articles-wizard-advanced-settings-box {
365
+ display: none;
366
+ }
367
+
368
+ #instant_articles_wizard_messages div.error,
369
+ #instant_articles_wizard_messages div.update,
370
+ #instant_articles_wizard_messages .notice,
371
+ #instant_articles_wizard div.error,
372
+ #instant_articles_wizard div.update,
373
+ #instant_articles_wizard .notice {
374
+ margin-left: 0;
375
+ }
376
+
377
+ #instant_articles_wizard.loading {
378
+ opacity: 0.5;
379
+ position: relative;
380
+ }
381
+
382
+ #instant_articles_wizard.loading::after {
383
+ content: '';
384
+ border: 16px solid rgba(255,255,255,0); /* Light grey */
385
+ border-top: 16px solid #3498db; /* Blue */
386
+ border-radius: 50%;
387
+ width: 120px;
388
+ height: 120px;
389
+ animation: spin 1s linear infinite;
390
+ display: block;
391
+ position: absolute;
392
+ margin-top: -120px;
393
+ margin-left: -120px;
394
+ left: 50%;
395
+ top: 50%;
396
+ }
397
+ #instant-articles-wizard-signup {
398
+ display: none;
399
+ }
400
+ #instant-articles-wizard-customize-style-next {
401
+ display: none;
402
+ }
403
+ @keyframes spin {
404
+ 0% { transform: rotate(0deg); }
405
+ 100% { transform: rotate(360deg); }
406
+ }
407
+
408
+ @media screen and (max-width: 782px) {
409
+ .instant-articles-card-content-box {
410
+ clear: both;
411
+ width: 100%;
412
+ padding: 0;
413
+ }
414
+ .instant-articles-card-content-right {
415
+ margin-top: 40px;
416
+ }
417
+ }
facebook-instant-articles.php CHANGED
@@ -4,7 +4,7 @@
4
  * Description: Add support for Instant Articles for Facebook to your WordPress site.
5
  * Author: Automattic, Dekode, Facebook
6
  * Author URI: https://vip.wordpress.com/plugins/instant-articles/
7
- * Version: 3.0.1
8
  * Text Domain: instant-articles
9
  * License: GPLv2
10
  * License URI: http://www.gnu.org/licenses/gpl-2.0.html
@@ -61,7 +61,7 @@ if ( version_compare( PHP_VERSION, '5.4', '<' ) ) {
61
 
62
  defined( 'ABSPATH' ) || die( 'Shame on you' );
63
 
64
- define( 'IA_PLUGIN_VERSION', '2.11' );
65
  define( 'IA_PLUGIN_PATH_FULL', __FILE__ );
66
  define( 'IA_PLUGIN_PATH', plugin_basename( __FILE__ ) );
67
  define( 'IA_PLUGIN_FILE_BASENAME', pathinfo( __FILE__, PATHINFO_FILENAME ) );
@@ -74,7 +74,7 @@ if ( version_compare( PHP_VERSION, '5.4', '<' ) ) {
74
 
75
  require_once( dirname( __FILE__ ) . '/embeds.php' );
76
  require_once( dirname( __FILE__ ) . '/class-instant-articles-post.php' );
77
- require_once( dirname( __FILE__ ) . '/settings/class-instant-articles-settings.php' );
78
  require_once( dirname( __FILE__ ) . '/meta-box/class-instant-articles-meta-box.php' );
79
  require_once( dirname( __FILE__ ) . '/class-instant-articles-publisher.php' );
80
 
@@ -89,6 +89,21 @@ if ( version_compare( PHP_VERSION, '5.4', '<' ) ) {
89
  }
90
  register_activation_hook( __FILE__, 'instant_articles_activate' );
91
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  /**
93
  * Plugin activation hook to remove our rewrite rules.
94
  *
@@ -187,7 +202,6 @@ if ( version_compare( PHP_VERSION, '5.4', '<' ) ) {
187
  ) );
188
  }
189
  }
190
-
191
  }
192
  add_action( 'pre_get_posts', 'instant_articles_query', 10, 1 );
193
 
@@ -226,14 +240,14 @@ if ( version_compare( PHP_VERSION, '5.4', '<' ) ) {
226
  'instant-articles-meta-box',
227
  plugins_url( '/css/instant-articles-meta-box.css', __FILE__ )
228
  );
229
- wp_register_style(
230
- 'instant-articles-settings-wizard',
231
- plugins_url( '/css/instant-articles-settings-wizard.css', __FILE__ )
232
- );
233
  wp_register_style(
234
  'instant-articles-settings',
235
  plugins_url( '/css/instant-articles-settings.css', __FILE__ )
236
  );
 
 
 
 
237
 
238
  wp_register_script(
239
  'instant-articles-meta-box',
@@ -267,6 +281,13 @@ if ( version_compare( PHP_VERSION, '5.4', '<' ) ) {
267
  null,
268
  true
269
  );
 
 
 
 
 
 
 
270
  }
271
  add_action( 'init', 'instant_articles_register_scripts' );
272
 
@@ -279,12 +300,14 @@ if ( version_compare( PHP_VERSION, '5.4', '<' ) ) {
279
  wp_enqueue_style( 'instant-articles-meta-box' );
280
  wp_enqueue_style( 'instant-articles-settings-wizard' );
281
  wp_enqueue_style( 'instant-articles-settings' );
 
282
 
283
  wp_enqueue_script( 'instant-articles-meta-box' );
284
  wp_enqueue_script( 'instant-articles-option-ads' );
285
  wp_enqueue_script( 'instant-articles-option-analytics' );
286
  wp_enqueue_script( 'instant-articles-option-publishing' );
287
  wp_enqueue_script( 'instant-articles-settings' );
 
288
  }
289
  add_action( 'admin_enqueue_scripts', 'instant_articles_enqueue_scripts' );
290
 
@@ -306,12 +329,12 @@ if ( version_compare( PHP_VERSION, '5.4', '<' ) ) {
306
  }
307
  add_action( 'wp_head', 'inject_url_claiming_meta_tag' );
308
 
309
- // Initialize the Instant Articles settings page.
310
- Instant_Articles_Settings::init();
311
-
312
  // Initialize the Instant Articles meta box.
313
  Instant_Articles_Meta_Box::init();
314
 
315
  // Initialize the Instant Articles publisher.
316
  Instant_Articles_Publisher::init();
 
 
 
317
  }
4
  * Description: Add support for Instant Articles for Facebook to your WordPress site.
5
  * Author: Automattic, Dekode, Facebook
6
  * Author URI: https://vip.wordpress.com/plugins/instant-articles/
7
+ * Version: 3.1
8
  * Text Domain: instant-articles
9
  * License: GPLv2
10
  * License URI: http://www.gnu.org/licenses/gpl-2.0.html
61
 
62
  defined( 'ABSPATH' ) || die( 'Shame on you' );
63
 
64
+ define( 'IA_PLUGIN_VERSION', '3.1' );
65
  define( 'IA_PLUGIN_PATH_FULL', __FILE__ );
66
  define( 'IA_PLUGIN_PATH', plugin_basename( __FILE__ ) );
67
  define( 'IA_PLUGIN_FILE_BASENAME', pathinfo( __FILE__, PATHINFO_FILENAME ) );
74
 
75
  require_once( dirname( __FILE__ ) . '/embeds.php' );
76
  require_once( dirname( __FILE__ ) . '/class-instant-articles-post.php' );
77
+ require_once( dirname( __FILE__ ) . '/wizard/class-instant-articles-wizard.php' );
78
  require_once( dirname( __FILE__ ) . '/meta-box/class-instant-articles-meta-box.php' );
79
  require_once( dirname( __FILE__ ) . '/class-instant-articles-publisher.php' );
80
 
89
  }
90
  register_activation_hook( __FILE__, 'instant_articles_activate' );
91
 
92
+ /**
93
+ * Show a message to set up the plugin when it is activated
94
+ */
95
+ add_action( 'admin_notices', 'instant_articles_setup_admin_notice' );
96
+ add_action( 'network_admin_notices', 'instant_articles_setup_admin_notice' ); // also show message on multisite
97
+ function instant_articles_setup_admin_notice() {
98
+ global $pagenow;
99
+ if ( $pagenow === 'plugins.php' && Instant_Articles_Wizard_State::get_current_state() !== Instant_Articles_Wizard_State::STATE_REVIEW_SUBMISSION ) {
100
+ $settings_url = Instant_Articles_Wizard::get_url();
101
+ echo '<div class="updated settings-error notice is-dismissible">';
102
+ echo '<p>Congrats, you\'ve installed the Instant Articles for WP plugin. Now <a href="' . esc_url_raw($settings_url) . '">set it up</a> to start publishing Instant Articles.';
103
+ echo '</div>';
104
+ }
105
+ }
106
+
107
  /**
108
  * Plugin activation hook to remove our rewrite rules.
109
  *
202
  ) );
203
  }
204
  }
 
205
  }
206
  add_action( 'pre_get_posts', 'instant_articles_query', 10, 1 );
207
 
240
  'instant-articles-meta-box',
241
  plugins_url( '/css/instant-articles-meta-box.css', __FILE__ )
242
  );
 
 
 
 
243
  wp_register_style(
244
  'instant-articles-settings',
245
  plugins_url( '/css/instant-articles-settings.css', __FILE__ )
246
  );
247
+ wp_register_style(
248
+ 'instant-articles-wizard',
249
+ plugins_url( '/css/instant-articles-wizard.css', __FILE__ )
250
+ );
251
 
252
  wp_register_script(
253
  'instant-articles-meta-box',
281
  null,
282
  true
283
  );
284
+ wp_register_script(
285
+ 'instant-articles-wizard',
286
+ plugins_url( '/js/instant-articles-wizard.js', __FILE__ ),
287
+ null,
288
+ null,
289
+ true
290
+ );
291
  }
292
  add_action( 'init', 'instant_articles_register_scripts' );
293
 
300
  wp_enqueue_style( 'instant-articles-meta-box' );
301
  wp_enqueue_style( 'instant-articles-settings-wizard' );
302
  wp_enqueue_style( 'instant-articles-settings' );
303
+ wp_enqueue_style( 'instant-articles-wizard' );
304
 
305
  wp_enqueue_script( 'instant-articles-meta-box' );
306
  wp_enqueue_script( 'instant-articles-option-ads' );
307
  wp_enqueue_script( 'instant-articles-option-analytics' );
308
  wp_enqueue_script( 'instant-articles-option-publishing' );
309
  wp_enqueue_script( 'instant-articles-settings' );
310
+ wp_enqueue_script( 'instant-articles-wizard' );
311
  }
312
  add_action( 'admin_enqueue_scripts', 'instant_articles_enqueue_scripts' );
313
 
329
  }
330
  add_action( 'wp_head', 'inject_url_claiming_meta_tag' );
331
 
 
 
 
332
  // Initialize the Instant Articles meta box.
333
  Instant_Articles_Meta_Box::init();
334
 
335
  // Initialize the Instant Articles publisher.
336
  Instant_Articles_Publisher::init();
337
+
338
+ // Initialize the Instant Articles Wizard page.
339
+ Instant_Articles_Wizard::init();
340
  }
js/instant-articles-settings.js CHANGED
@@ -1,25 +1,8 @@
1
  jQuery( function () {
2
- jQuery( '#instant-articles-fb-page-selector' ).change(function () {
3
- var val = jQuery( this ).val();
4
- var obj = jQuery.parseJSON( val );
5
-
6
- if ( val ) {
7
- jQuery( 'input[name=instant-articles-select-page]' ).attr( 'disabled', false );
8
- jQuery.each( obj, function ( key, value ) {
9
- jQuery( 'input[name="instant-articles-option-fb-page[' + key + ']"]' )
10
- .val( value );
11
- });
12
- }
13
- else {
14
- jQuery( 'input[name=instant-articles-select-page]' ).attr( 'disabled', true );
15
- }
16
- } ).trigger( 'change' );
17
-
18
-
19
  jQuery( '.instant-articles-settings-box > h2' ).click(function () {
20
  $h2 = jQuery( this );
21
  $h2.siblings('.inside').toggle();
22
  $h2.toggleClass('dashicons-arrow-down');
23
  $h2.toggleClass('dashicons-arrow-right');
24
- })
25
  } );
1
  jQuery( function () {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  jQuery( '.instant-articles-settings-box > h2' ).click(function () {
3
  $h2 = jQuery( this );
4
  $h2.siblings('.inside').toggle();
5
  $h2.toggleClass('dashicons-arrow-down');
6
  $h2.toggleClass('dashicons-arrow-right');
7
+ });
8
  } );
js/instant-articles-wizard.js ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ function instant_articles_wizard_load ( data ) {
2
+ jQuery( '#instant_articles_wizard' ).addClass( 'loading' );
3
+ jQuery.post( ajaxurl, data, function( response ) {
4
+ jQuery( '#instant_articles_wizard' ).html( response );
5
+ instant_articles_wizard_bind_events();
6
+ jQuery( '#instant_articles_wizard' ).removeClass( 'loading' );
7
+ window.scrollTo(0, 0);
8
+ }, 'html' );
9
+ }
10
+
11
+ function instant_articles_wizard_transition ( new_state, params ) {
12
+ instant_articles_wizard_load ( {
13
+ 'action': 'instant_articles_wizard_transition',
14
+ 'new_state': new_state,
15
+ 'params': JSON.stringify(params)
16
+ } );
17
+ }
18
+
19
+ function instant_articles_wizard_save_app ( app_id, app_secret ) {
20
+ instant_articles_wizard_load ( {
21
+ 'action': 'instant_articles_wizard_save_app',
22
+ 'app_id': app_id,
23
+ 'app_secret': app_secret
24
+ } );
25
+ }
26
+
27
+ function instant_articles_wizard_edit_app () {
28
+ instant_articles_wizard_load ( {
29
+ 'action': 'instant_articles_wizard_edit_app',
30
+ } );
31
+ }
32
+
33
+
34
+ function instant_articles_wizard_submit_for_review () {
35
+ instant_articles_wizard_load ( {
36
+ 'action': 'instant_articles_wizard_submit_for_review',
37
+ } );
38
+ }
39
+
40
+ function instant_articles_wizard_bind_events () {
41
+
42
+ jQuery( '#instant_articles_wizard a' ).on( 'click', function () {
43
+ if ( ! jQuery( this ).attr( 'target' ) ) {
44
+ jQuery( '#instant_articles_wizard' ).addClass( 'loading' );
45
+ }
46
+ });
47
+
48
+ jQuery( '.instant-articles-wizard-transition' ).on( 'click', function () {
49
+ if ( jQuery( this ).hasClass( 'instant-articles-button-disabled' ) ) {
50
+ return false;
51
+ }
52
+ instant_articles_wizard_transition( jQuery( this ).attr( 'data-new-state' ) );
53
+ });
54
+
55
+ jQuery('input[name=app_id], input[name=app_secret]').on( 'change', function () {
56
+ var app_id = jQuery('input[name=app_id]').val();
57
+ var app_secret = jQuery('input[name=app_secret]').val();
58
+ if (app_id && app_secret) {
59
+ jQuery( '#instant-articles-wizard-save-app' ).removeClass( 'instant-articles-button-disabled' );
60
+ }
61
+ else {
62
+ jQuery( '#instant-articles-wizard-save-app' ).addClass( 'instant-articles-button-disabled' );
63
+ }
64
+ });
65
+
66
+ jQuery('input[name=page_id]').on( 'change', function () {
67
+ var input = jQuery( this );
68
+ var page_id = jQuery('input[name=page_id]:checked').val();
69
+ var signed_up = ( jQuery('input[name=page_id]:checked').attr( 'data-signed-up' ) === 'yes' );
70
+ if ( page_id && signed_up ) {
71
+ jQuery( '#instant-articles-wizard-select-page' ).removeClass( 'instant-articles-button-disabled' );
72
+ }
73
+ else {
74
+ jQuery( '#instant-articles-wizard-select-page' ).addClass( 'instant-articles-button-disabled' );
75
+ }
76
+ });
77
+
78
+ jQuery( '#instant-articles-wizard-save-app' ).on( 'click', function () {
79
+ if ( jQuery( this ).hasClass( 'instant-articles-button-disabled' ) ) {
80
+ return false;
81
+ }
82
+ var app_id = jQuery('input[name=app_id]').val();
83
+ var app_secret = jQuery('input[name=app_secret]').val();
84
+ instant_articles_wizard_save_app( app_id, app_secret );
85
+ });
86
+
87
+ jQuery( '#instant-articles-wizard-edit-app' ).on( 'click', function () {
88
+ if ( jQuery( this ).hasClass( 'instant-articles-button-disabled' ) ) {
89
+ return false;
90
+ }
91
+ instant_articles_wizard_edit_app();
92
+ });
93
+
94
+ jQuery( '#instant-articles-wizard-submit-for-review' ).on( 'click', function () {
95
+ if ( jQuery( this ).hasClass( 'instant-articles-button-disabled' ) ) {
96
+ return false;
97
+ }
98
+ instant_articles_wizard_submit_for_review();
99
+ });
100
+
101
+ jQuery( '#instant-articles-wizard-select-page' ).on( 'click', function () {
102
+ if ( jQuery( this ).hasClass( 'instant-articles-button-disabled' ) ) {
103
+ return false;
104
+ }
105
+ var page_id = jQuery('input[name=page_id]:checked').val();
106
+ instant_articles_wizard_transition( 'STATE_STYLE_SELECTION', { page_id: page_id } );
107
+ });
108
+
109
+ jQuery( '#instant-articles-wizard-customize-style' ).on( 'click', function () {
110
+ jQuery( '#instant-articles-wizard-customize-style-next' ).show();
111
+ });
112
+
113
+ jQuery( '.instant-articles-card-content-box li' ).on( 'click', function () {
114
+ jQuery( '.instant-articles-card-content-box li.instant-articles-radio-selected' ).removeClass( 'instant-articles-radio-selected' );
115
+ jQuery( this ).find( 'input' ).attr( 'checked', 'checked' );
116
+ jQuery( this ).find( 'input' ).trigger( 'change' );
117
+ jQuery( this ).toggleClass( 'instant-articles-radio-selected' );
118
+ });
119
+ }
120
+
121
+
122
+ jQuery( document ).ready( function () {
123
+ instant_articles_wizard_bind_events();
124
+
125
+ jQuery( '.instant-articles-wizard-toggle' ).on( 'click', function () {
126
+ var text = jQuery( this ).text();
127
+ if ( text.indexOf( '►' ) !== -1 ) {
128
+ text = text.replace( '►', '▼' );
129
+ }
130
+ else {
131
+ text = text.replace( '▼', '►' );
132
+ }
133
+ jQuery( this ).text( text ).next().slideToggle();
134
+ return false;
135
+ });
136
+ });
meta-box/class-instant-articles-meta-box.php CHANGED
@@ -65,8 +65,8 @@ class Instant_Articles_Meta_Box {
65
  $submission_status = null;
66
  $published = 'publish' === $post->post_status;
67
 
68
- Instant_Articles_Settings::menu_items();
69
- $settings_page_href = Instant_Articles_Settings::get_href_to_settings_page();
70
 
71
  if ( $published ) {
72
  try {
65
  $submission_status = null;
66
  $published = 'publish' === $post->post_status;
67
 
68
+ Instant_Articles_Wizard::menu_items();
69
+ $settings_page_href = Instant_Articles_Wizard::get_url();
70
 
71
  if ( $published ) {
72
  try {
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: trrine, olethomas, bjornjohansen, dekode, automattic, facebook
3
  Tags: instant articles, facebook, mobile
4
  Requires at least: 4.3
5
  Tested up to: 4.6
6
- Stable tag: 3.0.1
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
@@ -85,6 +85,24 @@ Usually simply visiting the permalinks settings page in the WordPress dashboard
85
 
86
  == Changelog ==
87
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  = 3.0.1 =
89
 
90
  * Fix overzealous escaping
3
  Tags: instant articles, facebook, mobile
4
  Requires at least: 4.3
5
  Tested up to: 4.6
6
+ Stable tag: 3.1
7
  License: GPLv2 or later
8
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
9
 
85
 
86
  == Changelog ==
87
 
88
+ = 3.1 =
89
+ * New on-boarding flow wizard
90
+ * Automattic URL claiming
91
+ * Submit for review from wizard
92
+ * Improved transformation rules
93
+ * Option to submit only articles without warnings
94
+ * Jetpack compatibility
95
+ * Added Jetpack carousel rules
96
+ * Compatibility layer for Get The Image plugin
97
+ * Fix for relative URL checking
98
+ * Fix for missing subtitles
99
+ * Fix for double call of wpautop
100
+ * Fix for loadHTML warnings
101
+ * Fix for get_cover_media function
102
+ * Fix to prevent publishing of password protected posts
103
+
104
+ props diegoquinteiro everton-rosario gemedet jacobarriola menzow rinatkhaziev srtfisher
105
+
106
  = 3.0.1 =
107
 
108
  * Fix overzealous escaping
rules-configuration.json CHANGED
@@ -63,8 +63,8 @@
63
  "class": "PassThroughRule",
64
  "selector": "blockquote p"
65
  }, {
66
- "class": "ImageRule",
67
- "selector": "//p[img]",
68
  "properties": {
69
  "image.url": {
70
  "type": "string",
@@ -142,21 +142,60 @@
142
  "interactive.iframe" : {
143
  "type" : "children",
144
  "selector" : "iframe"
 
 
 
 
 
 
 
 
 
145
  },
146
- "interactive.caption" : {
147
- "type" : "element",
148
- "selector" : "figcaption"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  }
150
  }
151
  }, {
152
  "class": "InteractiveRule",
153
- "selector" : "//p[iframe]",
154
  "properties" : {
155
  "interactive.url" : {
156
  "type" : "string",
157
  "selector" : "iframe",
158
  "attribute": "src"
159
  },
 
 
 
 
 
160
  "interactive.width" : {
161
  "type" : "int",
162
  "selector" : "iframe",
@@ -166,27 +205,92 @@
166
  "type" : "int",
167
  "selector" : "iframe",
168
  "attribute": "height"
 
 
 
 
 
 
 
 
 
 
169
  },
170
  "interactive.iframe" : {
171
  "type" : "children",
172
  "selector" : "iframe"
173
  },
174
- "interactive.caption" : {
175
- "type" : "element",
176
- "selector" : "figcaption"
 
 
 
 
 
 
177
  }
178
  }
179
  }, {
180
  "class": "InteractiveRule",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
  "selector" : "div.embed",
182
  "properties" : {
183
  "interactive.iframe" : {
184
  "type" : "children",
185
  "selector" : "div.embed"
 
 
 
 
 
 
 
 
 
 
186
  }
187
  }
188
  }, {
189
- "class": "InteractiveRule",
190
  "selector" : "div.interactive",
191
  "properties" : {
192
  "interactive.iframe" : {
@@ -198,13 +302,14 @@
198
  "selector" : "iframe",
199
  "attribute": "height"
200
  },
201
- "interactive.iframe" : {
202
- "type" : "children",
203
- "selector" : "iframe"
 
204
  }
205
  }
206
  }, {
207
- "class": "InteractiveRule",
208
  "selector" : "//div[@class='embed' and iframe]",
209
  "properties" : {
210
  "interactive.url" : {
@@ -212,6 +317,11 @@
212
  "selector" : "iframe",
213
  "attribute": "src"
214
  },
 
 
 
 
 
215
  "interactive.width" : {
216
  "type" : "int",
217
  "selector" : "iframe",
@@ -224,23 +334,47 @@
224
  }
225
  }
226
  }, {
227
- "class": "InteractiveRule",
228
  "selector" : "//div[@class='interactive' and iframe]",
229
  "properties" : {
230
  "interactive.url" : {
231
  "type" : "string",
232
  "selector" : "iframe",
233
  "attribute": "src"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  }
235
  }
236
  }, {
237
- "class": "ImageRule",
238
- "selector": "//p[a[img]]|//a[img]",
239
- "properties": {
240
- "image.url": {
241
- "type": "string",
242
- "selector": "img",
243
- "attribute": "src"
 
 
 
 
 
 
 
 
 
244
  }
245
  }
246
  }, {
@@ -290,14 +424,5 @@
290
  "attribute": "type"
291
  }
292
  }
293
- }, {
294
- "class": "InteractiveRule",
295
- "selector" : "table",
296
- "properties" : {
297
- "interactive.iframe" : {
298
- "type" : "element",
299
- "selector" : "table"
300
- }
301
- }
302
- }]
303
  }
63
  "class": "PassThroughRule",
64
  "selector": "blockquote p"
65
  }, {
66
+ "class": "ImageInsideParagraphRule",
67
+ "selector": "img",
68
  "properties": {
69
  "image.url": {
70
  "type": "string",
142
  "interactive.iframe" : {
143
  "type" : "children",
144
  "selector" : "iframe"
145
+ }
146
+ }
147
+ }, {
148
+ "class": "InteractiveRule",
149
+ "selector" : "div.embed",
150
+ "properties" : {
151
+ "interactive.iframe" : {
152
+ "type" : "children",
153
+ "selector" : "div.embed"
154
  },
155
+ "interactive.height" : {
156
+ "type" : "int",
157
+ "selector" : "iframe",
158
+ "attribute": "height"
159
+ },
160
+ "interactive.width" : {
161
+ "type" : "int",
162
+ "selector" : "iframe",
163
+ "attribute": "width"
164
+ }
165
+ }
166
+ }, {
167
+ "class": "InteractiveRule",
168
+ "selector" : "div.interactive",
169
+ "properties" : {
170
+ "interactive.iframe" : {
171
+ "type" : "children",
172
+ "selector" : "div.interactive"
173
+ },
174
+ "interactive.height" : {
175
+ "type" : "int",
176
+ "selector" : "iframe",
177
+ "attribute": "height"
178
+ },
179
+ "interactive.width" : {
180
+ "type" : "int",
181
+ "selector" : "iframe",
182
+ "attribute": "width"
183
  }
184
  }
185
  }, {
186
  "class": "InteractiveRule",
187
+ "selector" : "//div[@class='embed' and iframe]",
188
  "properties" : {
189
  "interactive.url" : {
190
  "type" : "string",
191
  "selector" : "iframe",
192
  "attribute": "src"
193
  },
194
+ "interactive.iframe" : {
195
+ "type" : "children",
196
+ "selector" : "iframe",
197
+ "attribute": "src"
198
+ },
199
  "interactive.width" : {
200
  "type" : "int",
201
  "selector" : "iframe",
205
  "type" : "int",
206
  "selector" : "iframe",
207
  "attribute": "height"
208
+ }
209
+ }
210
+ }, {
211
+ "class": "InteractiveRule",
212
+ "selector" : "//div[@class='interactive' and iframe]",
213
+ "properties" : {
214
+ "interactive.url" : {
215
+ "type" : "string",
216
+ "selector" : "iframe",
217
+ "attribute": "src"
218
  },
219
  "interactive.iframe" : {
220
  "type" : "children",
221
  "selector" : "iframe"
222
  },
223
+ "interactive.height" : {
224
+ "type" : "int",
225
+ "selector" : "iframe",
226
+ "attribute": "height"
227
+ },
228
+ "interactive.width" : {
229
+ "type" : "int",
230
+ "selector" : "iframe",
231
+ "attribute": "width"
232
  }
233
  }
234
  }, {
235
  "class": "InteractiveRule",
236
+ "selector" : "table",
237
+ "properties" : {
238
+ "interactive.iframe" : {
239
+ "type" : "element",
240
+ "selector" : "table"
241
+ },
242
+ "interactive.height" : {
243
+ "type" : "int",
244
+ "selector" : "table",
245
+ "attribute": "height"
246
+ },
247
+ "interactive.width" : {
248
+ "type" : "int",
249
+ "selector" : "iframe",
250
+ "attribute": "width"
251
+ }
252
+ }
253
+ }, {
254
+ "class": "InteractiveInsideParagraphRule",
255
+ "selector" : "iframe",
256
+ "properties" : {
257
+ "interactive.url" : {
258
+ "type" : "string",
259
+ "selector" : "iframe",
260
+ "attribute": "src"
261
+ },
262
+ "interactive.height" : {
263
+ "type" : "int",
264
+ "selector" : "iframe",
265
+ "attribute": "height"
266
+ },
267
+ "interactive.width" : {
268
+ "type" : "int",
269
+ "selector" : "iframe",
270
+ "attribute": "width"
271
+ }
272
+ }
273
+ },{
274
+ "class": "InteractiveInsideParagraphRule",
275
  "selector" : "div.embed",
276
  "properties" : {
277
  "interactive.iframe" : {
278
  "type" : "children",
279
  "selector" : "div.embed"
280
+ },
281
+ "interactive.height" : {
282
+ "type" : "int",
283
+ "selector" : "iframe",
284
+ "attribute": "height"
285
+ },
286
+ "interactive.width" : {
287
+ "type" : "int",
288
+ "selector" : "iframe",
289
+ "attribute": "width"
290
  }
291
  }
292
  }, {
293
+ "class": "InteractiveInsideParagraphRule",
294
  "selector" : "div.interactive",
295
  "properties" : {
296
  "interactive.iframe" : {
302
  "selector" : "iframe",
303
  "attribute": "height"
304
  },
305
+ "interactive.width" : {
306
+ "type" : "int",
307
+ "selector" : "iframe",
308
+ "attribute": "width"
309
  }
310
  }
311
  }, {
312
+ "class": "InteractiveInsideParagraphRule",
313
  "selector" : "//div[@class='embed' and iframe]",
314
  "properties" : {
315
  "interactive.url" : {
317
  "selector" : "iframe",
318
  "attribute": "src"
319
  },
320
+ "interactive.iframe" : {
321
+ "type" : "children",
322
+ "selector" : "iframe",
323
+ "attribute": "src"
324
+ },
325
  "interactive.width" : {
326
  "type" : "int",
327
  "selector" : "iframe",
334
  }
335
  }
336
  }, {
337
+ "class": "InteractiveInsideParagraphRule",
338
  "selector" : "//div[@class='interactive' and iframe]",
339
  "properties" : {
340
  "interactive.url" : {
341
  "type" : "string",
342
  "selector" : "iframe",
343
  "attribute": "src"
344
+ },
345
+ "interactive.iframe" : {
346
+ "type" : "children",
347
+ "selector" : "iframe",
348
+ "attribute": "src"
349
+ },
350
+ "interactive.height" : {
351
+ "type" : "int",
352
+ "selector" : "iframe",
353
+ "attribute": "height"
354
+ },
355
+ "interactive.width" : {
356
+ "type" : "int",
357
+ "selector" : "iframe",
358
+ "attribute": "width"
359
  }
360
  }
361
  }, {
362
+ "class": "InteractiveInsideParagraphRule",
363
+ "selector" : "table",
364
+ "properties" : {
365
+ "interactive.iframe" : {
366
+ "type" : "element",
367
+ "selector" : "table"
368
+ },
369
+ "interactive.height" : {
370
+ "type" : "int",
371
+ "selector" : "table",
372
+ "attribute": "height"
373
+ },
374
+ "interactive.width" : {
375
+ "type" : "int",
376
+ "selector" : "table",
377
+ "attribute": "width"
378
  }
379
  }
380
  }, {
424
  "attribute": "type"
425
  }
426
  }
427
+ }]
 
 
 
 
 
 
 
 
 
428
  }
vendor/autoload.php CHANGED
@@ -4,4 +4,4 @@
4
 
5
  require_once __DIR__ . '/composer' . '/autoload_real.php';
6
 
7
- return ComposerAutoloaderInit337744729a91b1fdea5f68820e3d1dfe::getLoader();
4
 
5
  require_once __DIR__ . '/composer' . '/autoload_real.php';
6
 
7
+ return ComposerAutoloaderInit84d77f682115366106947dee11e898d3::getLoader();
vendor/composer/autoload_real.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
- class ComposerAutoloaderInit337744729a91b1fdea5f68820e3d1dfe
6
  {
7
  private static $loader;
8
 
@@ -19,15 +19,15 @@ class ComposerAutoloaderInit337744729a91b1fdea5f68820e3d1dfe
19
  return self::$loader;
20
  }
21
 
22
- spl_autoload_register(array('ComposerAutoloaderInit337744729a91b1fdea5f68820e3d1dfe', 'loadClassLoader'), true, true);
23
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
- spl_autoload_unregister(array('ComposerAutoloaderInit337744729a91b1fdea5f68820e3d1dfe', 'loadClassLoader'));
25
 
26
  $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION');
27
  if ($useStaticLoader) {
28
  require_once __DIR__ . '/autoload_static.php';
29
 
30
- call_user_func(\Composer\Autoload\ComposerStaticInit337744729a91b1fdea5f68820e3d1dfe::getInitializer($loader));
31
  } else {
32
  $map = require __DIR__ . '/autoload_namespaces.php';
33
  foreach ($map as $namespace => $path) {
@@ -48,19 +48,19 @@ class ComposerAutoloaderInit337744729a91b1fdea5f68820e3d1dfe
48
  $loader->register(true);
49
 
50
  if ($useStaticLoader) {
51
- $includeFiles = Composer\Autoload\ComposerStaticInit337744729a91b1fdea5f68820e3d1dfe::$files;
52
  } else {
53
  $includeFiles = require __DIR__ . '/autoload_files.php';
54
  }
55
  foreach ($includeFiles as $fileIdentifier => $file) {
56
- composerRequire337744729a91b1fdea5f68820e3d1dfe($fileIdentifier, $file);
57
  }
58
 
59
  return $loader;
60
  }
61
  }
62
 
63
- function composerRequire337744729a91b1fdea5f68820e3d1dfe($fileIdentifier, $file)
64
  {
65
  if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
66
  require $file;
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
+ class ComposerAutoloaderInit84d77f682115366106947dee11e898d3
6
  {
7
  private static $loader;
8
 
19
  return self::$loader;
20
  }
21
 
22
+ spl_autoload_register(array('ComposerAutoloaderInit84d77f682115366106947dee11e898d3', 'loadClassLoader'), true, true);
23
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
+ spl_autoload_unregister(array('ComposerAutoloaderInit84d77f682115366106947dee11e898d3', 'loadClassLoader'));
25
 
26
  $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION');
27
  if ($useStaticLoader) {
28
  require_once __DIR__ . '/autoload_static.php';
29
 
30
+ call_user_func(\Composer\Autoload\ComposerStaticInit84d77f682115366106947dee11e898d3::getInitializer($loader));
31
  } else {
32
  $map = require __DIR__ . '/autoload_namespaces.php';
33
  foreach ($map as $namespace => $path) {
48
  $loader->register(true);
49
 
50
  if ($useStaticLoader) {
51
+ $includeFiles = Composer\Autoload\ComposerStaticInit84d77f682115366106947dee11e898d3::$files;
52
  } else {
53
  $includeFiles = require __DIR__ . '/autoload_files.php';
54
  }
55
  foreach ($includeFiles as $fileIdentifier => $file) {
56
+ composerRequire84d77f682115366106947dee11e898d3($fileIdentifier, $file);
57
  }
58
 
59
  return $loader;
60
  }
61
  }
62
 
63
+ function composerRequire84d77f682115366106947dee11e898d3($fileIdentifier, $file)
64
  {
65
  if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
66
  require $file;
vendor/composer/autoload_static.php CHANGED
@@ -4,7 +4,7 @@
4
 
5
  namespace Composer\Autoload;
6
 
7
- class ComposerStaticInit337744729a91b1fdea5f68820e3d1dfe
8
  {
9
  public static $files = array (
10
  '7e702cccdb9dd904f2ccf22e5f37abae' => __DIR__ . '/..' . '/facebook/php-sdk-v4/src/Facebook/polyfills.php',
@@ -122,9 +122,9 @@ class ComposerStaticInit337744729a91b1fdea5f68820e3d1dfe
122
  public static function getInitializer(ClassLoader $loader)
123
  {
124
  return \Closure::bind(function () use ($loader) {
125
- $loader->prefixLengthsPsr4 = ComposerStaticInit337744729a91b1fdea5f68820e3d1dfe::$prefixLengthsPsr4;
126
- $loader->prefixDirsPsr4 = ComposerStaticInit337744729a91b1fdea5f68820e3d1dfe::$prefixDirsPsr4;
127
- $loader->classMap = ComposerStaticInit337744729a91b1fdea5f68820e3d1dfe::$classMap;
128
 
129
  }, null, ClassLoader::class);
130
  }
4
 
5
  namespace Composer\Autoload;
6
 
7
+ class ComposerStaticInit84d77f682115366106947dee11e898d3
8
  {
9
  public static $files = array (
10
  '7e702cccdb9dd904f2ccf22e5f37abae' => __DIR__ . '/..' . '/facebook/php-sdk-v4/src/Facebook/polyfills.php',
122
  public static function getInitializer(ClassLoader $loader)
123
  {
124
  return \Closure::bind(function () use ($loader) {
125
+ $loader->prefixLengthsPsr4 = ComposerStaticInit84d77f682115366106947dee11e898d3::$prefixLengthsPsr4;
126
+ $loader->prefixDirsPsr4 = ComposerStaticInit84d77f682115366106947dee11e898d3::$prefixDirsPsr4;
127
+ $loader->classMap = ComposerStaticInit84d77f682115366106947dee11e898d3::$classMap;
128
 
129
  }, null, ClassLoader::class);
130
  }
vendor/composer/installed.json CHANGED
@@ -33,17 +33,17 @@
33
  },
34
  {
35
  "name": "facebook/php-sdk-v4",
36
- "version": "5.2.0",
37
- "version_normalized": "5.2.0.0",
38
  "source": {
39
  "type": "git",
40
- "url": "https://github.com/facebook/facebook-php-sdk-v4.git",
41
- "reference": "c60fba1fe1de62db1e0776d32fc2fb778189459c"
42
  },
43
  "dist": {
44
  "type": "zip",
45
- "url": "https://api.github.com/repos/facebook/facebook-php-sdk-v4/zipball/c60fba1fe1de62db1e0776d32fc2fb778189459c",
46
- "reference": "c60fba1fe1de62db1e0776d32fc2fb778189459c",
47
  "shasum": ""
48
  },
49
  "require": {
@@ -57,7 +57,7 @@
57
  "suggest": {
58
  "guzzlehttp/guzzle": "Allows for implementation of the Guzzle HTTP client"
59
  },
60
- "time": "2016-05-18 22:04:36",
61
  "type": "library",
62
  "extra": {
63
  "branch-alias": {
@@ -88,12 +88,13 @@
88
  "keywords": [
89
  "facebook",
90
  "sdk"
91
- ]
 
92
  },
93
  {
94
  "name": "symfony/css-selector",
95
- "version": "v2.8.8",
96
- "version_normalized": "2.8.8.0",
97
  "source": {
98
  "type": "git",
99
  "url": "https://github.com/symfony/css-selector.git",
@@ -147,17 +148,17 @@
147
  },
148
  {
149
  "name": "facebook/facebook-instant-articles-sdk-php",
150
- "version": "v1.3.0",
151
- "version_normalized": "1.3.0.0",
152
  "source": {
153
  "type": "git",
154
  "url": "https://github.com/facebook/facebook-instant-articles-sdk-php.git",
155
- "reference": "4b74463f347c49f9592ab35b70129bb15b4b1e6d"
156
  },
157
  "dist": {
158
  "type": "zip",
159
- "url": "https://api.github.com/repos/facebook/facebook-instant-articles-sdk-php/zipball/4b74463f347c49f9592ab35b70129bb15b4b1e6d",
160
- "reference": "4b74463f347c49f9592ab35b70129bb15b4b1e6d",
161
  "shasum": ""
162
  },
163
  "require": {
@@ -171,7 +172,7 @@
171
  "phpunit/phpunit": "^4.8",
172
  "squizlabs/php_codesniffer": "^2.6.0"
173
  },
174
- "time": "2016-07-12 00:57:23",
175
  "type": "library",
176
  "installation-source": "dist",
177
  "autoload": {
33
  },
34
  {
35
  "name": "facebook/php-sdk-v4",
36
+ "version": "5.3.1",
37
+ "version_normalized": "5.3.1.0",
38
  "source": {
39
  "type": "git",
40
+ "url": "https://github.com/facebook/php-graph-sdk.git",
41
+ "reference": "7ed1ecdae6a5b2f8b8f60e132d06594b39b16fb1"
42
  },
43
  "dist": {
44
  "type": "zip",
45
+ "url": "https://api.github.com/repos/facebook/php-graph-sdk/zipball/7ed1ecdae6a5b2f8b8f60e132d06594b39b16fb1",
46
+ "reference": "7ed1ecdae6a5b2f8b8f60e132d06594b39b16fb1",
47
  "shasum": ""
48
  },
49
  "require": {
57
  "suggest": {
58
  "guzzlehttp/guzzle": "Allows for implementation of the Guzzle HTTP client"
59
  },
60
+ "time": "2016-08-09 11:32:26",
61
  "type": "library",
62
  "extra": {
63
  "branch-alias": {
88
  "keywords": [
89
  "facebook",
90
  "sdk"
91
+ ],
92
+ "abandoned": "facebook/graph-sdk"
93
  },
94
  {
95
  "name": "symfony/css-selector",
96
+ "version": "v2.8.9",
97
+ "version_normalized": "2.8.9.0",
98
  "source": {
99
  "type": "git",
100
  "url": "https://github.com/symfony/css-selector.git",
148
  },
149
  {
150
  "name": "facebook/facebook-instant-articles-sdk-php",
151
+ "version": "v1.4.0",
152
+ "version_normalized": "1.4.0.0",
153
  "source": {
154
  "type": "git",
155
  "url": "https://github.com/facebook/facebook-instant-articles-sdk-php.git",
156
+ "reference": "c46c25b2d1c4100885383667cde1a863440aff2a"
157
  },
158
  "dist": {
159
  "type": "zip",
160
+ "url": "https://api.github.com/repos/facebook/facebook-instant-articles-sdk-php/zipball/c46c25b2d1c4100885383667cde1a863440aff2a",
161
+ "reference": "c46c25b2d1c4100885383667cde1a863440aff2a",
162
  "shasum": ""
163
  },
164
  "require": {
172
  "phpunit/phpunit": "^4.8",
173
  "squizlabs/php_codesniffer": "^2.6.0"
174
  },
175
+ "time": "2016-08-13 06:34:04",
176
  "type": "library",
177
  "installation-source": "dist",
178
  "autoload": {
vendor/facebook/facebook-instant-articles-sdk-php/.gitignore DELETED
@@ -1,4 +0,0 @@
1
- composer.phar
2
- /vendor/
3
- *.ignored
4
- **/IgnoredTest.php
 
 
 
 
vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Client/Client.php CHANGED
@@ -242,4 +242,42 @@ class Client
242
 
243
  return $articleURLs;
244
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
  }
242
 
243
  return $articleURLs;
244
  }
245
+
246
+ /**
247
+ * Claims an URL for the page
248
+ *
249
+ * @param string $url The root URL of the site
250
+ */
251
+ public function claimURL($url)
252
+ {
253
+ // Remove protocol from the URL
254
+ $url = preg_replace('/^https?:\/\//i', '', $url);
255
+ $response = $this->facebook->post($this->pageID . '/claimed_urls?url=' . urlencode($url));
256
+ $node = $response->getGraphNode();
257
+ $error = $node->getField('error');
258
+ $success = $node->getField('success');
259
+ if ($error) {
260
+ throw new ClientException($error['error_user_msg']);
261
+ }
262
+ if (!$success) {
263
+ throw new ClientException('Could not claim the URL');
264
+ }
265
+ }
266
+
267
+ /**
268
+ * Submits the page for review
269
+ */
270
+ public function submitForReview()
271
+ {
272
+ $response = $this->facebook->post($this->pageID . '/?instant_articles_submit_for_review=true');
273
+ $node = $response->getGraphNode();
274
+ $error = $node->getField('error');
275
+ $success = $node->getField('success');
276
+ if ($error) {
277
+ throw new ClientException($error['error_user_msg']);
278
+ }
279
+ if (!$success) {
280
+ throw new ClientException('Could not submit for review');
281
+ }
282
+ }
283
  }
vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Client/Helper.php CHANGED
@@ -89,7 +89,7 @@ class Helper
89
  // Request the list of pages and associated page tokens that are
90
  // connected to this user
91
  try {
92
- $response = $this->facebook->get('/me/accounts?fields=name,id,access_token,supports_instant_articles');
93
  } catch (FacebookResponseException $e) {
94
  throw new FacebookSDKException('Graph API returned an error: ' . $e->getMessage());
95
  }
89
  // Request the list of pages and associated page tokens that are
90
  // connected to this user
91
  try {
92
+ $response = $this->facebook->get('/me/accounts?fields=name,id,access_token,supports_instant_articles,picture');
93
  } catch (FacebookResponseException $e) {
94
  throw new FacebookSDKException('Graph API returned an error: ' . $e->getMessage());
95
  }
vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Elements/InstantArticle.php CHANGED
@@ -34,7 +34,7 @@ use Facebook\InstantArticles\Validators\Type;
34
 
35
  class InstantArticle extends Element implements Container, InstantArticleInterface
36
  {
37
- const CURRENT_VERSION = '1.3.0';
38
 
39
  /**
40
  * The meta properties that are used on <head>
@@ -231,6 +231,50 @@ class InstantArticle extends Element implements Container, InstantArticleInterfa
231
  return $this;
232
  }
233
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  /**
235
  * @return Header header element from the InstantArticle
236
  */
34
 
35
  class InstantArticle extends Element implements Container, InstantArticleInterface
36
  {
37
+ const CURRENT_VERSION = '1.4.0';
38
 
39
  /**
40
  * The meta properties that are used on <head>
231
  return $this;
232
  }
233
 
234
+ /**
235
+ * Adds new child elements to the front of this InstantArticle
236
+ *
237
+ * @param Element to be added to this Article.
238
+ *
239
+ * @return $this
240
+ */
241
+ public function unshiftChild($child)
242
+ {
243
+ Type::enforce(
244
+ $child,
245
+ [
246
+ Ad::getClassName(),
247
+ Analytics::getClassName(),
248
+ AnimatedGIF::getClassName(),
249
+ Audio::getClassName(),
250
+ Blockquote::getClassName(),
251
+ Image::getClassName(),
252
+ H1::getClassName(),
253
+ H2::getClassName(),
254
+ Interactive::getClassName(),
255
+ ListElement::getClassName(),
256
+ Map::getClassName(),
257
+ Paragraph::getClassName(),
258
+ Pullquote::getClassName(),
259
+ RelatedArticles::getClassName(),
260
+ Slideshow::getClassName(),
261
+ SocialEmbed::getClassName(),
262
+ Video::getClassName()
263
+ ]
264
+ );
265
+ array_unshift($this->children, $child);
266
+
267
+ return $this;
268
+ }
269
+
270
+ /**
271
+ * @return string canonicalURL from the InstantArticle
272
+ */
273
+ public function getCanonicalURL()
274
+ {
275
+ return $this->canonicalURL;
276
+ }
277
+
278
  /**
279
  * @return Header header element from the InstantArticle
280
  */
vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Elements/Interactive.php CHANGED
@@ -250,7 +250,7 @@ class Interactive extends ElementWithHTML implements Container
250
  */
251
  public function isValid()
252
  {
253
- return !Type::isTextEmpty($this->source) || $this->html;
254
  }
255
 
256
  /**
250
  */
251
  public function isValid()
252
  {
253
+ return $this->html || (!Type::isTextEmpty($this->source) && $this->height && $this->width);
254
  }
255
 
256
  /**
vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Parser/instant-articles-rules.json CHANGED
@@ -444,6 +444,22 @@
444
  "selector" : "figure[data-feedback*='fb:likes']",
445
  "attribute": "data-feedback"
446
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
447
  "image.comments" : {
448
  "type" : "exists",
449
  "selector" : "figure[data-feedback*='fb:comments']",
444
  "selector" : "figure[data-feedback*='fb:likes']",
445
  "attribute": "data-feedback"
446
  },
447
+ "aspect-fit": {
448
+ "type": "exists",
449
+ "selector" : "figure[data-mode=aspect-fit]"
450
+ },
451
+ "aspect-fit-only": {
452
+ "type": "exists",
453
+ "selector" : "figure[data-mode=aspect-fit-only]"
454
+ },
455
+ "fullscreen": {
456
+ "type": "exists",
457
+ "selector" : "figure[data-mode=fullscreen]"
458
+ },
459
+ "non-interactive": {
460
+ "type": "exists",
461
+ "selector" : "figure[data-mode=non-interactive]"
462
+ },
463
  "image.comments" : {
464
  "type" : "exists",
465
  "selector" : "figure[data-feedback*='fb:comments']",
vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Transformer/Getters/GetterFactory.php CHANGED
@@ -16,6 +16,7 @@ class GetterFactory
16
  const TYPE_ELEMENT_GETTER = 'element';
17
  const TYPE_NEXTSIBLING_GETTER = 'sibling';
18
  const TYPE_EXISTS_GETTER = 'exists';
 
19
  const TYPE_XPATH_GETTER = 'xpath';
20
 
21
  /**
@@ -32,6 +33,7 @@ class GetterFactory
32
  * @see ElementGetter
33
  * @see NextSiblingGetter
34
  * @see ExistsGetter
 
35
  * @see XpathGetter
36
  *
37
  * @param string[] $getter_configuration that maps the properties for getter
@@ -47,6 +49,7 @@ class GetterFactory
47
  self::TYPE_ELEMENT_GETTER => ElementGetter::getClassName(),
48
  self::TYPE_NEXTSIBLING_GETTER => NextSiblingGetter::getClassName(),
49
  self::TYPE_EXISTS_GETTER => ExistsGetter::getClassName(),
 
50
  self::TYPE_XPATH_GETTER => XpathGetter::getClassName()
51
  ];
52
 
16
  const TYPE_ELEMENT_GETTER = 'element';
17
  const TYPE_NEXTSIBLING_GETTER = 'sibling';
18
  const TYPE_EXISTS_GETTER = 'exists';
19
+ const TYPE_JSON_GETTER = 'json';
20
  const TYPE_XPATH_GETTER = 'xpath';
21
 
22
  /**
33
  * @see ElementGetter
34
  * @see NextSiblingGetter
35
  * @see ExistsGetter
36
+ * @see JSONGetter
37
  * @see XpathGetter
38
  *
39
  * @param string[] $getter_configuration that maps the properties for getter
49
  self::TYPE_ELEMENT_GETTER => ElementGetter::getClassName(),
50
  self::TYPE_NEXTSIBLING_GETTER => NextSiblingGetter::getClassName(),
51
  self::TYPE_EXISTS_GETTER => ExistsGetter::getClassName(),
52
+ self::TYPE_JSON_GETTER => JSONGetter::getClassName(),
53
  self::TYPE_XPATH_GETTER => XpathGetter::getClassName()
54
  ];
55
 
vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Transformer/Getters/JSONGetter.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright (c) 2016-present, Facebook, Inc.
4
+ * All rights reserved.
5
+ *
6
+ * This source code is licensed under the license found in the
7
+ * LICENSE file in the root directory of this source tree.
8
+ */
9
+ namespace Facebook\InstantArticles\Transformer\Getters;
10
+
11
+ use Facebook\InstantArticles\Validators\Type;
12
+
13
+ class JSONGetter extends ChildrenGetter
14
+ {
15
+ /**
16
+ * @var string
17
+ */
18
+ protected $attribute;
19
+
20
+ public function createFrom($properties)
21
+ {
22
+ if (isset($properties['selector'])) {
23
+ $this->withSelector($properties['selector']);
24
+ }
25
+ if (isset($properties['attribute'])) {
26
+ $this->withAttribute($properties['attribute']);
27
+ }
28
+ }
29
+
30
+ /**
31
+ * @param string $attribute
32
+ *
33
+ * @return $this
34
+ */
35
+ public function withAttribute($attribute)
36
+ {
37
+ Type::enforce($attribute, Type::STRING);
38
+ $this->attribute = $attribute;
39
+
40
+ return $this;
41
+ }
42
+
43
+ public function get($node)
44
+ {
45
+ $content = null;
46
+
47
+ Type::enforce($node, 'DOMNode');
48
+ $elements = self::findAll($node, $this->selector);
49
+ if (!empty($elements) && $elements->item(0)) {
50
+ $element = $elements->item(0);
51
+ if ($this->attribute) {
52
+ $content = $element->getAttribute($this->attribute);
53
+ } else {
54
+ $content = $element->textContent;
55
+ }
56
+ }
57
+
58
+ if (!Type::isTextEmpty($content)) {
59
+ return json_decode($content, true);
60
+ }
61
+ return null;
62
+ }
63
+ }
vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Transformer/Rules/Compat/JetpackSlideshowRule.php ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright (c) 2016-present, Facebook, Inc.
4
+ * All rights reserved.
5
+ *
6
+ * This source code is licensed under the license found in the
7
+ * LICENSE file in the root directory of this source tree.
8
+ */
9
+ namespace Facebook\InstantArticles\Transformer\Rules\Compat;
10
+
11
+ use Facebook\InstantArticles\Transformer\Rules\ConfigurationSelectorRule;
12
+ use Facebook\InstantArticles\Validators\Type;
13
+ use Facebook\InstantArticles\Elements\InstantArticle;
14
+ use Facebook\InstantArticles\Elements\Image;
15
+ use Facebook\InstantArticles\Elements\Caption;
16
+ use Facebook\InstantArticles\Elements\Slideshow;
17
+
18
+ class JetpackSlideshowRule extends ConfigurationSelectorRule
19
+ {
20
+ const PROPERTY_JETPACK_DATA_GALLERY = 'jetpack.data-gallery';
21
+
22
+ public function getContextClass()
23
+ {
24
+ return InstantArticle::getClassName();
25
+ }
26
+
27
+ public static function create()
28
+ {
29
+ return new JetpackSlideshowRule();
30
+ }
31
+
32
+ public static function createFrom($configuration)
33
+ {
34
+ $slideshow_rule = self::create();
35
+ $slideshow_rule->withSelector($configuration['selector']);
36
+
37
+ $slideshow_rule->withProperties(
38
+ [
39
+ self::PROPERTY_JETPACK_DATA_GALLERY
40
+ ],
41
+ $configuration
42
+ );
43
+
44
+ return $slideshow_rule;
45
+ }
46
+
47
+ public function apply($transformer, $instant_article, $node)
48
+ {
49
+ // Builds the slideshow
50
+ $slideshow = Slideshow::create();
51
+ $instant_article->addChild($slideshow);
52
+
53
+ $gallery = $this->getProperty(self::PROPERTY_JETPACK_DATA_GALLERY, $node);
54
+
55
+ if ($gallery && isset($gallery)) {
56
+ foreach ($gallery as $gallery_image) {
57
+ // Constructs Image if it contains URL
58
+ if (!Type::isTextEmpty($gallery_image['src'])) {
59
+ $image = Image::create();
60
+ $image->withURL($gallery_image['src']);
61
+
62
+ // Constructs Caption element when present in the JSON
63
+ if (!Type::isTextEmpty($gallery_image['caption'])) {
64
+ $caption = Caption::create();
65
+ $caption->appendText($gallery_image['caption']);
66
+ $image->withCaption($caption);
67
+ }
68
+ $slideshow->addImage($image);
69
+ }
70
+ }
71
+ }
72
+
73
+ $transformer->transform($slideshow, $node);
74
+
75
+ return $instant_article;
76
+ }
77
+ }
vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Transformer/Rules/ConfigurationSelectorRule.php CHANGED
@@ -130,7 +130,7 @@ abstract class ConfigurationSelectorRule extends Rule
130
  }
131
  }
132
  }
133
-
134
  return false;
135
  }
136
 
130
  }
131
  }
132
  }
133
+
134
  return false;
135
  }
136
 
vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Transformer/Rules/H1Rule.php CHANGED
@@ -11,7 +11,7 @@ namespace Facebook\InstantArticles\Transformer\Rules;
11
  use Facebook\InstantArticles\Elements\Header;
12
  use Facebook\InstantArticles\Elements\Caption;
13
  use Facebook\InstantArticles\Elements\H1;
14
- use Facebook\InstantArticles\Elements\Instantarticle;
15
  use Facebook\InstantArticles\Validators\Type;
16
 
17
  class H1Rule extends ConfigurationSelectorRule
11
  use Facebook\InstantArticles\Elements\Header;
12
  use Facebook\InstantArticles\Elements\Caption;
13
  use Facebook\InstantArticles\Elements\H1;
14
+ use Facebook\InstantArticles\Elements\InstantArticle;
15
  use Facebook\InstantArticles\Validators\Type;
16
 
17
  class H1Rule extends ConfigurationSelectorRule
vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Transformer/Rules/H2Rule.php CHANGED
@@ -11,7 +11,7 @@ namespace Facebook\InstantArticles\Transformer\Rules;
11
  use Facebook\InstantArticles\Elements\Header;
12
  use Facebook\InstantArticles\Elements\Caption;
13
  use Facebook\InstantArticles\Elements\H2;
14
- use Facebook\InstantArticles\Elements\Instantarticle;
15
  use Facebook\InstantArticles\Validators\Type;
16
 
17
  class H2Rule extends ConfigurationSelectorRule
11
  use Facebook\InstantArticles\Elements\Header;
12
  use Facebook\InstantArticles\Elements\Caption;
13
  use Facebook\InstantArticles\Elements\H2;
14
+ use Facebook\InstantArticles\Elements\InstantArticle;
15
  use Facebook\InstantArticles\Validators\Type;
16
 
17
  class H2Rule extends ConfigurationSelectorRule
vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Transformer/Rules/ImageRule.php CHANGED
@@ -18,6 +18,11 @@ class ImageRule extends ConfigurationSelectorRule
18
  const PROPERTY_LIKE = 'image.like';
19
  const PROPERTY_COMMENTS = 'image.comments';
20
 
 
 
 
 
 
21
  public function getContextClass()
22
  {
23
  return InstantArticle::getClassName();
@@ -37,7 +42,11 @@ class ImageRule extends ConfigurationSelectorRule
37
  [
38
  self::PROPERTY_IMAGE_URL,
39
  self::PROPERTY_LIKE,
40
- self::PROPERTY_COMMENTS
 
 
 
 
41
  ],
42
  $configuration
43
  );
@@ -65,6 +74,16 @@ class ImageRule extends ConfigurationSelectorRule
65
  );
66
  }
67
 
 
 
 
 
 
 
 
 
 
 
68
  if ($this->getProperty(self::PROPERTY_LIKE, $node)) {
69
  $image->enableLike();
70
  }
18
  const PROPERTY_LIKE = 'image.like';
19
  const PROPERTY_COMMENTS = 'image.comments';
20
 
21
+ const ASPECT_FIT = 'aspect-fit';
22
+ const ASPECT_FIT_ONLY = 'aspect-fit-only';
23
+ const FULLSCREEN = 'fullscreen';
24
+ const NON_INTERACTIVE = 'non-interactive';
25
+
26
  public function getContextClass()
27
  {
28
  return InstantArticle::getClassName();
42
  [
43
  self::PROPERTY_IMAGE_URL,
44
  self::PROPERTY_LIKE,
45
+ self::PROPERTY_COMMENTS,
46
+ self::ASPECT_FIT,
47
+ self::ASPECT_FIT_ONLY,
48
+ self::FULLSCREEN,
49
+ self::NON_INTERACTIVE
50
  ],
51
  $configuration
52
  );
74
  );
75
  }
76
 
77
+ if ($this->getProperty(Image::ASPECT_FIT, $node)) {
78
+ $image->withPresentation(Image::ASPECT_FIT);
79
+ } elseif ($this->getProperty(Image::ASPECT_FIT_ONLY, $node)) {
80
+ $image->withPresentation(Image::ASPECT_FIT_ONLY);
81
+ } elseif ($this->getProperty(Image::FULLSCREEN, $node)) {
82
+ $image->withPresentation(Image::FULLSCREEN);
83
+ } elseif ($this->getProperty(Image::NON_INTERACTIVE, $node)) {
84
+ $image->withPresentation(Image::NON_INTERACTIVE);
85
+ }
86
+
87
  if ($this->getProperty(self::PROPERTY_LIKE, $node)) {
88
  $image->enableLike();
89
  }
vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Transformer/Rules/InteractiveInsideParagraphRule.php ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Copyright (c) 2016-present, Facebook, Inc.
4
+ * All rights reserved.
5
+ *
6
+ * This source code is licensed under the license found in the
7
+ * LICENSE file in the root directory of this source tree.
8
+ */
9
+ namespace Facebook\InstantArticles\Transformer\Rules;
10
+
11
+ use Facebook\InstantArticles\Elements\Interactive;
12
+ use Facebook\InstantArticles\Elements\Paragraph;
13
+ use Facebook\InstantArticles\Transformer\Warnings\InvalidSelector;
14
+ use Facebook\InstantArticles\Transformer\Warnings\NoRootInstantArticleFoundWarning;
15
+
16
+ class InteractiveInsideParagraphRule extends ConfigurationSelectorRule
17
+ {
18
+ const PROPERTY_IFRAME = 'interactive.iframe';
19
+ const PROPERTY_URL = 'interactive.url';
20
+ const PROPERTY_WIDTH_NO_MARGIN = Interactive::NO_MARGIN;
21
+ const PROPERTY_WIDTH_COLUMN_WIDTH = Interactive::COLUMN_WIDTH;
22
+ const PROPERTY_HEIGHT = 'interactive.height';
23
+ const PROPERTY_WIDTH = 'interactive.width';
24
+
25
+ public function getContextClass()
26
+ {
27
+ return Paragraph::getClassName();
28
+ }
29
+
30
+ public static function create()
31
+ {
32
+ return new InteractiveInsideParagraphRule();
33
+ }
34
+
35
+ public static function createFrom($configuration)
36
+ {
37
+ $interactive_rule = self::create();
38
+ $interactive_rule->withSelector($configuration['selector']);
39
+
40
+ $interactive_rule->withProperties(
41
+ [
42
+ self::PROPERTY_IFRAME,
43
+ self::PROPERTY_URL,
44
+ self::PROPERTY_WIDTH_NO_MARGIN,
45
+ self::PROPERTY_WIDTH_COLUMN_WIDTH,
46
+ self::PROPERTY_WIDTH,
47
+ self::PROPERTY_HEIGHT
48
+ ],
49
+ $configuration
50
+ );
51
+
52
+ return $interactive_rule;
53
+ }
54
+
55
+ public function apply($transformer, $context, $node)
56
+ {
57
+ $interactive = Interactive::create();
58
+
59
+ // Builds the interactive
60
+ $iframe = $this->getProperty(self::PROPERTY_IFRAME, $node);
61
+ $url = $this->getProperty(self::PROPERTY_URL, $node);
62
+ if ($iframe) {
63
+ $interactive->withHTML($iframe);
64
+ }
65
+ if ($url) {
66
+ $interactive->withSource($url);
67
+ }
68
+
69
+ if ($this->getProperty(self::PROPERTY_WIDTH_COLUMN_WIDTH, $node)) {
70
+ $interactive->withMargin(Interactive::COLUMN_WIDTH);
71
+ } else {
72
+ $interactive->withMargin(Interactive::NO_MARGIN);
73
+ }
74
+
75
+ $width = $this->getProperty(self::PROPERTY_WIDTH, $node);
76
+ if ($width) {
77
+ $interactive->withWidth($width);
78
+ }
79
+
80
+ $height = $this->getProperty(self::PROPERTY_HEIGHT, $node);
81
+ if ($height) {
82
+ $interactive->withHeight($height);
83
+ }
84
+
85
+
86
+ if ($iframe || $url) {
87
+ $instant_article = $transformer->getInstantArticle();
88
+ if ($instant_article) {
89
+ $instant_article->addChild($interactive);
90
+ $context->disableEmptyValidation();
91
+ $context = Paragraph::create();
92
+ $context->disableEmptyValidation();
93
+ $instant_article->addChild($context);
94
+ } else {
95
+ $transformer->addWarning(
96
+ // This new error message should be something like:
97
+ // Could not transform Interactive, as no root InstantArticle was provided.
98
+ new NoRootInstantArticleFoundWarning(null, $node)
99
+ );
100
+ }
101
+ } else {
102
+ $transformer->addWarning(
103
+ new InvalidSelector(
104
+ self::PROPERTY_IFRAME,
105
+ $instant_article,
106
+ $node,
107
+ $this
108
+ )
109
+ );
110
+ }
111
+
112
+ $suppress_warnings = $transformer->suppress_warnings;
113
+ $transformer->suppress_warnings = true;
114
+ $transformer->transform($interactive, $node);
115
+ $transformer->suppress_warnings = $suppress_warnings;
116
+
117
+ return $context;
118
+ }
119
+ }
vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Transformer/Rules/InteractiveRule.php CHANGED
@@ -55,7 +55,7 @@ class InteractiveRule extends ConfigurationSelectorRule
55
  {
56
  $interactive = Interactive::create();
57
 
58
- // Builds the image
59
  $iframe = $this->getProperty(self::PROPERTY_IFRAME, $node);
60
  $url = $this->getProperty(self::PROPERTY_URL, $node);
61
  if ($iframe) {
55
  {
56
  $interactive = Interactive::create();
57
 
58
+ // Builds the interactive
59
  $iframe = $this->getProperty(self::PROPERTY_IFRAME, $node);
60
  $url = $this->getProperty(self::PROPERTY_URL, $node);
61
  if ($iframe) {
vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Transformer/Rules/Rule.php CHANGED
@@ -23,9 +23,8 @@ abstract class Rule
23
  $log->debug('node matches: '.($matches_node ? 'MATCHES' : 'no match'));
24
  $log->debug('rule: '.get_class($this));
25
  $log->debug('-------');
26
- return true;
27
  }
28
- if ($node->nodeName === 'img') {
29
  $log->debug('context class: '.get_class($context));
30
  $log->debug('context matches: '.($matches_context ? 'MATCHES' : 'no match'));
31
  $log->debug('node name: <'.$node->nodeName.' />');
@@ -34,7 +33,7 @@ abstract class Rule
34
  $log->debug('rule: '.get_class($this));
35
  $log->debug('-------');
36
  }
37
- return false;
38
  }
39
 
40
  abstract public function matchesContext($context);
23
  $log->debug('node matches: '.($matches_node ? 'MATCHES' : 'no match'));
24
  $log->debug('rule: '.get_class($this));
25
  $log->debug('-------');
 
26
  }
27
+ if ($node->nodeName === 'iframe') {
28
  $log->debug('context class: '.get_class($context));
29
  $log->debug('context matches: '.($matches_context ? 'MATCHES' : 'no match'));
30
  $log->debug('node name: <'.$node->nodeName.' />');
33
  $log->debug('rule: '.get_class($this));
34
  $log->debug('-------');
35
  }
36
+ return $matches_context && $matches_node;
37
  }
38
 
39
  abstract public function matchesContext($context);
vendor/facebook/facebook-instant-articles-sdk-php/src/Facebook/InstantArticles/Transformer/Warnings/validator_warning_messages.ini CHANGED
@@ -24,7 +24,7 @@ H3 = "This is a text container that must not be empty nor contain only whitespac
24
  Header = "Header must have at least a title and/or Ad."
25
  Image = "Image must have a URL."
26
  InstantArticle = "InstantArticle relies on InstantArticleValidator."
27
- Interactive = "Interactive must have a src and/or HTML."
28
  Italic = "This is a text container that must not be empty nor contain only whitespace characters such as spaces, tabs, new lines or &nbsp;."
29
  LineBreak = "LineBreak is always valid."
30
  ListElement = "ListElement must have at least one of its ListItem valid."
24
  Header = "Header must have at least a title and/or Ad."
25
  Image = "Image must have a URL."
26
  InstantArticle = "InstantArticle relies on InstantArticleValidator."
27
+ Interactive = "Interactive embeds must have valid positive integer width and height values for an empty HTML content. In Interactive elements with HTML content, the width/height properties are optional."
28
  Italic = "This is a text container that must not be empty nor contain only whitespace characters such as spaces, tabs, new lines or &nbsp;."
29
  LineBreak = "LineBreak is always valid."
30
  ListElement = "ListElement must have at least one of its ListItem valid."
vendor/facebook/facebook-instant-articles-sdk-php/tests/Facebook/InstantArticles/Client/ClientTest.php CHANGED
@@ -630,4 +630,197 @@ class ClientTest extends \PHPUnit_Framework_TestCase
630
  $result = $this->client->getArticlesURLs();
631
  $this->assertEquals($expected, $result);
632
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
633
  }
630
  $result = $this->client->getArticlesURLs();
631
  $this->assertEquals($expected, $result);
632
  }
633
+
634
+ public function testClaimURL()
635
+ {
636
+ $url = 'example.com';
637
+
638
+ $serverResponseMock =
639
+ $this->getMockBuilder('Facebook\FacebookResponse')
640
+ ->disableOriginalConstructor()
641
+ ->getMock();
642
+ $graphNodeMock =
643
+ $this->getMockBuilder('Facebook\GraphNodes\GraphNode')
644
+ ->disableOriginalConstructor()
645
+ ->getMock();
646
+
647
+ $serverResponseMock
648
+ ->expects($this->once())
649
+ ->method('getGraphNode')
650
+ ->willReturn($graphNodeMock);
651
+ $graphNodeMock
652
+ ->expects($this->exactly(2))
653
+ ->method('getField')
654
+ ->withConsecutive(
655
+ [$this->equalTo('error')],
656
+ [$this->equalTo('success')]
657
+ )
658
+ ->will($this->onConsecutiveCalls(
659
+ null,
660
+ true
661
+ ));
662
+
663
+ $this->facebook
664
+ ->expects($this->once())
665
+ ->method('post')
666
+ ->with('PAGE_ID/claimed_urls?url=' . $url)
667
+ ->willReturn($serverResponseMock);
668
+
669
+ $result = $this->client->claimURL($url);
670
+ }
671
+
672
+ public function testClaimURLWithProtocl()
673
+ {
674
+ $url = 'http://example.com';
675
+
676
+ $serverResponseMock =
677
+ $this->getMockBuilder('Facebook\FacebookResponse')
678
+ ->disableOriginalConstructor()
679
+ ->getMock();
680
+ $graphNodeMock =
681
+ $this->getMockBuilder('Facebook\GraphNodes\GraphNode')
682
+ ->disableOriginalConstructor()
683
+ ->getMock();
684
+
685
+ $serverResponseMock
686
+ ->expects($this->once())
687
+ ->method('getGraphNode')
688
+ ->willReturn($graphNodeMock);
689
+ $graphNodeMock
690
+ ->expects($this->exactly(2))
691
+ ->method('getField')
692
+ ->withConsecutive(
693
+ [$this->equalTo('error')],
694
+ [$this->equalTo('success')]
695
+ )
696
+ ->will($this->onConsecutiveCalls(
697
+ null,
698
+ true
699
+ ));
700
+
701
+ $this->facebook
702
+ ->expects($this->once())
703
+ ->method('post')
704
+ ->with('PAGE_ID/claimed_urls?url=example.com')
705
+ ->willReturn($serverResponseMock);
706
+
707
+ $result = $this->client->claimURL($url);
708
+ }
709
+
710
+ public function testClaimURLError()
711
+ {
712
+ $url = 'example.com';
713
+ $error_user_msg = "Error message";
714
+
715
+ $serverResponseMock =
716
+ $this->getMockBuilder('Facebook\FacebookResponse')
717
+ ->disableOriginalConstructor()
718
+ ->getMock();
719
+ $graphNodeMock =
720
+ $this->getMockBuilder('Facebook\GraphNodes\GraphNode')
721
+ ->disableOriginalConstructor()
722
+ ->getMock();
723
+
724
+ $serverResponseMock
725
+ ->expects($this->once())
726
+ ->method('getGraphNode')
727
+ ->willReturn($graphNodeMock);
728
+ $graphNodeMock
729
+ ->expects($this->exactly(2))
730
+ ->method('getField')
731
+ ->withConsecutive(
732
+ [$this->equalTo('error')],
733
+ [$this->equalTo('success')]
734
+ )
735
+ ->will($this->onConsecutiveCalls(
736
+ array( 'error_user_msg' => $error_user_msg ),
737
+ false
738
+ ));
739
+
740
+ $this->facebook
741
+ ->expects($this->once())
742
+ ->method('post')
743
+ ->with('PAGE_ID/claimed_urls?url=' .$url)
744
+ ->willReturn($serverResponseMock);
745
+
746
+ $this->setExpectedException('\Facebook\InstantArticles\Client\ClientException');
747
+
748
+ $result = $this->client->claimURL($url);
749
+ }
750
+
751
+ public function testSubmitForReview()
752
+ {
753
+ $serverResponseMock =
754
+ $this->getMockBuilder('Facebook\FacebookResponse')
755
+ ->disableOriginalConstructor()
756
+ ->getMock();
757
+ $graphNodeMock =
758
+ $this->getMockBuilder('Facebook\GraphNodes\GraphNode')
759
+ ->disableOriginalConstructor()
760
+ ->getMock();
761
+
762
+ $serverResponseMock
763
+ ->expects($this->once())
764
+ ->method('getGraphNode')
765
+ ->willReturn($graphNodeMock);
766
+ $graphNodeMock
767
+ ->expects($this->exactly(2))
768
+ ->method('getField')
769
+ ->withConsecutive(
770
+ [$this->equalTo('error')],
771
+ [$this->equalTo('success')]
772
+ )
773
+ ->will($this->onConsecutiveCalls(
774
+ null,
775
+ true
776
+ ));
777
+
778
+ $this->facebook
779
+ ->expects($this->once())
780
+ ->method('post')
781
+ ->with('PAGE_ID/?instant_articles_submit_for_review=true')
782
+ ->willReturn($serverResponseMock);
783
+
784
+ $result = $this->client->submitForReview();
785
+ }
786
+
787
+ public function testSubmitForReviewError()
788
+ {
789
+ $error_user_msg = "Error message";
790
+
791
+ $serverResponseMock =
792
+ $this->getMockBuilder('Facebook\FacebookResponse')
793
+ ->disableOriginalConstructor()
794
+ ->getMock();
795
+ $graphNodeMock =
796
+ $this->getMockBuilder('Facebook\GraphNodes\GraphNode')
797
+ ->disableOriginalConstructor()
798
+ ->getMock();
799
+
800
+ $serverResponseMock
801
+ ->expects($this->once())
802
+ ->method('getGraphNode')
803
+ ->willReturn($graphNodeMock);
804
+ $graphNodeMock
805
+ ->expects($this->exactly(2))
806
+ ->method('getField')
807
+ ->withConsecutive(
808
+ [$this->equalTo('error')],
809
+ [$this->equalTo('success')]
810
+ )
811
+ ->will($this->onConsecutiveCalls(
812
+ array( 'error_user_msg' => $error_user_msg ),
813
+ false
814
+ ));
815
+
816
+ $this->facebook
817
+ ->expects($this->once())
818
+ ->method('post')
819
+ ->with('PAGE_ID/?instant_articles_submit_for_review=true')
820
+ ->willReturn($serverResponseMock);
821
+
822
+ $this->setExpectedException('\Facebook\InstantArticles\Client\ClientException');
823
+
824
+ $result = $this->client->submitForReview();
825
+ }
826
  }
vendor/facebook/facebook-instant-articles-sdk-php/tests/Facebook/InstantArticles/Client/HelperTest.php CHANGED
@@ -54,7 +54,7 @@ class HelperTest extends \PHPUnit_Framework_TestCase
54
  $this->facebook
55
  ->expects($this->once())
56
  ->method('get')
57
- ->with('/me/accounts?fields=name,id,access_token,supports_instant_articles')
58
  ->willReturn($response);
59
 
60
  $pagesAndTokensReturned = $this->helper->getPagesAndTokens($accessToken);
54
  $this->facebook
55
  ->expects($this->once())
56
  ->method('get')
57
+ ->with('/me/accounts?fields=name,id,access_token,supports_instant_articles,picture')
58
  ->willReturn($response);
59
 
60
  $pagesAndTokensReturned = $this->helper->getPagesAndTokens($accessToken);
vendor/facebook/facebook-instant-articles-sdk-php/tests/Facebook/InstantArticles/Elements/InteractiveTest.php CHANGED
@@ -21,17 +21,20 @@ class InteractiveTest extends \PHPUnit_Framework_TestCase
21
 
22
  $rendered = $interactive->render();
23
  $this->assertEquals($expected, $rendered);
 
24
  }
25
 
26
  public function testRenderBasic()
27
  {
28
  $interactive =
29
  Interactive::create()
30
- ->withSource('http://foo.com/interactive-graphic');
 
 
31
 
32
  $expected =
33
  '<figure class="op-interactive">'.
34
- '<iframe src="http://foo.com/interactive-graphic"></iframe>'.
35
  '</figure>';
36
 
37
  $rendered = $interactive->render();
@@ -43,6 +46,8 @@ class InteractiveTest extends \PHPUnit_Framework_TestCase
43
  $social_embed =
44
  interactive::create()
45
  ->withSource('http://foo.com/interactive-graphic')
 
 
46
  ->withCaption(
47
  Caption::create()
48
  ->appendText('Some caption to the interactive graphic')
@@ -50,7 +55,7 @@ class InteractiveTest extends \PHPUnit_Framework_TestCase
50
 
51
  $expected =
52
  '<figure class="op-interactive">'.
53
- '<iframe src="http://foo.com/interactive-graphic"></iframe>'.
54
  '<figcaption>Some caption to the interactive graphic</figcaption>'.
55
  '</figure>';
56
 
@@ -58,36 +63,32 @@ class InteractiveTest extends \PHPUnit_Framework_TestCase
58
  $this->assertEquals($expected, $rendered);
59
  }
60
 
61
- public function testRenderBasicWithHeight()
62
  {
63
  $interactive =
64
  Interactive::create()
65
  ->withSource('http://foo.com/interactive-graphic')
66
  ->withHeight(640);
67
 
68
- $expected =
69
- '<figure class="op-interactive">'.
70
- '<iframe src="http://foo.com/interactive-graphic" height="640"></iframe>'.
71
- '</figure>';
72
 
73
  $rendered = $interactive->render();
74
  $this->assertEquals($expected, $rendered);
 
75
  }
76
 
77
- public function testRenderBasicWithWidth()
78
  {
79
  $interactive =
80
- Interactive::create()
81
- ->withSource('http://foo.com/interactive-graphic')
82
- ->withWidth(640);
83
 
84
- $expected =
85
- '<figure class="op-interactive">' .
86
- '<iframe src="http://foo.com/interactive-graphic" width="640"></iframe>' .
87
- '</figure>';
88
 
89
  $rendered = $interactive->render();
90
  $this->assertEquals($expected, $rendered);
 
91
  }
92
 
93
  public function testRenderBasicWithWidthHeight()
@@ -100,46 +101,67 @@ class InteractiveTest extends \PHPUnit_Framework_TestCase
100
 
101
  $expected =
102
  '<figure class="op-interactive">' .
103
- '<iframe src="http://foo.com/interactive-graphic" width="1600" height="900"></iframe>' .
104
  '</figure>';
105
 
106
  $rendered = $interactive->render();
107
  $this->assertEquals($expected, $rendered);
108
  }
109
 
110
- public function testRenderBasicWithColumnWidth()
111
  {
112
  $interactive =
113
  Interactive::create()
114
  ->withSource('http://foo.com/interactive-graphic')
115
  ->withMargin(Interactive::COLUMN_WIDTH);
116
 
117
- $expected =
118
- '<figure class="op-interactive">'.
119
- '<iframe src="http://foo.com/interactive-graphic" class="column-width"></iframe>'.
120
- '</figure>';
121
 
122
  $rendered = $interactive->render();
123
  $this->assertEquals($expected, $rendered);
 
124
  }
125
 
126
- public function testRenderBasicWithNoMargin()
127
  {
128
  $interactive =
129
  Interactive::create()
130
  ->withSource('http://foo.com/interactive-graphic')
131
  ->withMargin(Interactive::NO_MARGIN);
132
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  $expected =
134
  '<figure class="op-interactive">'.
135
- '<iframe src="http://foo.com/interactive-graphic" class="no-margin"></iframe>'.
 
 
 
136
  '</figure>';
137
 
138
  $rendered = $interactive->render();
139
  $this->assertEquals($expected, $rendered);
140
  }
141
 
142
- public function testRenderInlineWithHeightAndWidth()
143
  {
144
  $inline =
145
  '<h1>Some custom code</h1>'.
@@ -148,12 +170,11 @@ class InteractiveTest extends \PHPUnit_Framework_TestCase
148
  $interactive =
149
  Interactive::create()
150
  ->withHTML($inline)
151
- ->withHeight(640)
152
  ->withMargin(Interactive::NO_MARGIN);
153
 
154
  $expected =
155
  '<figure class="op-interactive">'.
156
- '<iframe class="no-margin" height="640">'.
157
  '<h1>Some custom code</h1>'.
158
  '<script>alert("test & more test");</script>'.
159
  '</iframe>'.
@@ -161,5 +182,6 @@ class InteractiveTest extends \PHPUnit_Framework_TestCase
161
 
162
  $rendered = $interactive->render();
163
  $this->assertEquals($expected, $rendered);
 
164
  }
165
  }
21
 
22
  $rendered = $interactive->render();
23
  $this->assertEquals($expected, $rendered);
24
+ $this->assertFalse($interactive->isValid());
25
  }
26
 
27
  public function testRenderBasic()
28
  {
29
  $interactive =
30
  Interactive::create()
31
+ ->withSource('http://foo.com/interactive-graphic')
32
+ ->withWidth(640)
33
+ ->withHeight(300);
34
 
35
  $expected =
36
  '<figure class="op-interactive">'.
37
+ '<iframe src="http://foo.com/interactive-graphic" width="640" height="300"></iframe>'.
38
  '</figure>';
39
 
40
  $rendered = $interactive->render();
46
  $social_embed =
47
  interactive::create()
48
  ->withSource('http://foo.com/interactive-graphic')
49
+ ->withWidth(640)
50
+ ->withHeight(300)
51
  ->withCaption(
52
  Caption::create()
53
  ->appendText('Some caption to the interactive graphic')
55
 
56
  $expected =
57
  '<figure class="op-interactive">'.
58
+ '<iframe src="http://foo.com/interactive-graphic" width="640" height="300"></iframe>'.
59
  '<figcaption>Some caption to the interactive graphic</figcaption>'.
60
  '</figure>';
61
 
63
  $this->assertEquals($expected, $rendered);
64
  }
65
 
66
+ public function testRenderBasicWithOnlyHeight()
67
  {
68
  $interactive =
69
  Interactive::create()
70
  ->withSource('http://foo.com/interactive-graphic')
71
  ->withHeight(640);
72
 
73
+ $expected = '';
 
 
 
74
 
75
  $rendered = $interactive->render();
76
  $this->assertEquals($expected, $rendered);
77
+ $this->assertFalse($interactive->isValid());
78
  }
79
 
80
+ public function testRenderBasicWithOnlyWidth()
81
  {
82
  $interactive =
83
+ Interactive::create()
84
+ ->withSource('http://foo.com/interactive-graphic')
85
+ ->withWidth(640);
86
 
87
+ $expected = '';
 
 
 
88
 
89
  $rendered = $interactive->render();
90
  $this->assertEquals($expected, $rendered);
91
+ $this->assertFalse($interactive->isValid());
92
  }
93
 
94
  public function testRenderBasicWithWidthHeight()
101
 
102
  $expected =
103
  '<figure class="op-interactive">' .
104
+ '<iframe src="http://foo.com/interactive-graphic" width="1600" height="900"></iframe>' .
105
  '</figure>';
106
 
107
  $rendered = $interactive->render();
108
  $this->assertEquals($expected, $rendered);
109
  }
110
 
111
+ public function testRenderBasicWithOnlyColumnWidth()
112
  {
113
  $interactive =
114
  Interactive::create()
115
  ->withSource('http://foo.com/interactive-graphic')
116
  ->withMargin(Interactive::COLUMN_WIDTH);
117
 
118
+ $expected = '';
 
 
 
119
 
120
  $rendered = $interactive->render();
121
  $this->assertEquals($expected, $rendered);
122
+ $this->assertFalse($interactive->isValid());
123
  }
124
 
125
+ public function testRenderBasicWithOnlyNoMargin()
126
  {
127
  $interactive =
128
  Interactive::create()
129
  ->withSource('http://foo.com/interactive-graphic')
130
  ->withMargin(Interactive::NO_MARGIN);
131
 
132
+ $expected = '';
133
+
134
+ $rendered = $interactive->render();
135
+ $this->assertEquals($expected, $rendered);
136
+ $this->assertFalse($interactive->isValid());
137
+ }
138
+
139
+ public function testRenderInlineWithHeightAndWidth()
140
+ {
141
+ $inline =
142
+ '<h1>Some custom code</h1>'.
143
+ '<script>alert("test & more test");</script>';
144
+
145
+ $interactive =
146
+ Interactive::create()
147
+ ->withHTML($inline)
148
+ ->withWidth(600)
149
+ ->withHeight(640)
150
+ ->withMargin(Interactive::NO_MARGIN);
151
+
152
  $expected =
153
  '<figure class="op-interactive">'.
154
+ '<iframe class="no-margin" width="600" height="640">'.
155
+ '<h1>Some custom code</h1>'.
156
+ '<script>alert("test & more test");</script>'.
157
+ '</iframe>'.
158
  '</figure>';
159
 
160
  $rendered = $interactive->render();
161
  $this->assertEquals($expected, $rendered);
162
  }
163
 
164
+ public function testRenderInline()
165
  {
166
  $inline =
167
  '<h1>Some custom code</h1>'.
170
  $interactive =
171
  Interactive::create()
172
  ->withHTML($inline)
 
173
  ->withMargin(Interactive::NO_MARGIN);
174
 
175
  $expected =
176
  '<figure class="op-interactive">'.
177
+ '<iframe class="no-margin">'.
178
  '<h1>Some custom code</h1>'.
179
  '<script>alert("test & more test");</script>'.
180
  '</iframe>'.
182
 
183
  $rendered = $interactive->render();
184
  $this->assertEquals($expected, $rendered);
185
+ $this->assertTrue($interactive->isValid());
186
  }
187
  }
vendor/facebook/facebook-instant-articles-sdk-php/tests/Facebook/InstantArticles/Transformer/CMS/WPTransformerTest.php CHANGED
@@ -6,14 +6,15 @@
6
  * This source code is licensed under the license found in the
7
  * LICENSE file in the root directory of this source tree.
8
  */
9
- namespace Facebook\InstantArticles\Transformer;
10
 
 
11
  use Facebook\InstantArticles\Elements\InstantArticle;
12
  use Facebook\InstantArticles\Elements\Header;
13
  use Facebook\InstantArticles\Elements\Time;
14
  use Facebook\InstantArticles\Elements\Author;
15
 
16
- class WPTest extends \PHPUnit_Framework_TestCase
17
  {
18
  protected function setUp()
19
  {
6
  * This source code is licensed under the license found in the
7
  * LICENSE file in the root directory of this source tree.
8
  */
9
+ namespace Facebook\InstantArticles\Transformer\CMS;
10
 
11
+ use Facebook\InstantArticles\Transformer\Transformer;
12
  use Facebook\InstantArticles\Elements\InstantArticle;
13
  use Facebook\InstantArticles\Elements\Header;
14
  use Facebook\InstantArticles\Elements\Time;
15
  use Facebook\InstantArticles\Elements\Author;
16
 
17
+ class WPTransformerTest extends \PHPUnit_Framework_TestCase
18
  {
19
  protected function setUp()
20
  {
vendor/facebook/facebook-instant-articles-sdk-php/tests/Facebook/InstantArticles/Transformer/CMS/wp-ia.xml CHANGED
@@ -16,7 +16,7 @@
16
  <address><a>bill</a></address>
17
  </header>
18
  <p>Yes, peace is good for everybody!<br/>
19
- Man kind.</p>
20
  <figure>
21
  <img src="http://example.com/image0.jpg"/>
22
  </figure>
@@ -30,11 +30,126 @@ Man kind.</p>
30
  <img src="http://example.com/image2.jpg"/>
31
  </figure>
32
  <p> and some after img.</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  <ul>
34
  <li>Some text on li before img</li>
35
  <li>Some text on li before imgand after img</li>
36
  <li>Some text after img</li>
37
  </ul>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  </article>
39
  </body>
40
  </html>
16
  <address><a>bill</a></address>
17
  </header>
18
  <p>Yes, peace is good for everybody!<br/>
19
+ Man kind.</p>
20
  <figure>
21
  <img src="http://example.com/image0.jpg"/>
22
  </figure>
30
  <img src="http://example.com/image2.jpg"/>
31
  </figure>
32
  <p> and some after img.</p>
33
+ <figure class="op-interactive">
34
+ <iframe src="http://example.com/0/" class="no-margin" width="300" height="200"></iframe>
35
+ </figure>
36
+ <p>Some text before iframe</p>
37
+ <figure class="op-interactive">
38
+ <iframe src="http://example.com/1/" class="no-margin" width="300" height="200"></iframe>
39
+ </figure>
40
+ <figure class="op-interactive">
41
+ <iframe src="http://example.com/2/" class="no-margin" width="300" height="200"></iframe>
42
+ </figure>
43
+ <p> some after iframe.</p>
44
+ <p>Some text before iframe</p>
45
+ <figure class="op-interactive">
46
+ <iframe src="http://example.com/3/" class="no-margin" width="300" height="200"></iframe>
47
+ </figure>
48
+ <p> and some after iframe.</p>
49
+ <figure class="op-interactive">
50
+ <iframe src="http://example.com/loose/with_url" class="no-margin" width="300" height="200"></iframe>
51
+ </figure>
52
+ <figure class="op-interactive">
53
+ <iframe class="no-margin">
54
+ <div>
55
+ <h1>Iframe loose without url</h1>
56
+ </div>
57
+ </iframe>
58
+ </figure>
59
+ <figure class="op-interactive">
60
+ <iframe class="no-margin">
61
+ <div>
62
+ <h1>some embed here</h1>
63
+ </div>
64
+ </iframe>
65
+ </figure>
66
+ <figure class="op-interactive">
67
+ <iframe class="no-margin">
68
+ <div>
69
+ <h1>some embed here</h1>
70
+ </div>
71
+ </iframe>
72
+ </figure>
73
+ <figure class="op-interactive">
74
+ <iframe class="no-margin">
75
+ <div>some content</div>
76
+ <script>alert('hi & hello to you @ testing!');</script></iframe>
77
+ </figure>
78
+ <figure class="op-interactive">
79
+ <iframe src="http://example.com/4/" class="no-margin" width="300" height="200"></iframe>
80
+ </figure>
81
+ <figure class="op-interactive">
82
+ <iframe class="no-margin">
83
+ <div>some content</div>
84
+ <script>alert('hi & hello to you @ testing!');</script></iframe>
85
+ </figure>
86
+ <figure class="op-interactive">
87
+ <iframe src="http://example.com/4/" class="no-margin" width="300" height="200"></iframe>
88
+ </figure>
89
+ <figure class="op-interactive">
90
+ <iframe class="no-margin">
91
+ <div>some content</div>
92
+ <script>alert('hi & hello to you @ testing!');</script></iframe>
93
+ </figure>
94
+ <figure class="op-interactive">
95
+ <iframe class="no-margin" height="200">
96
+ <table width="200" height="200">
97
+ <thead><td>header 1</td>
98
+ <td>header 2</td>
99
+ </thead>
100
+ <tbody>
101
+ <tr><td>Line 1 column 1</td>
102
+ <td>Line 1 column 2</td>
103
+ </tr>
104
+ <tr><td>Line 2 column 1</td>
105
+ <td>Line 2 column 2</td>
106
+ </tr>
107
+ </tbody>
108
+ </table>
109
+ </iframe>
110
+ </figure>
111
  <ul>
112
  <li>Some text on li before img</li>
113
  <li>Some text on li before imgand after img</li>
114
  <li>Some text after img</li>
115
  </ul>
116
+ <figure class="op-slideshow">
117
+ <figure>
118
+ <img src="http://example.com/img0.jpg"/>
119
+ </figure>
120
+ <figure>
121
+ <img src="http://example.com/img1.jpg"/>
122
+ </figure>
123
+ <figure>
124
+ <img src="http://example.com/img2.jpg"/>
125
+ <figcaption>Image 2</figcaption>
126
+ </figure>
127
+ <figure>
128
+ <img src="http://example.com/img3.jpg"/>
129
+ <figcaption>Image 3</figcaption>
130
+ </figure>
131
+ <figure>
132
+ <img src="http://example.com/img4.jpg"/>
133
+ </figure>
134
+ </figure>
135
+ <figure class="op-slideshow">
136
+ <figure>
137
+ <img src="http://example.com/img1.jpg"/>
138
+ <figcaption> Image 1 </figcaption>
139
+ </figure>
140
+ <figure>
141
+ <img src="http://example.com/img2.jpg"/>
142
+ <figcaption> Image 2 </figcaption>
143
+ </figure>
144
+ <figure>
145
+ <img src="http://example.com/img3.jpg"/>
146
+ <figcaption> Image 3 </figcaption>
147
+ </figure>
148
+ </figure>
149
+ <figure>
150
+ <img src="http://example.com/img.jpg"/>
151
+ <figcaption>blue eyes</figcaption>
152
+ </figure>
153
  </article>
154
  </body>
155
  </html>
vendor/facebook/facebook-instant-articles-sdk-php/tests/Facebook/InstantArticles/Transformer/CMS/wp-rules.json CHANGED
@@ -17,6 +17,9 @@
17
  },{
18
  "class": "PassThroughRule",
19
  "selector" : "del"
 
 
 
20
  }, {
21
  "class": "PassThroughRule",
22
  "selector" : "span"
@@ -59,8 +62,8 @@
59
  }, {
60
  "class": "PassThroughRule",
61
  "selector": "blockquote p"
62
- },{
63
- "class": "ImageRule",
64
  "selector": "img",
65
  "properties": {
66
  "image.url": {
@@ -69,8 +72,8 @@
69
  "attribute": "src"
70
  }
71
  }
72
- }, {
73
- "class": "ImageInsideParagraphRule",
74
  "selector": "img",
75
  "properties": {
76
  "image.url": {
@@ -117,6 +120,49 @@
117
  }, {
118
  "class": "H2Rule",
119
  "selector" : "h3,h4,h5,h6"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
  }, {
121
  "class": "InteractiveRule",
122
  "selector" : "div.interactive",
@@ -130,19 +176,205 @@
130
  "selector" : "iframe",
131
  "attribute": "height"
132
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  "interactive.iframe" : {
134
  "type" : "children",
135
  "selector" : "iframe"
 
 
 
 
 
 
 
 
 
 
136
  }
137
  }
138
  }, {
139
  "class": "InteractiveRule",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  "selector" : "//div[@class='interactive' and iframe]",
141
  "properties" : {
142
  "interactive.url" : {
143
  "type" : "string",
144
  "selector" : "iframe",
145
  "attribute": "src"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
  }
147
  }
148
  }, {
@@ -192,14 +424,95 @@
192
  "attribute": "type"
193
  }
194
  }
195
- }, {
196
- "class": "InteractiveRule",
197
- "selector" : "table",
198
- "properties" : {
199
- "interactive.iframe" : {
200
- "type" : "element",
201
- "selector" : "table"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
202
  }
203
  }
204
- }]
 
205
  }
17
  },{
18
  "class": "PassThroughRule",
19
  "selector" : "del"
20
+ },{
21
+ "class": "PassThroughRule",
22
+ "selector" : "mark"
23
  }, {
24
  "class": "PassThroughRule",
25
  "selector" : "span"
62
  }, {
63
  "class": "PassThroughRule",
64
  "selector": "blockquote p"
65
+ }, {
66
+ "class": "ImageInsideParagraphRule",
67
  "selector": "img",
68
  "properties": {
69
  "image.url": {
72
  "attribute": "src"
73
  }
74
  }
75
+ },{
76
+ "class": "ImageRule",
77
  "selector": "img",
78
  "properties": {
79
  "image.url": {
120
  }, {
121
  "class": "H2Rule",
122
  "selector" : "h3,h4,h5,h6"
123
+ }, {
124
+ "class": "InteractiveRule",
125
+ "selector" : "iframe",
126
+ "properties" : {
127
+ "interactive.url" : {
128
+ "type" : "string",
129
+ "selector" : "iframe",
130
+ "attribute": "src"
131
+ },
132
+ "interactive.width" : {
133
+ "type" : "int",
134
+ "selector" : "iframe",
135
+ "attribute": "width"
136
+ },
137
+ "interactive.height" : {
138
+ "type" : "int",
139
+ "selector" : "iframe",
140
+ "attribute": "height"
141
+ },
142
+ "interactive.iframe" : {
143
+ "type" : "children",
144
+ "selector" : "iframe"
145
+ }
146
+ }
147
+ }, {
148
+ "class": "InteractiveRule",
149
+ "selector" : "div.embed",
150
+ "properties" : {
151
+ "interactive.iframe" : {
152
+ "type" : "children",
153
+ "selector" : "div.embed"
154
+ },
155
+ "interactive.height" : {
156
+ "type" : "int",
157
+ "selector" : "iframe",
158
+ "attribute": "height"
159
+ },
160
+ "interactive.width" : {
161
+ "type" : "int",
162
+ "selector" : "iframe",
163
+ "attribute": "width"
164
+ }
165
+ }
166
  }, {
167
  "class": "InteractiveRule",
168
  "selector" : "div.interactive",
176
  "selector" : "iframe",
177
  "attribute": "height"
178
  },
179
+ "interactive.width" : {
180
+ "type" : "int",
181
+ "selector" : "iframe",
182
+ "attribute": "width"
183
+ }
184
+ }
185
+ }, {
186
+ "class": "InteractiveRule",
187
+ "selector" : "//div[@class='embed' and iframe]",
188
+ "properties" : {
189
+ "interactive.url" : {
190
+ "type" : "string",
191
+ "selector" : "iframe",
192
+ "attribute": "src"
193
+ },
194
+ "interactive.iframe" : {
195
+ "type" : "children",
196
+ "selector" : "iframe",
197
+ "attribute": "src"
198
+ },
199
+ "interactive.width" : {
200
+ "type" : "int",
201
+ "selector" : "iframe",
202
+ "attribute": "width"
203
+ },
204
+ "interactive.height" : {
205
+ "type" : "int",
206
+ "selector" : "iframe",
207
+ "attribute": "height"
208
+ }
209
+ }
210
+ }, {
211
+ "class": "InteractiveRule",
212
+ "selector" : "//div[@class='interactive' and iframe]",
213
+ "properties" : {
214
+ "interactive.url" : {
215
+ "type" : "string",
216
+ "selector" : "iframe",
217
+ "attribute": "src"
218
+ },
219
  "interactive.iframe" : {
220
  "type" : "children",
221
  "selector" : "iframe"
222
+ },
223
+ "interactive.height" : {
224
+ "type" : "int",
225
+ "selector" : "iframe",
226
+ "attribute": "height"
227
+ },
228
+ "interactive.width" : {
229
+ "type" : "int",
230
+ "selector" : "iframe",
231
+ "attribute": "width"
232
  }
233
  }
234
  }, {
235
  "class": "InteractiveRule",
236
+ "selector" : "table",
237
+ "properties" : {
238
+ "interactive.iframe" : {
239
+ "type" : "element",
240
+ "selector" : "table"
241
+ },
242
+ "interactive.height" : {
243
+ "type" : "int",
244
+ "selector" : "table",
245
+ "attribute": "height"
246
+ },
247
+ "interactive.width" : {
248
+ "type" : "int",
249
+ "selector" : "iframe",
250
+ "attribute": "width"
251
+ }
252
+ }
253
+ }, {
254
+ "class": "InteractiveInsideParagraphRule",
255
+ "selector" : "iframe",
256
+ "properties" : {
257
+ "interactive.url" : {
258
+ "type" : "string",
259
+ "selector" : "iframe",
260
+ "attribute": "src"
261
+ },
262
+ "interactive.height" : {
263
+ "type" : "int",
264
+ "selector" : "iframe",
265
+ "attribute": "height"
266
+ },
267
+ "interactive.width" : {
268
+ "type" : "int",
269
+ "selector" : "iframe",
270
+ "attribute": "width"
271
+ }
272
+ }
273
+ },{
274
+ "class": "InteractiveInsideParagraphRule",
275
+ "selector" : "div.embed",
276
+ "properties" : {
277
+ "interactive.iframe" : {
278
+ "type" : "children",
279
+ "selector" : "div.embed"
280
+ },
281
+ "interactive.height" : {
282
+ "type" : "int",
283
+ "selector" : "iframe",
284
+ "attribute": "height"
285
+ },
286
+ "interactive.width" : {
287
+ "type" : "int",
288
+ "selector" : "iframe",
289
+ "attribute": "width"
290
+ }
291
+ }
292
+ }, {
293
+ "class": "InteractiveInsideParagraphRule",
294
+ "selector" : "div.interactive",
295
+ "properties" : {
296
+ "interactive.iframe" : {
297
+ "type" : "children",
298
+ "selector" : "div.interactive"
299
+ },
300
+ "interactive.height" : {
301
+ "type" : "int",
302
+ "selector" : "iframe",
303
+ "attribute": "height"
304
+ },
305
+ "interactive.width" : {
306
+ "type" : "int",
307
+ "selector" : "iframe",
308
+ "attribute": "width"
309
+ }
310
+ }
311
+ }, {
312
+ "class": "InteractiveInsideParagraphRule",
313
+ "selector" : "//div[@class='embed' and iframe]",
314
+ "properties" : {
315
+ "interactive.url" : {
316
+ "type" : "string",
317
+ "selector" : "iframe",
318
+ "attribute": "src"
319
+ },
320
+ "interactive.iframe" : {
321
+ "type" : "children",
322
+ "selector" : "iframe",
323
+ "attribute": "src"
324
+ },
325
+ "interactive.width" : {
326
+ "type" : "int",
327
+ "selector" : "iframe",
328
+ "attribute": "width"
329
+ },
330
+ "interactive.height" : {
331
+ "type" : "int",
332
+ "selector" : "iframe",
333
+ "attribute": "height"
334
+ }
335
+ }
336
+ }, {
337
+ "class": "InteractiveInsideParagraphRule",
338
  "selector" : "//div[@class='interactive' and iframe]",
339
  "properties" : {
340
  "interactive.url" : {
341
  "type" : "string",
342
  "selector" : "iframe",
343
  "attribute": "src"
344
+ },
345
+ "interactive.iframe" : {
346
+ "type" : "children",
347
+ "selector" : "iframe",
348
+ "attribute": "src"
349
+ },
350
+ "interactive.height" : {
351
+ "type" : "int",
352
+ "selector" : "iframe",
353
+ "attribute": "height"
354
+ },
355
+ "interactive.width" : {
356
+ "type" : "int",
357
+ "selector" : "iframe",
358
+ "attribute": "width"
359
+ }
360
+ }
361
+ }, {
362
+ "class": "InteractiveInsideParagraphRule",
363
+ "selector" : "table",
364
+ "properties" : {
365
+ "interactive.iframe" : {
366
+ "type" : "element",
367
+ "selector" : "table"
368
+ },
369
+ "interactive.height" : {
370
+ "type" : "int",
371
+ "selector" : "table",
372
+ "attribute": "height"
373
+ },
374
+ "interactive.width" : {
375
+ "type" : "int",
376
+ "selector" : "table",
377
+ "attribute": "width"
378
  }
379
  }
380
  }, {
424
  "attribute": "type"
425
  }
426
  }
427
+ },
428
+
429
+
430
+
431
+ {
432
+ "class" : "IgnoreRule",
433
+ "selector" : "p.jetpack-slideshow-noscript"
434
+ },
435
+ {
436
+ "class": "CaptionRule",
437
+ "selector" : "div.wp-caption-text"
438
+ },
439
+ {
440
+ "class" : "PassThroughRule",
441
+ "selector" : "div.gallery-row"
442
+ },
443
+ {
444
+ "class" : "PassThroughRule",
445
+ "selector" : "div.tiled-gallery p"
446
+ },
447
+ {
448
+ "class" : "PassThroughRule",
449
+ "selector" : "div.gallery-row p"
450
+ },
451
+ {
452
+ "class" : "PassThroughRule",
453
+ "selector" : "div.gallery-group p"
454
+ },
455
+ {
456
+ "class" : "PassThroughRule",
457
+ "selector" : "div.gallery-group"
458
+ },
459
+ {
460
+ "class": "ImageRule",
461
+ "selector" : "div.wp-caption",
462
+ "properties" : {
463
+ "image.url" : {
464
+ "type" : "string",
465
+ "selector" : "img",
466
+ "attribute": "src"
467
+ }
468
+ }
469
+ },
470
+ {
471
+ "class": "SlideshowImageRule",
472
+ "selector" : "div.tiled-gallery-item",
473
+ "properties" : {
474
+ "image.url" : {
475
+ "type" : "string",
476
+ "selector" : "img",
477
+ "attribute": "data-orig-file"
478
+ },
479
+ "caption.title" : {
480
+ "type" : "string",
481
+ "selector" : "div.tiled-gallery-caption"
482
+ }
483
+ }
484
+ },
485
+ {
486
+ "class": "SlideshowRule",
487
+ "selector" : "div.tiled-gallery"
488
+ },
489
+ {
490
+ "class": "Compat\\JetpackSlideshowRule",
491
+ "selector" : "div.jetpack-slideshow",
492
+ "properties": {
493
+ "jetpack.data-gallery": {
494
+ "type": "json",
495
+ "selector": "div.jetpack-slideshow",
496
+ "attribute": "data-gallery"
497
+ }
498
+ }
499
+ },
500
+ {
501
+ "class": "CaptionRule",
502
+ "selector" : "div.tiled-gallery-caption"
503
+ },
504
+
505
+
506
+ {
507
+ "class": "ImageInsideParagraphRule",
508
+ "selector": "figure.wp-caption",
509
+ "properties": {
510
+ "image.url": {
511
+ "type": "string",
512
+ "selector": "img",
513
+ "attribute": "src"
514
  }
515
  }
516
+ }
517
+ ]
518
  }
vendor/facebook/facebook-instant-articles-sdk-php/tests/Facebook/InstantArticles/Transformer/CMS/wp.html CHANGED
@@ -1,12 +1,133 @@
1
  <!DOCTYPE html>
2
- <html><body><p>Yes, peace is good for everybody!<br>
3
- Man kind.</p>
4
- <p><img src="http://example.com/image0.jpg" />Some text after img.</p>
5
- <p>Some text before img<img src="http://example.com/image1.jpg" /></p>
6
- <p>Some text before img<img src="http://example.com/image2.jpg" /> and some after img.</p>
7
- <ul>
8
- <li>Some text on li before img<img src="http://example.com/image.jpg" width="600" height="360"/></li>
9
- <li>Some text on li before img<img src="http://example.com/image.jpg" width="600" height="360"/>and after img</li>
10
- <li><img src="http://example.com/image.jpg" width="600" height="360"/>Some text after img</li>
11
- </ul>
12
- </body></html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <!DOCTYPE html>
2
+ <html>
3
+ <body>
4
+ <p>Yes, peace is good for everybody!<br>
5
+ Man kind.</p>
6
+ <p><img src="http://example.com/image0.jpg" />Some text after img.</p>
7
+ <p>Some text before img<img src="http://example.com/image1.jpg" /></p>
8
+ <p>Some text before img<img src="http://example.com/image2.jpg" /> and some after img.</p>
9
+ <p><iframe width="300" height="200" src="http://example.com/0/"/></p>
10
+ <p>Some text before iframe<iframe width="300" height="200" src="http://example.com/1/"/></p>
11
+ <p><iframe width="300" height="200" src="http://example.com/2/"></iframe> some after iframe.</p>
12
+ <p>Some text before iframe<iframe width="300" height="200" src="http://example.com/3/"/> and some after iframe.</p>
13
+ <iframe width="300" height="200" src="http://example.com/loose/with_url"></iframe>
14
+ <iframe>
15
+ <div>
16
+ <h1>Iframe loose without url</h1>
17
+ </div>
18
+ </iframe>
19
+ <div class="embed">
20
+ <div>
21
+ <h1>some embed here</h1>
22
+ </div>
23
+ </div>
24
+ <div class="interactive">
25
+ <div>
26
+ <h1>some embed here</h1>
27
+ </div>
28
+ </div>
29
+ <div class="interactive">
30
+ <iframe>
31
+ <div>some content</div>
32
+ <script>alert('hi & hello to you @ testing!');</script>
33
+ </iframe>
34
+ </div>
35
+ <div class="interactive">
36
+ <iframe width="300" height="200" src="http://example.com/4/"></iframe>
37
+ </div>
38
+ <div class="interactive">
39
+ <iframe>
40
+ <div>some content</div>
41
+ <script>alert('hi & hello to you @ testing!');</script>
42
+ </iframe>
43
+ </div>
44
+ <div class="embed">
45
+ <iframe width="300" height="200" src="http://example.com/4/"></iframe>
46
+ </div>
47
+ <div class="embed">
48
+ <iframe>
49
+ <div>some content</div>
50
+ <script>alert('hi & hello to you @ testing!');</script>
51
+ </iframe>
52
+ </div>
53
+ <table width="200" height="200">
54
+ <thead>
55
+ <td>header 1</td>
56
+ <td>header 2</td>
57
+ </thead>
58
+ <tbody>
59
+ <tr>
60
+ <td>Line 1 column 1</td>
61
+ <td>Line 1 column 2</td>
62
+ </tr>
63
+ <tr>
64
+ <td>Line 2 column 1</td>
65
+ <td>Line 2 column 2</td>
66
+ </tr>
67
+ </tbody>
68
+ </table>
69
+ <ul>
70
+ <li>Some text on li before img<img src="http://example.com/image.jpg" width="600" height="360"/></li>
71
+ <li>Some text on li before img<img src="http://example.com/image.jpg" width="600" height="360"/>and after img</li>
72
+ <li><img src="http://example.com/image.jpg" width="600" height="360"/>Some text after img</li>
73
+ </ul>
74
+ <p class="jetpack-slideshow-noscript robots-nocontent">This slideshow requires JavaScript.</p>
75
+ <div
76
+ class="slideshow-window jetpack-slideshow slideshow-black"
77
+ data-trans="fade" data-autostart="1"
78
+ data-gallery="[{&quot;src&quot;:&quot;http:\/\/example.com/img0.jpg&quot;,&quot;id&quot;:&quot;434&quot;,&quot;title&quot;:&quot;img0&quot;,&quot;alt&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;itemprop&quot;:&quot;image&quot;},{&quot;src&quot;:&quot;http:\/\/example.com\/img1.jpg&quot;,&quot;id&quot;:&quot;474&quot;,&quot;title&quot;:&quot;img1&quot;,&quot;alt&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;itemprop&quot;:&quot;image&quot;},{&quot;src&quot;:&quot;http:\/\/example.com\/img2.jpg&quot;,&quot;id&quot;:&quot;470&quot;,&quot;title&quot;:&quot;img2&quot;,&quot;alt&quot;:&quot;&quot;,&quot;caption&quot;:&quot;Image 2&quot;,&quot;itemprop&quot;:&quot;image&quot;},{&quot;src&quot;:&quot;http:\/\/example.com\/img3.jpg&quot;,&quot;id&quot;:&quot;466&quot;,&quot;title&quot;:&quot;Image 3&quot;,&quot;alt&quot;:&quot;&quot;,&quot;caption&quot;:&quot;Image 3&quot;,&quot;itemprop&quot;:&quot;image&quot;},{&quot;src&quot;:&quot;http:\/\/example.com\/img4.jpg&quot;,&quot;id&quot;:&quot;462&quot;,&quot;title&quot;:&quot;img4&quot;,&quot;alt&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;itemprop&quot;:&quot;image&quot;}]"
79
+ itemscope itemtype="http://schema.org/ImageGallery">
80
+ </div>
81
+
82
+ <div class="tiled-gallery type-rectangular tiled-gallery-unresized" >
83
+ <div class="gallery-row">
84
+ <div class="gallery-group images-1">
85
+ <div class="tiled-gallery-item tiled-gallery-item-large">
86
+ <a href="http://www.diegoquinteiro.com/15-razoes-para-ser-contra-o-impeachment/forum/">
87
+ <meta itemprop="width" content="318">
88
+ <meta itemprop="height" content="211">
89
+ <img data-orig-file="http://example.com/img1.jpg"
90
+ data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Forum" data-image-description="" data-medium-file="http://http://example.com/img1.jpg" data-large-file="http://example.com/img1.jpg" src="http://example.com/img1.jpg?resize=318%2C211"
91
+ width="318" height="211" data-original-width="318" data-original-height="211" title="Forum" alt="riots" style="width: 318px; height: 211px;" />
92
+ </a>
93
+ <div class="tiled-gallery-caption" itemprop="caption description"> Image 1 </div>
94
+ </p>
95
+ </div>
96
+ </p>
97
+ </div>
98
+ <p> <!-- close group -->
99
+ <div class="gallery-group images-2">
100
+ <div class="tiled-gallery-item tiled-gallery-item-small" itemprop="associatedMedia">
101
+ <a href="http://example.com/link" border="0" itemprop="url">
102
+ <meta itemprop="width" content="174">
103
+ <meta itemprop="height" content="109">
104
+ <img data-attachment-id="519" data-orig-file="http://example.com/img2.jpg" data-orig-size="2078,1310" data-comments-opened="" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="Chaplin &#8211; Fábrica" data-image-description="" data-medium-file="http://example.com/img2.jpg" data-large-file="http://example.com/img2.jpg" src="http://example.com/img2.jpg?resize=174%2C109" width="174" height="109" data-original-width="174" data-original-height="109" itemprop="http://schema.org/image" title="Image Title" alt="chaplin" style="width: 174px; height: 109px;" />
105
+ </a>
106
+ <div class="tiled-gallery-caption" itemprop="caption description"> Image 2 </div>
107
+ </p>
108
+ </div>
109
+ <div class="tiled-gallery-item tiled-gallery-item-small" itemprop="associatedMedia">
110
+ <a href="http://example.com/link" border="0" itemprop="url">
111
+ <meta itemprop="width" content="174">
112
+ <meta itemprop="height" content="98">
113
+ <img data-attachment-id="532" data-orig-file="http://example.com/img3.jpg" data-image-meta="{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}" data-image-title="highlight image" data-image-description="" data-medium-file="http://www.diegoquinteiro.com/wp-content/uploads/2016/06/highlight-image-300x169.jpg" data-large-file="http://www.diegoquinteiro.com/wp-content/uploads/2016/06/highlight-image-1024x576.jpg" src="http://i1.wp.com/www.diegoquinteiro.com/wp-content/uploads/2016/06/highlight-image.jpg?resize=174%2C98" width="174" height="98" data-original-width="174" data-original-height="98" itemprop="http://schema.org/image" title="highlight image" alt="Dog cat" style="width: 174px; height: 98px;" />
114
+ </a>
115
+ <div class="tiled-gallery-caption" itemprop="caption description"> Image 3 </div>
116
+ </p>
117
+ </div>
118
+ </p>
119
+ </div>
120
+ <p> <!-- close group -->
121
+ </div>
122
+ <p> <!-- close row -->
123
+ </div>
124
+ <p>
125
+ <figure style="width: 1280px" class="wp-caption alignnone">
126
+ <img src="http://example.com/img.jpg" width="1280" height="740" class />
127
+ <br />
128
+ <figcaption class="wp-caption-text">blue eyes</figcaption>
129
+ </figure>
130
+ </p>
131
+
132
+ </body>
133
+ </html>
vendor/facebook/facebook-instant-articles-sdk-php/tests/Facebook/InstantArticles/Transformer/Example/simple-ia.html CHANGED
@@ -26,6 +26,11 @@
26
  <figcaption>Photographer</figcaption>
27
  </figure>
28
  <p>Curabitur vulputate odio eu justo <i>venenatis</i>, a pretium orci placerat. Nam sed neque quis eros vestibulum mattis. Donec vitae mi egestas, laoreet massa et, fringilla libero.</p>
 
 
 
 
 
29
  <figure class="op-interactive">
30
  <iframe>
31
  <h1>Custom code for your social embed</h1>
26
  <figcaption>Photographer</figcaption>
27
  </figure>
28
  <p>Curabitur vulputate odio eu justo <i>venenatis</i>, a pretium orci placerat. Nam sed neque quis eros vestibulum mattis. Donec vitae mi egestas, laoreet massa et, fringilla libero.</p>
29
+ <figure class="op-interactive">
30
+ <iframe>
31
+ <iframe width="620" height="349" src="https://www.youtube.com/embed/s1tN0ggNreA?feature=oembed" frameborder="0" allowfullscreen=""/>
32
+ </iframe>
33
+ </figure>
34
  <figure class="op-interactive">
35
  <iframe>
36
  <h1>Custom code for your social embed</h1>
vendor/facebook/facebook-instant-articles-sdk-php/tests/Facebook/InstantArticles/Transformer/Example/simple.html CHANGED
@@ -18,6 +18,9 @@
18
  <img src="http://domain.com/image-body.png" alt="Photographer"/>
19
  </div>
20
  <p>Curabitur vulputate odio eu justo <i>venenatis</i>, a pretium orci placerat. Nam sed neque quis eros vestibulum mattis. Donec vitae mi egestas, laoreet massa et, fringilla libero.</p>
 
 
 
21
  <div class="embed">
22
  <h1>Custom code for your social embed</h1>
23
  <script>alert("test & more test");</script>
18
  <img src="http://domain.com/image-body.png" alt="Photographer"/>
19
  </div>
20
  <p>Curabitur vulputate odio eu justo <i>venenatis</i>, a pretium orci placerat. Nam sed neque quis eros vestibulum mattis. Donec vitae mi egestas, laoreet massa et, fringilla libero.</p>
21
+ <div class="embed">
22
+ <iframe width="620" height="349" src="https://www.youtube.com/embed/s1tN0ggNreA?feature=oembed" frameborder="0" allowfullscreen></iframe>
23
+ </div>
24
  <div class="embed">
25
  <h1>Custom code for your social embed</h1>
26
  <script>alert("test & more test");</script>
vendor/facebook/facebook-instant-articles-sdk-php/tests/Facebook/InstantArticles/Transformer/TransformerTest.php CHANGED
@@ -97,7 +97,7 @@ class TransformerTest extends \PHPUnit_Framework_TestCase
97
  public function testTitleTransformedWithBold()
98
  {
99
  $transformer = new Transformer();
100
- $json_file = file_get_contents(__DIR__ . '/wp-rules.json');
101
  $transformer->loadRules($json_file);
102
 
103
  $title_html_string = '<?xml encoding="utf-8" ?><h1>Title <b>in bold</b></h1>';
97
  public function testTitleTransformedWithBold()
98
  {
99
  $transformer = new Transformer();
100
+ $json_file = file_get_contents(__DIR__ . '/CMS/wp-rules.json');
101
  $transformer->loadRules($json_file);
102
 
103
  $title_html_string = '<?xml encoding="utf-8" ?><h1>Title <b>in bold</b></h1>';
vendor/facebook/php-sdk-v4/src/Facebook/Facebook.php CHANGED
@@ -53,12 +53,12 @@ class Facebook
53
  /**
54
  * @const string Version number of the Facebook PHP SDK.
55
  */
56
- const VERSION = '5.2.0';
57
 
58
  /**
59
  * @const string Default Graph API version for requests.
60
  */
61
- const DEFAULT_GRAPH_VERSION = 'v2.6';
62
 
63
  /**
64
  * @const string The name of the environment variable that contains the app ID.
53
  /**
54
  * @const string Version number of the Facebook PHP SDK.
55
  */
56
+ const VERSION = '5.3.1';
57
 
58
  /**
59
  * @const string Default Graph API version for requests.
60
  */
61
+ const DEFAULT_GRAPH_VERSION = 'v2.7';
62
 
63
  /**
64
  * @const string The name of the environment variable that contains the app ID.
vendor/facebook/php-sdk-v4/src/Facebook/FacebookResponse.php CHANGED
@@ -213,7 +213,7 @@ class FacebookResponse
213
  /**
214
  * Returns the exception that was thrown for this request.
215
  *
216
- * @return FacebookSDKException|null
217
  */
218
  public function getThrownException()
219
  {
213
  /**
214
  * Returns the exception that was thrown for this request.
215
  *
216
+ * @return FacebookResponseException|null
217
  */
218
  public function getThrownException()
219
  {
vendor/facebook/php-sdk-v4/src/Facebook/Helpers/FacebookRedirectLoginHelper.php CHANGED
@@ -122,7 +122,7 @@ class FacebookRedirectLoginHelper
122
  */
123
  private function makeUrl($redirectUrl, array $scope, array $params = [], $separator = '&')
124
  {
125
- $state = $this->pseudoRandomStringGenerator->getPseudoRandomString(static::CSRF_LENGTH);
126
  $this->persistentDataHandler->set('state', $state);
127
 
128
  return $this->oAuth2Client->getAuthorizationUrl($redirectUrl, $state, $scope, $params, $separator);
@@ -219,6 +219,7 @@ class FacebookRedirectLoginHelper
219
  }
220
 
221
  $this->validateCsrf();
 
222
 
223
  $redirectUrl = $redirectUrl ?: $this->urlDetectionHandler->getCurrentUrl();
224
  // At minimum we need to remove the state param
@@ -250,6 +251,14 @@ class FacebookRedirectLoginHelper
250
  throw new FacebookSDKException('Cross-site request forgery validation failed. The "state" param from the URL and session do not match.');
251
  }
252
 
 
 
 
 
 
 
 
 
253
  /**
254
  * Return the code.
255
  *
122
  */
123
  private function makeUrl($redirectUrl, array $scope, array $params = [], $separator = '&')
124
  {
125
+ $state = $this->persistentDataHandler->get('state') ?: $this->pseudoRandomStringGenerator->getPseudoRandomString(static::CSRF_LENGTH);
126
  $this->persistentDataHandler->set('state', $state);
127
 
128
  return $this->oAuth2Client->getAuthorizationUrl($redirectUrl, $state, $scope, $params, $separator);
219
  }
220
 
221
  $this->validateCsrf();
222
+ $this->resetCsrf();
223
 
224
  $redirectUrl = $redirectUrl ?: $this->urlDetectionHandler->getCurrentUrl();
225
  // At minimum we need to remove the state param
251
  throw new FacebookSDKException('Cross-site request forgery validation failed. The "state" param from the URL and session do not match.');
252
  }
253
 
254
+ /**
255
+ * Resets the CSRF so that it doesn't get reused.
256
+ */
257
+ private function resetCsrf()
258
+ {
259
+ $this->persistentDataHandler->set('state', null);
260
+ }
261
+
262
  /**
263
  * Return the code.
264
  *
vendor/facebook/php-sdk-v4/src/Facebook/PseudoRandomString/PseudoRandomStringGeneratorTrait.php CHANGED
@@ -48,7 +48,6 @@ trait PseudoRandomStringGeneratorTrait
48
  *
49
  * @param string $binaryData The binary data to convert to hex.
50
  * @param int $length The length of the string to return.
51
- * @throws \RuntimeException Throws an exception when multibyte support is not enabled
52
  *
53
  * @return string
54
  */
48
  *
49
  * @param string $binaryData The binary data to convert to hex.
50
  * @param int $length The length of the string to return.
 
51
  *
52
  * @return string
53
  */
vendor/facebook/php-sdk-v4/src/Facebook/Url/FacebookUrlDetectionHandler.php CHANGED
@@ -95,7 +95,8 @@ class FacebookUrlDetectionHandler implements UrlDetectionInterface
95
  protected function getHostName()
96
  {
97
  // Check for proxy first
98
- if ($header = $this->getHeader('X_FORWARDED_HOST') && $this->isValidForwardedHost($header)) {
 
99
  $elements = explode(',', $header);
100
  $host = $elements[count($elements) - 1];
101
  } elseif (!$host = $this->getHeader('HOST')) {
95
  protected function getHostName()
96
  {
97
  // Check for proxy first
98
+ $header = $this->getHeader('X_FORWARDED_HOST');
99
+ if ($header && $this->isValidForwardedHost($header)) {
100
  $elements = explode(',', $header);
101
  $host = $elements[count($elements) - 1];
102
  } elseif (!$host = $this->getHeader('HOST')) {
vendor/facebook/php-sdk-v4/src/Facebook/autoload.php CHANGED
@@ -32,6 +32,8 @@ if (version_compare(PHP_VERSION, '5.4.0', '<')) {
32
  throw new Exception('The Facebook SDK requires PHP version 5.4 or higher.');
33
  }
34
 
 
 
35
  /**
36
  * Register the autoloader for the Facebook SDK classes.
37
  *
32
  throw new Exception('The Facebook SDK requires PHP version 5.4 or higher.');
33
  }
34
 
35
+ require_once __DIR__ . '/polyfills.php';
36
+
37
  /**
38
  * Register the autoloader for the Facebook SDK classes.
39
  *
wizard/class-instant-articles-invalid-wizard-transition-exception.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Facebook Instant Articles for WP.
4
+ * This source code is licensed under the license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @package default
8
+ */
9
+
10
+ /**
11
+ * Exception thrown when triggering an invalid transition on the state machine.
12
+ *
13
+ * @since 3.1
14
+ */
15
+ class Instant_Articles_Invalid_Wizard_Transition_Exception extends Exception {
16
+ public function __construct ( $original_state, $new_state ) {
17
+ parent::__construct( "Invalid transition: $original_state => $new_state." );
18
+ }
19
+ }
wizard/class-instant-articles-option-ads.php ADDED
@@ -0,0 +1,245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Facebook Instant Articles for WP.
4
+ * This source code is licensed under the license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @package default
8
+ */
9
+
10
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-option.php' );
11
+
12
+ /**
13
+ * Configuration class for Ads.
14
+ */
15
+ class Instant_Articles_Option_Ads extends Instant_Articles_Option {
16
+
17
+ const OPTION_KEY = 'instant-articles-option-ads';
18
+
19
+ public static $sections = array(
20
+ 'title' => 'Ads',
21
+ 'description' => '<p>Choose your preferred method for displaying ads in your Instant Articles and input the code in the boxes below. Learn more about your options for <a href="https://developers.facebook.com/docs/instant-articles/ads-analytics" target="_blank">advertising in Instant Articles</a>.</p>',
22
+ );
23
+
24
+ public static $fields = array(
25
+
26
+ 'ad_source' => array(
27
+ 'label' => 'Ad Type',
28
+ 'description' => 'This plugin will automatically place the ads within your articles.',
29
+ 'render' => array( 'Instant_Articles_Option_Ads', 'custom_render_ad_source' ),
30
+ 'select_options' => array(
31
+ 'none' => 'None',
32
+ 'fan' => 'Facebook Audience Network',
33
+ 'iframe' => 'Custom iframe URL',
34
+ 'embed' => 'Custom Embed Code',
35
+ ),
36
+ 'default' => 'none',
37
+ ),
38
+
39
+ 'fan_placement_id' => array(
40
+ 'label' => 'Audience Network Placement ID',
41
+ 'description' => 'Find your <a href="https://developers.facebook.com/docs/audience-network/instantarticles/banner" target="_blank">Placement ID</a> for Facebook Audience Network on your app\'s Audience Network Portal',
42
+ 'default' => null,
43
+ ),
44
+
45
+ 'iframe_url' => array(
46
+ 'label' => 'Source URL',
47
+ 'placeholder' => '//ad-server.com/my-ad',
48
+ 'description' => 'Note: Instant Articles only supports Direct Sold ads. No programmatic ad networks, other than Facebook\'s Audience Network, are permitted.',
49
+ 'default' => '',
50
+ ),
51
+
52
+ 'embed_code' => array(
53
+ 'label' => 'Embed Code',
54
+ 'render' => 'textarea',
55
+ 'description' => 'Add code to be used for displayed ads in your Instant Articles.',
56
+ 'default' => '',
57
+ 'placeholder' => '<script>...</script>',
58
+ 'double_encode' => true,
59
+ ),
60
+
61
+ 'dimensions' => array(
62
+ 'label' => 'Ad Dimensions',
63
+ 'render' => 'select',
64
+ 'select_options' => array(
65
+ '300x250' => 'Large (300 x 250)',
66
+ '320x50' => 'Small (320 x 50)',
67
+ ),
68
+ 'default' => '300x250',
69
+ ),
70
+
71
+ );
72
+
73
+ /**
74
+ * Constructor.
75
+ *
76
+ * @since 0.4
77
+ */
78
+ public function __construct() {
79
+ parent::__construct(
80
+ self::OPTION_KEY,
81
+ self::$sections,
82
+ self::$fields
83
+ );
84
+ wp_localize_script( 'instant-articles-option-ads', 'INSTANT_ARTICLES_OPTION_ADS', array(
85
+ 'option_field_id_source' => self::OPTION_KEY . '-ad_source',
86
+ 'option_field_id_fan' => self::OPTION_KEY . '-fan_placement_id',
87
+ 'option_field_id_iframe' => self::OPTION_KEY . '-iframe_url',
88
+ 'option_field_id_embed' => self::OPTION_KEY . '-embed_code',
89
+ 'option_field_id_dimensions' => self::OPTION_KEY . '-dimensions',
90
+ ) );
91
+ }
92
+
93
+ /**
94
+ * Renders the ad source.
95
+ *
96
+ * @param array $args configuration fields for the ad.
97
+ * @since 0.4
98
+ */
99
+ public static function custom_render_ad_source( $args ) {
100
+ $id = $args['label_for'];
101
+ $name = $args['serialized_with_group'] . '[ad_source]';
102
+
103
+ $description = isset( $args['description'] )
104
+ ? '<p class="description">' . esc_html( $args['description'] ) . '</p>'
105
+ : '';
106
+
107
+ ?>
108
+ <select id="<?php echo esc_attr( $id ); ?>" name="<?php echo esc_attr( $name ); ?>" >
109
+ <?php foreach ( $args['select_options'] as $ad_source_key => $ad_source_name ) : ?>
110
+ <option
111
+ value="<?php echo esc_attr( $ad_source_key ); ?>"
112
+ <?php echo selected( self::$settings['ad_source'], $ad_source_key ) ?>
113
+ >
114
+ <?php echo esc_html( $ad_source_name ); ?>
115
+ </option>
116
+ <?php endforeach; ?>
117
+
118
+ <?php
119
+ $compat_plugins = parent::get_registered_compat( 'instant_articles_compat_registry_ads' );
120
+ asort( $compat_plugins );
121
+ if ( count( $compat_plugins ) > 0 ) :
122
+ ?>
123
+ <optgroup label="From Supported Plugins">
124
+ <?php foreach ( $compat_plugins as $ad_source_key => $ad_source_info ) : ?>
125
+ <option
126
+ value="<?php echo esc_attr( $ad_source_key ); ?>"
127
+ <?php echo selected( self::$settings['ad_source'], $ad_source_key ) ?>
128
+ >
129
+ <?php echo esc_html( $ad_source_info['name'] ); ?>
130
+ </option>
131
+ <?php endforeach; ?>
132
+ </optgroup>
133
+ <?php endif; ?>
134
+
135
+ ?>
136
+ </select>
137
+ <?php echo wp_kses_post( $description ); ?>
138
+ <?php
139
+ }
140
+
141
+ /**
142
+ * Sanitize and return all the field values.
143
+ *
144
+ * This method receives a payload containing all value for its fields and
145
+ * should return the same payload after having been sanitized.
146
+ *
147
+ * Do not, encode the payload as this is performed by the
148
+ * universal_sanitize_and_encode_handler() of the parent class.
149
+ *
150
+ * @param array $field_values The array map with field values.
151
+ * @since 0.5
152
+ */
153
+ public function sanitize_option_fields( $field_values ) {
154
+ foreach ( $field_values as $field_id => $field_value ) {
155
+ $field = self::$fields[ $field_id ];
156
+
157
+ switch ( $field_id ) {
158
+ case 'ad_source':
159
+ $all_options = array();
160
+
161
+ $registered_compat_ads = Instant_Articles_Option::get_registered_compat(
162
+ 'instant_articles_compat_registry_ads'
163
+ );
164
+
165
+ foreach ( $field['select_options'] as $option_id => $option_info ) {
166
+ $all_options[] = $option_id;
167
+ }
168
+ foreach ( $registered_compat_ads as $compat_id => $compat_info ) {
169
+ $all_options[] = $compat_id;
170
+ }
171
+
172
+ if ( ! in_array( $field_value, $all_options, true ) ) {
173
+ $field_values[ $field_id ] = $field['default'];
174
+ add_settings_error(
175
+ $field_id,
176
+ 'invalid_option',
177
+ 'Invalid Ad Source'
178
+ );
179
+ }
180
+ break;
181
+
182
+ case 'fan_placement_id':
183
+ if ( isset( $field_values['ad_source'] ) && 'fan' === $field_values['ad_source'] ) {
184
+ if ( preg_match( '/^[0-9_]+$/', $field_values[ $field_id ] ) !== 1 ) {
185
+ add_settings_error(
186
+ $field_id,
187
+ 'invalid_placement_id',
188
+ 'Invalid Audience Network Placement ID provided'
189
+ );
190
+ $field_values[ $field_id ] = $field['default'];
191
+ }
192
+ }
193
+ break;
194
+
195
+ case 'iframe_url':
196
+ if ( isset( $field_values['ad_source'] ) && 'iframe' === $field_values['ad_source'] ) {
197
+ $url = $field_values[ $field_id ];
198
+ if ( substr( $url, 0, 2 ) === '//' ) {
199
+ // Allow URLs without protocol prefix
200
+ $url = 'http:' . $url;
201
+ }
202
+ $url = filter_var( $url , FILTER_VALIDATE_URL );
203
+ if ( ! $url ) {
204
+ $field_values[ $field_id ] = $field['default'];
205
+ add_settings_error(
206
+ $field_id,
207
+ 'invalid_url',
208
+ 'Invalid URL provided for Ad iframe'
209
+ );
210
+ }
211
+ }
212
+ break;
213
+
214
+ case 'embed_code':
215
+ if ( isset( $field_values['ad_source'] ) && 'embed' === $field_values['ad_source'] ) {
216
+ $document = new DOMDocument();
217
+ $fragment = $document->createDocumentFragment();
218
+ if ( ! @$fragment->appendXML( $field_values[ $field_id ] ) ) {
219
+ add_settings_error(
220
+ 'embed_code',
221
+ 'invalid_markup',
222
+ 'Invalid HTML markup provided for ad custom embed code'
223
+ );
224
+ }
225
+ }
226
+ break;
227
+
228
+ case 'dimensions':
229
+ if ( isset( $field_values['ad_source'] ) && 'none' !== $field_values['ad_source'] ) {
230
+ if ( ! array_key_exists( $field_value, $field['select_options'] ) ) {
231
+ $field_values[ $field_id ] = $field['default'];
232
+ add_settings_error(
233
+ 'embed_code',
234
+ 'invalid_dimensions',
235
+ 'Invalid dimensions provided for Ad'
236
+ );
237
+ }
238
+ }
239
+ break;
240
+ }
241
+ }
242
+
243
+ return $field_values;
244
+ }
245
+ }
wizard/class-instant-articles-option-analytics.php ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Facebook Instant Articles for WP.
4
+ * This source code is licensed under the license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @package default
8
+ */
9
+
10
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-option.php' );
11
+
12
+ /**
13
+ * Analytics configuration class.
14
+ */
15
+ class Instant_Articles_Option_Analytics extends Instant_Articles_Option {
16
+
17
+ const OPTION_KEY = 'instant-articles-option-analytics';
18
+
19
+ public static $sections = array(
20
+ 'title' => 'Analytics',
21
+ 'description' => '<p>Enable 3rd-party analytics to be used with Instant Articles.</p><p>If you already use a WordPress plugin to manage analytics, you can enable it below. You can also embed code to insert your own trackers and analytics. <a href="https://developers.facebook.com/docs/instant-articles/ads-analytics#analytics" target="_blank">Learn more about Analytics in Instant Articles</a>.</p>',
22
+ );
23
+
24
+ public static $fields = array(
25
+
26
+ 'integrations' => array(
27
+ 'label' => '3rd party integrations',
28
+ 'render' => array( 'Instant_Articles_Option_Analytics', 'custom_render_integrations' ),
29
+ 'default' => array(),
30
+ ),
31
+
32
+ 'embed_code_enabled' => array(
33
+ 'label' => 'Embed code',
34
+ 'render' => 'checkbox',
35
+ 'default' => false,
36
+ 'description' => 'Add code for any other analytics services you wish to use.',
37
+ 'checkbox_label' => 'Enable custom embed code',
38
+ ),
39
+
40
+ 'embed_code' => array(
41
+ 'label' => '',
42
+ 'render' => 'textarea',
43
+ 'placeholder' => '<script>...</script>',
44
+ 'description' => 'Note: You do not need to include any &lt;op-tracker&gt; tags. The plugin will automatically include them in the article markup.',
45
+ 'default' => '',
46
+ 'double_encode' => true,
47
+ ),
48
+ );
49
+
50
+ /**
51
+ * Constructor.
52
+ *
53
+ * @since 0.4
54
+ */
55
+ public function __construct() {
56
+ parent::__construct(
57
+ self::OPTION_KEY,
58
+ self::$sections,
59
+ self::$fields
60
+ );
61
+ wp_localize_script( 'instant-articles-option-analytics', 'INSTANT_ARTICLES_OPTION_ANALYTICS', array(
62
+ 'option_field_id_embed_code_enabled' => self::OPTION_KEY . '-embed_code_enabled',
63
+ 'option_field_id_embed_code' => self::OPTION_KEY . '-embed_code',
64
+ ) );
65
+ }
66
+
67
+ /**
68
+ * Renders the markup for the `integrations` field.
69
+ *
70
+ * @param array $args The array with configuration of fields.
71
+ * @since 0.4
72
+ */
73
+ public static function custom_render_integrations( $args ) {
74
+ $name = $args['serialized_with_group'] . '[integrations][]';
75
+
76
+ $compat_plugins = parent::get_registered_compat( 'instant_articles_compat_registry_analytics' );
77
+
78
+ if ( empty( $compat_plugins ) ) {
79
+ ?>
80
+ <em>
81
+ <?php echo esc_html( 'No supported analytics plugins are installed nor activated' ); ?>
82
+ </em>
83
+ <?php
84
+
85
+ return;
86
+ }
87
+
88
+ asort( $compat_plugins );
89
+ foreach ( $compat_plugins as $plugin_id => $plugin_info ) {
90
+ ?>
91
+ <label>
92
+ <input
93
+ type="checkbox"
94
+ name="<?php echo esc_attr( $name ); ?>"
95
+ value="<?php echo esc_attr( $plugin_id ); ?>"
96
+ <?php echo checked( in_array( $plugin_id, self::$settings['integrations'], true ) ) ?>
97
+ >
98
+ <?php echo esc_html( $plugin_info['name'] ); ?>
99
+ </label>
100
+ <br />
101
+ <?php
102
+ }
103
+ ?>
104
+ <p class="description">Select which analytics services you'd like to use with Instant Articles.</p>
105
+ <?php
106
+ }
107
+
108
+ /**
109
+ * Sanitize and return all the field values.
110
+ *
111
+ * This method receives a payload containing all value for its fields and
112
+ * should return the same payload after having been sanitized.
113
+ *
114
+ * Do not encode the payload as this is performed by the
115
+ * universal_sanitize_and_encode_handler() of the parent class.
116
+ *
117
+ * @param array $field_values The values in an array mapped keys.
118
+ * @since 0.5
119
+ */
120
+ public function sanitize_option_fields( $field_values ) {
121
+ foreach ( $field_values as $field_id => $field_value ) {
122
+ $field = self::$fields[ $field_id ];
123
+
124
+ switch ( $field_id ) {
125
+ case 'embed_code':
126
+ if ( isset( $field_values['embed_code_enabled'] ) && $field_values['embed_code_enabled'] ) {
127
+ $document = new DOMDocument();
128
+ $fragment = $document->createDocumentFragment();
129
+ if ( ! @$fragment->appendXML( $field_values[ $field_id ] ) ) {
130
+ add_settings_error(
131
+ 'embed_code',
132
+ 'invalid_markup',
133
+ 'Invalid HTML markup provided for custom analytics tracker code'
134
+ );
135
+ }
136
+ }
137
+
138
+ break;
139
+ }
140
+ }
141
+
142
+ return $field_values;
143
+ }
144
+ }
wizard/class-instant-articles-option-fb-app.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Facebook Instant Articles for WP.
4
+ * This source code is licensed under the license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @package default
8
+ */
9
+
10
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-option.php' );
11
+
12
+ /**
13
+ * The Instant Articles FB app configuration.
14
+ */
15
+ class Instant_Articles_Option_FB_App extends Instant_Articles_Option {
16
+
17
+ const OPTION_KEY = 'instant-articles-option-fb-app';
18
+
19
+ public static $sections = array(
20
+ 'title' => '',
21
+ 'description' => '',
22
+ );
23
+
24
+ public static $fields = array(
25
+
26
+ 'app_id' => array(
27
+ 'label' => 'App ID',
28
+ 'default' => '',
29
+ ),
30
+
31
+ 'app_secret' => array(
32
+ 'label' => 'App Secret',
33
+ 'render' => 'password',
34
+ 'default' => '',
35
+ ),
36
+
37
+ 'user_access_token' => array(
38
+ 'visible' => false,
39
+ 'label' => 'User Access Token',
40
+ 'render' => 'password',
41
+ 'default' => '',
42
+ ),
43
+
44
+ );
45
+
46
+ /**
47
+ * Consturctor.
48
+ *
49
+ * @since 0.4
50
+ */
51
+ public function __construct() {
52
+ $this->options_manager = new parent(
53
+ self::OPTION_KEY,
54
+ self::$sections,
55
+ self::$fields,
56
+ /**
57
+ * Register this Option on a specific page group (used as the first
58
+ * argument of `register_setting()` and called by `settings_fields()`).
59
+ *
60
+ * @since 0.5
61
+ */
62
+ Instant_Articles_Option::PAGE_OPTION_GROUP_WIZARD
63
+ );
64
+ }
65
+ }
wizard/class-instant-articles-option-fb-page.php ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Facebook Instant Articles for WP.
4
+ * This source code is licensed under the license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @package default
8
+ */
9
+
10
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-option.php' );
11
+
12
+ /**
13
+ * FB Page configuration.
14
+ */
15
+ class Instant_Articles_Option_FB_Page extends Instant_Articles_Option {
16
+
17
+ const OPTION_KEY = 'instant-articles-option-fb-page';
18
+
19
+ public static $sections = array(
20
+ 'title' => '',
21
+ 'description' => '',
22
+ );
23
+
24
+ public static $fields = array(
25
+
26
+ 'page_id' => array(
27
+ 'visible' => false,
28
+ 'label' => 'Page ID',
29
+ 'default' => '',
30
+ ),
31
+
32
+ 'page_name' => array(
33
+ 'visible' => false,
34
+ 'label' => 'Page Name',
35
+ 'default' => '',
36
+ ),
37
+
38
+ 'page_picture' => array(
39
+ 'visible' => false,
40
+ 'label' => 'Page Picture',
41
+ 'default' => '',
42
+ ),
43
+
44
+ 'page_access_token' => array(
45
+ 'visible' => false,
46
+ 'label' => 'Page Access Token',
47
+ 'default' => '',
48
+ ),
49
+
50
+ 'page_access_token_expiration' => array(
51
+ 'visible' => false,
52
+ 'label' => 'Page Token Expiration',
53
+ 'default' => '',
54
+ ),
55
+
56
+ );
57
+
58
+ /**
59
+ * Constructor.
60
+ *
61
+ * @since 0.4
62
+ */
63
+ public function __construct() {
64
+ $this->options_manager = new parent(
65
+ self::OPTION_KEY,
66
+ self::$sections,
67
+ self::$fields,
68
+ /**
69
+ * Register this Option on a specific page group (used as the first
70
+ * argument of `register_setting()` and called by `settings_fields()`).
71
+ *
72
+ * @since 0.5
73
+ */
74
+ Instant_Articles_Option::PAGE_OPTION_GROUP_WIZARD
75
+ );
76
+ }
77
+ }
wizard/class-instant-articles-option-publishing.php ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Facebook Instant Articles for WP.
4
+ * This source code is licensed under the license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @package default
8
+ */
9
+
10
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-option.php' );
11
+
12
+ /**
13
+ * Class for publishing configuration.
14
+ */
15
+ class Instant_Articles_Option_Publishing extends Instant_Articles_Option {
16
+
17
+ const OPTION_KEY = 'instant-articles-option-publishing';
18
+
19
+ public static $sections = array(
20
+ 'title' => 'Publishing Settings',
21
+ );
22
+
23
+ public static $fields = array(
24
+
25
+ 'dev_mode' => array(
26
+ 'label' => 'Development Mode',
27
+ 'description' => 'Articles published while in Development Mode are saved as "drafts" within Facebook and will not be made live. Note: Since articles in "draft" are not reviewed, Development Mode should be disabled when publishing articles to Facebook which you intend to use in your <a href="https://developers.facebook.com/docs/instant-articles/publishing#review" target="_blank">one-time review</a>.',
28
+ 'render' => 'checkbox',
29
+ 'default' => false,
30
+ 'checkbox_label' => 'Enable development mode',
31
+ ),
32
+
33
+ 'custom_rules_enabled' => array(
34
+ 'label' => 'Custom transformer rules',
35
+ 'render' => 'checkbox',
36
+ 'default' => '',
37
+ 'checkbox_label' => 'Enable custom transformer rules',
38
+ 'description' => 'Define your own rules to customize the transformation of your content into Instant Articles',
39
+ 'default' => '',
40
+ ),
41
+
42
+ 'custom_rules' => array(
43
+ 'label' => '',
44
+ 'render' => 'textarea',
45
+ 'placeholder' => '{ "rules": [{ "class": "BoldRule", "selector": "span.bold" }, ... ] }',
46
+ 'description' => 'Read more about <a href="https://github.com/facebook/facebook-instant-articles-sdk-php/blob/master/docs/QuickStart.md#custom-transformer-rules" target="_blank">defining your own custom rules</a> to extend/override the <a href="https://github.com/Automattic/facebook-instant-articles-wp/blob/master/rules-configuration.json" target="_blank">built-in ruleset</a>. If you\'ve defined a common rule which you think this plugin should include by default, <a href="https://github.com/Automattic/facebook-instant-articles-wp/issues/new" target="_blank">tell us about it</a>!',
47
+ 'default' => '',
48
+ ),
49
+
50
+ 'block_publish_with_warnings' => array(
51
+ 'label' => 'Transformation warnings',
52
+ 'description' => 'Articles won\'t be available as Instant Articles if transformation process contains warnings. Note: All warnings should be fixed in order to have them available as Instant Articles.',
53
+ 'render' => 'checkbox',
54
+ 'default' => false,
55
+ 'checkbox_label' => 'Don\'t publish with warnings',
56
+ ),
57
+ );
58
+
59
+ /**
60
+ * Constructor.
61
+ *
62
+ * @since 0.4
63
+ */
64
+ public function __construct() {
65
+ parent::__construct(
66
+ self::OPTION_KEY,
67
+ self::$sections,
68
+ self::$fields
69
+ );
70
+ wp_localize_script( 'instant-articles-option-publishing', 'INSTANT_ARTICLES_OPTION_PUBLISHING', array(
71
+ 'option_field_id_custom_rules_enabled' => self::OPTION_KEY . '-custom_rules_enabled',
72
+ 'option_field_id_custom_rules' => self::OPTION_KEY . '-custom_rules',
73
+ ) );
74
+ }
75
+
76
+ /**
77
+ * Sanitize and return all the field values.
78
+ *
79
+ * This method receives a payload containing all value for its fields and
80
+ * should return the same payload after having been sanitized.
81
+ *
82
+ * Do not encode the payload as this is performed by the
83
+ * universal_sanitize_and_encode_handler() of the parent class.
84
+ *
85
+ * @param array $field_values array map with key field_id => value.
86
+ * @since 0.5
87
+ */
88
+ public function sanitize_option_fields( $field_values ) {
89
+ foreach ( $field_values as $field_id => $field_value ) {
90
+ $field = self::$fields[ $field_id ];
91
+
92
+ switch ( $field_id ) {
93
+ case 'dev_mode':
94
+ $field_values[ $field_id ] = (bool) $field_value
95
+ ? (string) true
96
+ : (string) $field['default'];
97
+ break;
98
+
99
+ case 'custom_rules':
100
+ if ( isset( $field_values['custom_rules_enabled'] ) && $field_values['custom_rules_enabled'] ) {
101
+ $custom_rules_json = json_decode( $field_values['custom_rules'] );
102
+ if ( null === $custom_rules_json ) {
103
+ $field_values['custom_rules'] = $field['default'];
104
+ add_settings_error(
105
+ 'custom_embed',
106
+ 'invalid_json',
107
+ 'Invalid JSON provided for custom rules code'
108
+ );
109
+ }
110
+ }
111
+ break;
112
+
113
+ default:
114
+ // Should never happen.
115
+ break;
116
+ }
117
+ }
118
+
119
+ return $field_values;
120
+ }
121
+ }
wizard/class-instant-articles-option-styles.php ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Facebook Instant Articles for WP.
4
+ * This source code is licensed under the license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @package default
8
+ */
9
+
10
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-option.php' );
11
+
12
+ /**
13
+ * Configuration class for Ads.
14
+ */
15
+ class Instant_Articles_Option_Styles extends Instant_Articles_Option {
16
+
17
+ const OPTION_KEY = 'instant-articles-option-styles';
18
+
19
+ public static $sections = array(
20
+ 'title' => 'Styles',
21
+ 'description' => '<p>Assign your Instant Articles a custom style. To begin, customize a template using the Style Editor. Next, input the name of the style below.</p><p><strong>Note:</strong> If this field is left blank, the plugin will enable the “Default” style. Learn more about Instant Articles style options in the <a href="https://developers.facebook.com/docs/instant-articles/guides/design" target="_blank">Design Guide</a>.</p>',
22
+ );
23
+
24
+ public static $fields = array(
25
+
26
+ 'article_style' => array(
27
+ 'label' => 'Article Style',
28
+ 'default' => 'default',
29
+ ),
30
+
31
+ );
32
+
33
+ /**
34
+ * Constructor.
35
+ *
36
+ * @since 0.4
37
+ */
38
+ public function __construct() {
39
+ parent::__construct(
40
+ self::OPTION_KEY,
41
+ self::$sections,
42
+ self::$fields
43
+ );
44
+ }
45
+ }
wizard/class-instant-articles-option.php ADDED
@@ -0,0 +1,490 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Facebook Instant Articles for WP.
4
+ * This source code is licensed under the license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @package default
8
+ */
9
+
10
+ /**
11
+ * Base class for configurations.
12
+ */
13
+ class Instant_Articles_Option {
14
+
15
+ const PAGE_OPTION_GROUP = 'instant-articles-settings';
16
+ const PAGE_OPTION_GROUP_WIZARD = 'instant-articles-settings-wizard';
17
+ public static $fields = array();
18
+
19
+ /**
20
+ * Settings for options.
21
+ *
22
+ * @var array $settings The settings for each option.
23
+ */
24
+ public static $settings = array();
25
+
26
+ /**
27
+ * The key for field.
28
+ *
29
+ * @var string $key The key for field.
30
+ */
31
+ private $key;
32
+
33
+ /**
34
+ * Each section.
35
+ *
36
+ * @var array $sections The sections where fields will be stored/showed.
37
+ */
38
+ private $sections;
39
+
40
+ /**
41
+ * Page options.
42
+ *
43
+ * @var array $page_option_group
44
+ */
45
+ private $page_option_group;
46
+
47
+ /**
48
+ * Contains all the fields for the option. Supported field properties:
49
+ *
50
+ * Id - String. COMING SOON (to avoid using the key as the identifier)
51
+ * visible - Boolean. Use this to hide the entire row of the rendered field in the UI. Defaults to true.
52
+ * disable - Boolean|Array. Use to place the `disabled` attribute on the field. For fields with multiple options (<select>) an Array containing keys of all disabled `select_options` can be defined. Defaults to false.
53
+ * serialized_with_group - String|false/null. Controls whether a particular field of a setting is serialized with the group or saved as its own independant option. Defaults to true.
54
+ * label - String|null. The label for the field.
55
+ * description - String. The description for the field, rendered as additional information below the field.
56
+ * render - String|Function. How the field should be rendered as. Non-strings are assumed to be a function for rendering the field (third parameter of `add_settings_field()`). If a String, it is meant to be any of the standard <form> input types (checkbox, radio, textarea, hidden, select, textarea, password etc.') and the rendering will be handled by self::universal_render_handler(); if not defined, "text" is assumed
57
+ * select_options - Array. Defines the <options> for a checkbox (key of this Array is used as its `value` attribute). Only used if the `render` is "select".
58
+ * radio_options - Array. TO BE IMPLEMENTED (should mimic the "select_options" functionality)
59
+ * placeholder - String. Used as the `placeholder` attribute for the <form> field.
60
+ * default - String|null. Used as a default value for the field when there is no value yet saved in the db.
61
+ *
62
+ * @var array $fields The fields for option and its properties.
63
+ * @since 0.4
64
+ */
65
+ private $field_definitions;
66
+
67
+ /**
68
+ * If $option_group is not specified, the Option will be registered with a
69
+ * "default" page group (the first argument of `register_setting()` and called
70
+ * by `settings_fields()`).
71
+ *
72
+ * @param string $option_key The ID for the field.
73
+ * @param array $sections The sections for each field.
74
+ * @param array $option_fields All the fields.
75
+ * @param array $option_group Optional, if not informed will use self::PAGE_OPTION_GROUP.
76
+ * @since 0.4
77
+ */
78
+ public function __construct( $option_key, $sections, $option_fields, $option_group = null ) {
79
+ $this->key = $option_key;
80
+ $this->sections = $sections;
81
+ $this->field_definitions = $option_fields;
82
+ $this->page_option_group = null === $option_group
83
+ ? self::PAGE_OPTION_GROUP
84
+ : $option_group;
85
+
86
+ $this->init();
87
+ }
88
+
89
+ /**
90
+ * Option initiator.
91
+ *
92
+ * @since 0.4
93
+ */
94
+ private function init() {
95
+ $saved_options = self::get_option_decoded( $this->key );
96
+
97
+ foreach ( $this->field_definitions as $field_key => $field ) {
98
+ self::$settings[ $field_key ] = isset( $saved_options[ $field_key ] )
99
+ ? $saved_options[ $field_key ]
100
+ : $field['default'];
101
+ }
102
+
103
+ $this->wp_bootstrap_register_option();
104
+ $this->wp_bootstrap_create_sections();
105
+ $this->wp_bootstrap_add_fields_to_section();
106
+ }
107
+
108
+ /**
109
+ * Decodes the option.
110
+ *
111
+ * @param string $option_key to be returned.
112
+ * @return array from a json decoded content.
113
+ * @since 0.4
114
+ */
115
+ public static function get_option_decoded( $option_key = null ) {
116
+ if ( ! isset( $option_key ) ) {
117
+ // Late Static Binding to use the const OPTION_KEY from the child class which called this function.
118
+ $option_key = static::OPTION_KEY;
119
+ }
120
+
121
+ $raw_data = get_option( $option_key );
122
+
123
+ // Hack which creates an empty setting if it doesn't yet exist.
124
+ // Temporary solution to an unknown oddity which is double-escaping the JSON
125
+ // data as a string when attempting to access a setting that doesn't exist.
126
+ if ( false === $raw_data ) {
127
+ add_option( $option_key );
128
+ $raw_data = get_option( $option_key );
129
+ }
130
+
131
+ return json_decode( $raw_data, true );
132
+ }
133
+
134
+ /**
135
+ * Obtains the compat related to $action_tag.
136
+ *
137
+ * @param string $action_tag The tag registered compat will be retrieved.
138
+ * @since 0.4
139
+ */
140
+ public static function get_registered_compat( $action_tag ) {
141
+ $registered_compat_integrations = array();
142
+
143
+ do_action_ref_array(
144
+ $action_tag,
145
+ array( &$registered_compat_integrations )
146
+ );
147
+ return $registered_compat_integrations;
148
+ }
149
+
150
+ /**
151
+ * Registers the sanitization and encoding handler.
152
+ *
153
+ * @since 0.4
154
+ */
155
+ private function wp_bootstrap_register_option() {
156
+ register_setting(
157
+ $this->page_option_group,
158
+ $this->key,
159
+ array( $this, 'universal_sanitize_and_encode_handler' )
160
+ );
161
+ }
162
+
163
+ /**
164
+ * Create title and description sections.
165
+ *
166
+ * @since 0.4
167
+ */
168
+ private function wp_bootstrap_create_sections() {
169
+ $title = isset( $this->sections['title'] )
170
+ ? $this->sections['title']
171
+ : '';
172
+
173
+ $description = isset( $this->sections['description'] )
174
+ ? wp_kses(
175
+ $this->sections['description'],
176
+ array(
177
+ 'a' => array(
178
+ 'href' => array(),
179
+ 'target' => array(),
180
+ ),
181
+ 'em' => array(),
182
+ 'p' => array(),
183
+ 'strong' => array(),
184
+ )
185
+ )
186
+ : '';
187
+
188
+ add_settings_section(
189
+ $this->key,
190
+ esc_html( $title ),
191
+ function () use ( $description ) {
192
+ echo wp_kses_post( $description );
193
+ },
194
+ $this->key
195
+ );
196
+ }
197
+
198
+ /**
199
+ * Add fields to defined section.
200
+ *
201
+ * @since 0.4
202
+ */
203
+ private function wp_bootstrap_add_fields_to_section() {
204
+ foreach ( $this->field_definitions as $field_key => $field ) {
205
+ $standalone_id = $this->key . '-' . $field_key;
206
+
207
+ // Default values of arguments for renderer.
208
+ $renderer_args = array(
209
+ // The "label_for" arg causes WordPress to wrap the label of the field with a <label> tag.
210
+ 'label_for' => $standalone_id,
211
+ 'serialized_with_group' => $this->key,
212
+ 'render' => 'text',
213
+ 'value' => self::$settings[ $field_key ],
214
+ );
215
+
216
+ // Override default arguments for renderer.
217
+ foreach ( $field as $key => $val ) {
218
+ $renderer_args[ $key ] = $val;
219
+
220
+ // The WordPress do_settings_fields() will add a `class` attribute to
221
+ // the <tr> tag of the rendered output with value of anything defined in
222
+ // a "class" key of the args for the renderer.
223
+ // We force it to include a value of "hidden" since this class is
224
+ // defined in WordPress's global CSS with `display: none;`.
225
+ if ( 'visible' === $key && false === $val ) {
226
+ $renderer_args['class'] = ( ! empty( $renderer_args['class'] )
227
+ ? $renderer_args['class'] . ' '
228
+ : '') . 'hidden';
229
+ }
230
+ }
231
+
232
+ $renderer_handle = 'string' === gettype( $renderer_args['render'] )
233
+ ? array( 'Instant_Articles_Option', 'universal_render_handler' )
234
+ : $renderer_args['render'];
235
+
236
+ add_settings_field(
237
+ $standalone_id,
238
+ $field['label'],
239
+ $renderer_handle,
240
+ $this->key,
241
+ $this->key,
242
+ $renderer_args
243
+ );
244
+ }
245
+ }
246
+
247
+ /**
248
+ * Function to render all fields with its labels and inputs.
249
+ *
250
+ * @param array $args array map with its field names and values.
251
+ * @since 0.4
252
+ */
253
+ public static function universal_render_handler( $args = null ) {
254
+ $id = isset( $args['label_for'] )
255
+ ? $args['label_for']
256
+ : '';
257
+
258
+ $type = isset( $args['render'] ) && gettype( 'string' === $args['render'] )
259
+ ? $args['render']
260
+ : 'text';
261
+
262
+ if ( ! empty( $args['value'] ) ) {
263
+ $option_value = $args['value'];
264
+ } elseif ( ! empty( $args['default'] ) ) {
265
+ $option_value = $args['default'];
266
+ } else {
267
+ $option_value = '';
268
+ }
269
+
270
+ // Determines correct values based on whether the settings option
271
+ // is intended to be serialized as a field of a parent option name.
272
+ if ( gettype( 'string' === $args['serialized_with_group'] ) ) {
273
+ $group = $args['serialized_with_group'];
274
+ $group_key = substr( $id, strlen( $group . '-' ) );
275
+ $name = $group . '[' . $group_key . ']';
276
+ } else {
277
+ $name = $id;
278
+ }
279
+
280
+ $placeholder = isset( $args['placeholder'] )
281
+ ? $args['placeholder']
282
+ : '';
283
+
284
+ $attr_disabled = isset( $args['disable'] ) && true === $args['disable']
285
+ ? disabled()
286
+ : '';
287
+
288
+ $field_description = isset( $args['description'] )
289
+ ? wp_kses(
290
+ $args['description'],
291
+ array(
292
+ 'a' => array(
293
+ 'href' => array(),
294
+ 'target' => array(),
295
+ ),
296
+ 'em' => array(),
297
+ 'strong' => array(),
298
+ )
299
+ )
300
+ : '';
301
+
302
+ $field_checkbox_label = isset( $args['checkbox_label'] )
303
+ ? $args['checkbox_label']
304
+ : '';
305
+
306
+ switch ( $type ) {
307
+ case 'hidden':
308
+ ?>
309
+ <input
310
+ type="hidden"
311
+ name="<?php echo esc_attr( $name ) ?>"
312
+ id="<?php echo esc_attr( $id ) ?>"
313
+ value="<?php echo esc_attr( $option_value ); ?>"
314
+ />
315
+ <?php if ( $field_description ) : ?>
316
+ <p class="description">
317
+ <?php echo wp_kses_post( $field_description ); ?>
318
+ </p>
319
+ <?php endif; ?>
320
+ <?php
321
+ break;
322
+
323
+ case 'checkbox':
324
+ $attr_checked = checked( 1, $option_value, false );
325
+ ?>
326
+ <label>
327
+ <input
328
+ type="checkbox"
329
+ value="1"
330
+ name="<?php echo esc_attr( $name ) ?>"
331
+ id="<?php echo esc_attr( $id ) ?>"
332
+ <?php echo esc_attr( $attr_checked ); ?>
333
+ <?php echo esc_attr( $attr_disabled ); ?>
334
+ />
335
+ <?php echo esc_html( $field_checkbox_label ); ?>
336
+ </label>
337
+ <?php if ( $field_description ) : ?>
338
+ <p class="description">
339
+ <?php echo wp_kses_post( $field_description ); ?>
340
+ </p>
341
+ <?php endif; ?>
342
+ <?php
343
+ break;
344
+
345
+ case 'select':
346
+ ?>
347
+ <select
348
+ id="<?php echo esc_attr( $id ) ?>"
349
+ name="<?php echo esc_attr( $name ) ?>"
350
+ <?php echo esc_html( $attr_disabled ) ?>
351
+ >
352
+ <?php foreach ( $args['select_options'] as $option_key => $option_name ) : ?>
353
+ <option
354
+ value="<?php echo esc_attr( $option_key ) ?>"
355
+ <?php echo selected( $option_key, $option_value ) ?>
356
+ <?php echo isset( $args['disable'] )
357
+ && gettype( $args['disable'] ) === 'array'
358
+ && in_array( $option_key, $args['disable'], true )
359
+ ? disabled()
360
+ : '';
361
+ ?>
362
+ >
363
+ <?php echo esc_html( $option_name ); ?>
364
+ </option>
365
+ <?php endforeach; ?>
366
+ </select>
367
+ <?php if ( $field_description ) : ?>
368
+ <p class="description">
369
+ <?php echo wp_kses_post( $field_description ); ?>
370
+ </p>
371
+ <?php endif; ?>
372
+ <?php
373
+ break;
374
+
375
+ case 'textarea':
376
+ ?>
377
+ <textarea
378
+ name="<?php echo esc_attr( $name ) ?>"
379
+ id="<?php echo esc_attr( $id ) ?>"
380
+ <?php if ( $placeholder ) : ?>
381
+ placeholder="<?php echo esc_attr( $placeholder ); ?>"
382
+ <?php endif; ?>
383
+ <?php echo esc_attr( $attr_disabled ); ?>
384
+ class="large-text code"
385
+ rows="8"
386
+ ><?php echo array_key_exists( 'double_encode', $args ) && $args[ 'double_encode' ] ? htmlspecialchars( $option_value ) : esc_html( $option_value ); ?></textarea>
387
+ <?php if ( $field_description ) : ?>
388
+ <p class="description">
389
+ <?php echo wp_kses_post( $field_description); ?>
390
+ </p>
391
+ <?php endif; ?>
392
+ <?php
393
+ break;
394
+
395
+ case 'text':
396
+ case 'password':
397
+ default:
398
+ ?>
399
+ <input
400
+ type="<?php echo esc_attr( $type ) ?>"
401
+ name="<?php echo esc_attr( $name ) ?>"
402
+ id="<?php echo esc_attr( $id ) ?>"
403
+ <?php if ( $placeholder ) : ?>
404
+ placeholder="<?php echo esc_attr( $placeholder ) ?>"
405
+ <?php endif; ?>
406
+ <?php echo esc_attr( $attr_disabled ) ?>
407
+ value="<?php echo esc_attr( $option_value ) ?>"
408
+ class="regular-text"
409
+ />
410
+ <?php if ( $field_description ) : ?>
411
+ <p class="description">
412
+ <?php echo wp_kses_post( $field_description ); ?>
413
+ </p>
414
+ <?php endif; ?>
415
+ <?php
416
+ break;
417
+ }
418
+ }
419
+
420
+ /**
421
+ * Intercepts the form data for an individual option on its way to the server.
422
+ * Receives one argument containing the payload data and passes it along to
423
+ * the child's own sanitation method. Returns an encoded payload for it
424
+ * to continue its way to the server.
425
+ *
426
+ * @param array $payload map of fields and their values.
427
+ * @return string encoded json with fields.
428
+ * @since 0.5
429
+ */
430
+ public function universal_sanitize_and_encode_handler( $payload ) {
431
+ // Handle empty payload.
432
+ if ( ! is_array( $payload ) ) {
433
+ $payload = array();
434
+ }
435
+
436
+ // Remove any fields which could have been injected into the payload client-side.
437
+ $allowed_payload = array_intersect_key( $payload, static::$fields );
438
+ $allowed_payload = $payload;
439
+
440
+ // Pass the value along to the Child class's method to perform sanitation on its fields.
441
+ $sanitized_payload = static::sanitize_option_fields( $allowed_payload );
442
+
443
+ // Encode the payload into JSON before it's sent off to be saved.
444
+ return wp_json_encode( $sanitized_payload );
445
+ }
446
+
447
+ /**
448
+ * "Pass through" function. This should be overridden in child classes which
449
+ * are responsible for sanitizing its own $field_values.
450
+ *
451
+ * @param array $field_values array of values for fields.
452
+ * @since 0.5
453
+ */
454
+ public function sanitize_option_fields( $field_values ) {
455
+ return $field_values;
456
+ }
457
+
458
+ /**
459
+ * Updates options from decoded map
460
+ *
461
+ * @param string $option_key to be returned.
462
+ * @return array from a json decoded content.
463
+ * @since 0.4
464
+ */
465
+ public static function update_option( $option = array(), $option_key = null ) {
466
+ if ( ! isset( $option_key ) ) {
467
+ // Late Static Binding to use the const OPTION_KEY from the child class which called this function.
468
+ $option_key = static::OPTION_KEY;
469
+ }
470
+
471
+ wp_cache_delete ( 'alloptions', 'options' );
472
+ update_option( $option_key, $option );
473
+ }
474
+ /**
475
+ * Updates options from decoded map
476
+ *
477
+ * @param string $option_key to be returned.
478
+ * @return array from a json decoded content.
479
+ * @since 0.4
480
+ */
481
+ public static function delete_option( $option_key = null ) {
482
+ if ( ! isset( $option_key ) ) {
483
+ // Late Static Binding to use the const OPTION_KEY from the child class which called this function.
484
+ $option_key = static::OPTION_KEY;
485
+ }
486
+
487
+ wp_cache_delete ( 'alloptions', 'options' );
488
+ delete_option( $option_key );
489
+ }
490
+ }
wizard/class-instant-articles-wizard-fb-helper.php ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Facebook Instant Articles for WP.
4
+ * This source code is licensed under the license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @package default
8
+ */
9
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-option-fb-app.php' );
10
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-option-fb-page.php' );
11
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-wizard-state.php' );
12
+
13
+ use Facebook\PersistentData\PersistentDataInterface;
14
+
15
+ /**
16
+ * Class responsible for functionality and rendering of the settings
17
+ *
18
+ * @since 0.4
19
+ */
20
+ class Instant_Articles_Wizard_FB_Helper implements PersistentDataInterface {
21
+
22
+ /**
23
+ * @var string Prefix to use for session options.
24
+ */
25
+ protected $session_prefix = 'instant_articles_fbrlh_';
26
+
27
+ /**
28
+ * @inheritdoc
29
+ */
30
+ public function get( $key ) {
31
+
32
+ return get_option( $this->session_prefix . $key );
33
+ }
34
+
35
+ /**
36
+ * @inheritdoc
37
+ */
38
+ public function set( $key, $value ) {
39
+
40
+ update_option( $this->session_prefix . $key, $value );
41
+ }
42
+
43
+
44
+ /**
45
+ * Facebook Permissions.
46
+ *
47
+ * @var array $fb_app_permissions The permissions asked for FB user to list pages he manages.
48
+ */
49
+ public static $fb_app_permissions = array( 'pages_manage_instant_articles', 'pages_show_list' );
50
+
51
+ /**
52
+ * SDK instance.
53
+ *
54
+ * @var Facebook $fb_sdk the instance reference to FB sdk
55
+ */
56
+ public $fb_sdk;
57
+
58
+ /**
59
+ * Settings structure.
60
+ *
61
+ * @var array $fb_app_settings The map data structure to store settings.
62
+ */
63
+ protected $fb_app_settings;
64
+
65
+ /**
66
+ * Constructor for Settings page.
67
+ *
68
+ * @since 0.4
69
+ */
70
+ public function __construct() {
71
+ $this->fb_app_settings = Instant_Articles_Option_FB_App::get_option_decoded();
72
+
73
+ if ( isset( $this->fb_app_settings['app_id'] ) && isset( $this->fb_app_settings['app_secret'] ) ) {
74
+ $app_id = $this->fb_app_settings['app_id'];
75
+ $app_secret = $this->fb_app_settings['app_secret'];
76
+ }
77
+
78
+ if ( ! empty( $app_id ) && ! empty( $app_secret ) ) {
79
+ $this->fb_sdk = new Facebook\Facebook(array(
80
+ 'app_id' => $app_id,
81
+ 'app_secret' => $app_secret,
82
+ 'default_graph_version' => 'v2.6',
83
+ 'persistent_data_handler' => $this
84
+ ));
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Gets the login url.
90
+ *
91
+ * @since 0.4
92
+ */
93
+ public function get_login_url() {
94
+ if ( isset( $this->fb_sdk ) ) {
95
+ $helper = $this->fb_sdk->getRedirectLoginHelper();
96
+ $redirect_url = Instant_Articles_Wizard::get_url();
97
+
98
+ $login_url = $helper->getLoginUrl(
99
+ $redirect_url,
100
+ self::$fb_app_permissions
101
+ );
102
+
103
+ return $login_url;
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Retrieves granted permissions.
109
+ *
110
+ * @param string $access_token The user access token.
111
+ * @since 0.5
112
+ */
113
+ public function get_fb_permissions( $access_token ) {
114
+
115
+ $permissions = array();
116
+
117
+ if ( isset( $this->fb_sdk ) && $access_token ) {
118
+
119
+ try {
120
+ $permissions_query = $this->fb_sdk->get( '/me/permissions', $access_token );
121
+ $decoded_permissions = $permissions_query->getDecodedBody();
122
+ if ( isset( $decoded_permissions['data'] ) ) {
123
+ foreach ( $decoded_permissions['data'] as $permission ) {
124
+ if ( 'granted' === $permission['status'] ) {
125
+ $permissions[ $permission['permission'] ] = 'granted';
126
+ }
127
+ }
128
+ }
129
+ } catch (Facebook\Exceptions\FacebookResponseException $e) {
130
+ // When Graph returns an error.
131
+ Logger::getLogger( 'instantarticles-wp-plugin' )->error(
132
+ 'Graph returned an error: '.$e->getMessage(),
133
+ $e->getTraceAsString()
134
+ );
135
+
136
+ } catch (Facebook\Exceptions\FacebookSDKException $e) {
137
+ // When validation fails or other local issues.
138
+ Logger::getLogger( 'instantarticles-wp-plugin' )->error(
139
+ 'Facebook SDK returned an error: '.$e->getMessage(),
140
+ $e->getTraceAsString()
141
+ );
142
+
143
+ }
144
+ }
145
+
146
+ if ( isset( $permissions ) ) {
147
+ // Logged in.
148
+ return $permissions;
149
+ }
150
+ }
151
+
152
+ /**
153
+ * Retrieves Facebook access token.
154
+ *
155
+ * @since 0.4
156
+ */
157
+ public function get_fb_access_token() {
158
+ $access_token = null;
159
+
160
+ if ( isset( $this->fb_sdk ) ) {
161
+ try {
162
+ $helper = $this->fb_sdk->getRedirectLoginHelper();
163
+ $access_token = $helper->getAccessToken();
164
+ } catch (Facebook\Exceptions\FacebookResponseException $e) {
165
+ // When Graph returns an error.
166
+ Logger::getLogger( 'instantarticles-wp-plugin' )->error(
167
+ 'Graph returned an error: '.$e->getMessage(),
168
+ $e->getTraceAsString()
169
+ );
170
+
171
+ } catch (Facebook\Exceptions\FacebookSDKException $e) {
172
+ // When validation fails or other local issues.
173
+ Logger::getLogger( 'instantarticles-wp-plugin' )->error(
174
+ 'Facebook SDK returned an error: '.$e->getMessage(),
175
+ $e->getTraceAsString()
176
+ );
177
+ }
178
+ }
179
+
180
+ if ( null !== $access_token ) {
181
+ // Logged in.
182
+ return $access_token;
183
+ }
184
+ }
185
+
186
+ public function get_pages() {
187
+ $helper = new Facebook\InstantArticles\Client\Helper(
188
+ $this->fb_sdk
189
+ );
190
+
191
+ $fb_app_settings = Instant_Articles_Option_FB_App::get_option_decoded();
192
+
193
+ $page_nodes = $helper->getPagesAndTokens(
194
+ new Facebook\Authentication\AccessToken( $fb_app_settings[ 'user_access_token' ] )
195
+ )->all();
196
+
197
+ $pages = array();
198
+
199
+ // Map GraphNode objects to simple value objects that are smaller when serialized.
200
+ foreach ( $page_nodes as $page_node ) {
201
+ $pages[ $page_node->getField( 'id' ) ] = array(
202
+ 'page_id' => $page_node->getField( 'id' ),
203
+ 'page_name' => $page_node->getField( 'name' ),
204
+ 'page_picture' => $page_node->getField( 'picture' )->getField( 'url' ),
205
+ 'page_access_token' => $page_node->getField( 'access_token' ),
206
+ 'supports_instant_articles' => $page_node->getField( 'supports_instant_articles' ),
207
+ );
208
+ }
209
+
210
+ return $pages;
211
+ }
212
+ }
wizard/class-instant-articles-wizard-review-submission.php ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Facebook Instant Articles for WP.
4
+ * This source code is licensed under the license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @package default
8
+ */
9
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-option-fb-app.php' );
10
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-option-fb-page.php' );
11
+
12
+ use Facebook\InstantArticles\Client\Client;
13
+
14
+ /**
15
+ * Class responsible for functionality and rendering of the review settings
16
+ *
17
+ * @since 3.1
18
+ */
19
+ class Instant_Articles_Wizard_Review_Submission {
20
+
21
+ const MIN_ARTICLES = 5;
22
+
23
+ const STATUS_REJECTED = 'REJECTED';
24
+ const STATUS_APPROVED = 'APPROVED';
25
+ const STATUS_PENDING = 'PENDING';
26
+ const STATUS_NOT_SUBMITTED = 'NOT_SUBMITTED';
27
+
28
+ public static function getArticlesForReview() {
29
+ $post_types = apply_filters( 'instant_articles_post_types', array( 'post' ) );
30
+
31
+ // Cap the number of articles returned to
32
+ // 100 because of performance concerns.
33
+ return wp_get_recent_posts(
34
+ array(
35
+ 'numberposts' => min( self::MIN_ARTICLES, 100 ),
36
+ 'post_type' => $post_types
37
+ ),
38
+ 'OBJECT'
39
+ );
40
+ }
41
+
42
+ public static function getPageID() {
43
+ if ( ! static::isPageSet() ) {
44
+ return null;
45
+ }
46
+ $fb_page_settings = Instant_Articles_Option_FB_Page::get_option_decoded();
47
+ return $fb_page_settings['page_id'];
48
+ }
49
+
50
+ public static function isPageSet() {
51
+ $fb_app_settings = Instant_Articles_Option_FB_App::get_option_decoded();
52
+ $fb_page_settings = Instant_Articles_Option_FB_Page::get_option_decoded();
53
+
54
+ if ( isset( $fb_app_settings['app_id'] )
55
+ && isset( $fb_app_settings['app_secret'] )
56
+ && isset( $fb_page_settings['page_access_token'] )
57
+ && isset( $fb_page_settings['page_id'] ) ) {
58
+ return true;
59
+ }
60
+
61
+ return false;
62
+ }
63
+
64
+ public static function getClient() {
65
+ if ( ! static::isPageSet() ) {
66
+ return null;
67
+ }
68
+
69
+ $fb_app_settings = Instant_Articles_Option_FB_App::get_option_decoded();
70
+ $fb_page_settings = Instant_Articles_Option_FB_Page::get_option_decoded();
71
+
72
+ $client = Client::create(
73
+ $fb_app_settings['app_id'],
74
+ $fb_app_settings['app_secret'],
75
+ $fb_page_settings['page_access_token'],
76
+ $fb_page_settings['page_id'],
77
+ false
78
+ );
79
+
80
+ return $client;
81
+ }
82
+
83
+ public static function getReviewSubmissionStatus() {
84
+ if ( ! static::isPageSet() ) {
85
+ return null;
86
+ }
87
+ return static::getClient()->getReviewSubmissionStatus();
88
+ }
89
+
90
+ public static function getArticlesURLs() {
91
+ if ( ! static::isPageSet() ) {
92
+ return null;
93
+ }
94
+ return static::getClient()->getArticlesURLs(static::MIN_ARTICLES);
95
+ }
96
+ }
wizard/class-instant-articles-wizard-state.php ADDED
@@ -0,0 +1,352 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Facebook Instant Articles for WP.
4
+ * This source code is licensed under the license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @package default
8
+ */
9
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-option-ads.php' );
10
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-option-analytics.php' );
11
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-option-fb-app.php' );
12
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-option-fb-page.php' );
13
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-option-publishing.php' );
14
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-option-styles.php' );
15
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-invalid-wizard-transition-exception.php' );
16
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-wizard-fb-helper.php' );
17
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-wizard-review-submission.php' );
18
+
19
+ use Facebook\InstantArticles\Client\Client;
20
+ use Facebook\InstantArticles\Client\ClientException;
21
+
22
+ /**
23
+ * Set-up wizard state machine.
24
+ *
25
+ * @since 3.1
26
+ * @see ./state_machine.txt
27
+ */
28
+ class Instant_Articles_Wizard_State {
29
+
30
+ // STATES
31
+ const STATE_OVERVIEW = 'STATE_OVERVIEW';
32
+ const STATE_APP_SETUP = 'STATE_APP_SETUP';
33
+ const STATE_PAGE_SELECTION = 'STATE_PAGE_SELECTION';
34
+ const STATE_STYLE_SELECTION = 'STATE_STYLE_SELECTION';
35
+ const STATE_REVIEW_SUBMISSION = 'STATE_REVIEW_SUBMISSION';
36
+
37
+ // TRANSITIONS
38
+ const TRANSITION_START_WIZARD = 'TRANSITION_START_WIZARD';
39
+ const TRANSITION_SET_UP_APP = 'TRANSITION_SET_UP_APP';
40
+ const TRANSITION_SELECT_PAGE = 'TRANSITION_SELECT_PAGE';
41
+ const TRANSITION_SELECT_STYLE = 'TRANSITION_SELECT_STYLE';
42
+ const TRANSITION_EDIT_STYLE = 'TRANSITION_EDIT_STYLE';
43
+ const TRANSITION_EDIT_PAGE = 'TRANSITION_EDIT_PAGE';
44
+ const TRANSITION_EDIT_APP = 'TRANSITION_EDIT_APP';
45
+
46
+ // WIZARD TIMELINE
47
+ const TIMELINE_PAST = 'TIMELINE_PAST';
48
+ const TIMELINE_CURRENT = 'TIMELINE_CURRENT';
49
+ const TIMELINE_FUTURE = 'TIMELINE_FUTURE';
50
+
51
+
52
+ /**
53
+ * Transition vectors, format:
54
+ *
55
+ * array(
56
+ * ORIGINAL_STATE => array(
57
+ * NEW_STATE => TRANSITION_NAME,
58
+ * ...
59
+ * ),
60
+ * ...
61
+ * )
62
+ *
63
+ */
64
+ public static $transition_vectors = array(
65
+ self::STATE_OVERVIEW => array (
66
+ self::STATE_APP_SETUP => self::TRANSITION_START_WIZARD
67
+ ),
68
+ self::STATE_APP_SETUP => array (
69
+ self::STATE_PAGE_SELECTION => self::TRANSITION_SET_UP_APP
70
+ ),
71
+ self::STATE_PAGE_SELECTION => array (
72
+ self::STATE_STYLE_SELECTION => self::TRANSITION_SELECT_PAGE,
73
+ self::STATE_APP_SETUP => self::TRANSITION_EDIT_APP
74
+ ),
75
+ self::STATE_STYLE_SELECTION => array (
76
+ self::STATE_REVIEW_SUBMISSION => self::TRANSITION_SELECT_STYLE,
77
+ self::STATE_PAGE_SELECTION => self::TRANSITION_EDIT_PAGE,
78
+ self::STATE_APP_SETUP => self::TRANSITION_EDIT_APP
79
+ ),
80
+ self::STATE_REVIEW_SUBMISSION => array (
81
+ self::STATE_STYLE_SELECTION => self::TRANSITION_EDIT_STYLE,
82
+ self::STATE_PAGE_SELECTION => self::TRANSITION_EDIT_PAGE,
83
+ self::STATE_APP_SETUP => self::TRANSITION_EDIT_APP
84
+ ),
85
+ );
86
+
87
+ /**
88
+ * Order of states on the wizard
89
+ */
90
+ public static $timeline = array(
91
+ self::STATE_OVERVIEW => 0,
92
+ self::STATE_APP_SETUP => 1,
93
+ self::STATE_PAGE_SELECTION => 2,
94
+ self::STATE_STYLE_SELECTION => 3,
95
+ self::STATE_REVIEW_SUBMISSION => 4
96
+ );
97
+
98
+ /**
99
+ * Gets the timeline position for a given state.
100
+ *
101
+ * @param string $state The state constant
102
+ * @return string The timeline constant (PAST, CURRENT or FUTURE)
103
+ */
104
+ public static function get_timeline_position( $state ) {
105
+ $current_state = self::get_current_state();
106
+
107
+ if ( self::$timeline[ $current_state ] > self::$timeline[ $state ] ) {
108
+ return self::TIMELINE_PAST;
109
+ } elseif ( $state === $current_state ) {
110
+ if (
111
+ $current_state === self::STATE_REVIEW_SUBMISSION &&
112
+ Instant_Articles_Wizard_Review_Submission::getReviewSubmissionStatus() === Instant_Articles_Wizard_Review_Submission::STATUS_APPROVED
113
+ ) {
114
+ return self::TIMELINE_PAST;
115
+ }
116
+ return self::TIMELINE_CURRENT;
117
+ } else {
118
+ return self::TIMELINE_FUTURE;
119
+ }
120
+ }
121
+
122
+ /**
123
+ * Retrieves the current state of the wizard
124
+ *
125
+ * @return string The state constant for the current state
126
+ */
127
+ public static function get_current_state() {
128
+
129
+ $option = get_option( 'instant-articles-current-state', null );
130
+ if ( in_array( $option, array_keys( self::$transition_vectors ) ) ) {
131
+ return $option;
132
+ }
133
+
134
+ // Legacy compatibility - calculate state from existing setup step
135
+ $fb_page_settings = Instant_Articles_Option_FB_Page::get_option_decoded();
136
+ $fb_app_settings = Instant_Articles_Option_FB_App::get_option_decoded();
137
+
138
+ $app_set_up = ! empty( $fb_app_settings['app_id'] ) && ! empty( $fb_app_settings['app_secret'] );
139
+ $user_logged_in = ! empty( $fb_app_settings['user_access_token'] );
140
+ $page_selected = ! empty( $fb_page_settings['page_id'] ) && ! empty( $fb_page_settings['page_name'] );
141
+ $review_submitted = Instant_Articles_Wizard_Review_Submission::getReviewSubmissionStatus() === Instant_Articles_Wizard_Review_Submission::STATUS_NOT_SUBMITTED;
142
+
143
+ if ( ! $app_set_up ) {
144
+ return self::STATE_OVERVIEW;
145
+ } elseif ( ! $user_logged_in && ! $page_selected ) {
146
+ return self::STATE_APP_SETUP;
147
+ } elseif ( ! $page_selected ) {
148
+ return self::STATE_PAGE_SELECTION;
149
+ } elseif ( ! $review_submitted ) {
150
+ return self::STATE_REVIEW_SUBMISSION;
151
+ } else {
152
+ return self::STATE_STYLE_SELECTION;
153
+ }
154
+
155
+ }
156
+
157
+ /**
158
+ * Calculates the transition name for 2 given states
159
+ *
160
+ * @param string $from Original state constant
161
+ * @param string $to New state constant
162
+ * @return string The transition constant or null for invalid transitions
163
+ */
164
+ public static function get_transition( $from, $to ) {
165
+ if ( isset ( self::$transition_vectors[ $from ][ $to ] ) ) {
166
+ return self::$transition_vectors[ $from ][ $to ];
167
+ }
168
+ return null;
169
+ }
170
+
171
+
172
+ /**
173
+ * Executes the transition from the current state to a new state
174
+ *
175
+ * @param string $new_state Constant for the final state of the transition
176
+ * @param mixed[] $params Parameters for the transition
177
+ * @throws Instant_Articles_Invalid_Wizard_Transition_Exception if an invalid transition is attempted
178
+ */
179
+ public static function do_transition( $new_state, $params = array() ) {
180
+ $current_state = self::get_current_state();
181
+ $transition = self::get_transition( $current_state, $new_state );
182
+
183
+ if ( $transition === null ) {
184
+ throw new Instant_Articles_Invalid_Wizard_Transition_Exception( $current_state, $new_state );
185
+ }
186
+
187
+ switch ( $transition ) {
188
+ case self::TRANSITION_START_WIZARD:
189
+ return self::transition_start_wizard();
190
+
191
+ case self::TRANSITION_SET_UP_APP:
192
+ return self::transition_set_up_app( $params[ 'app_id' ], $params[ 'app_secret'], $params[ 'user_access_token' ] );
193
+
194
+ case self::TRANSITION_SELECT_PAGE:
195
+ // TODO: validate params
196
+ return self::transition_select_page( $params[ 'page_id'] );
197
+ case self::TRANSITION_SELECT_STYLE:
198
+ // TODO: validate params
199
+ return self::transition_select_style();
200
+ case self::TRANSITION_EDIT_APP:
201
+ return self::transition_edit_app();
202
+ case self::TRANSITION_EDIT_PAGE:
203
+ return self::transition_edit_page();
204
+ case self::TRANSITION_EDIT_STYLE:
205
+ return self::transition_edit_style();
206
+ }
207
+ }
208
+
209
+ /**
210
+ * Claims the URL for this site.
211
+ */
212
+ public static function claim_url() {
213
+ $fb_app_settings = Instant_Articles_Option_FB_App::get_option_decoded();
214
+ $fb_page_settings = Instant_Articles_Option_FB_Page::get_option_decoded();
215
+
216
+ if ( ! $fb_app_settings[ 'app_id' ] ) {
217
+ throw new InvalidArgumentException( 'Missing app_id for claiming the URL.' );
218
+ }
219
+ if ( ! $fb_app_settings[ 'app_secret' ] ) {
220
+ throw new InvalidArgumentException( 'Missing app_secret for claiming the URL.' );
221
+ }
222
+ if ( ! $fb_page_settings[ 'page_access_token' ] ) {
223
+ throw new InvalidArgumentException( 'Missing page_access_token for claiming the URL.' );
224
+ }
225
+ if ( ! $fb_page_settings[ 'page_id' ] ) {
226
+ throw new InvalidArgumentException( 'Missing page_id for claiming the URL.' );
227
+ }
228
+
229
+ $client = Client::create(
230
+ $fb_app_settings[ 'app_id' ],
231
+ $fb_app_settings[ 'app_secret' ],
232
+ $fb_page_settings[ 'page_access_token' ],
233
+ $fb_page_settings[ 'page_id' ]
234
+ );
235
+
236
+
237
+ // We need the home URL without the protocol for claiming
238
+ $url = preg_replace( '/^https?:\/\//i', '', esc_url_raw( home_url( '/' ) ) );
239
+
240
+ try {
241
+ $client->claimURL( $url );
242
+ } catch (Exception $e) {
243
+ // Here we override the error message to give an actionable
244
+ // instruction to the user that is specific for WordPress.
245
+ throw new Exception("Could not claim the URL for the selected page, please make sure your site is publicly available.");
246
+ }
247
+ }
248
+
249
+ //---------------------------
250
+ // Transition implementations
251
+ //---------------------------
252
+
253
+ /**
254
+ * Transition for when starting the wizard from overview.
255
+ */
256
+ private static function transition_start_wizard() {
257
+ return update_option( 'instant-articles-current-state', self::STATE_APP_SETUP );
258
+ }
259
+
260
+ /**
261
+ * Transition for when the user inputs the app info and logs in with it's personal account.
262
+ */
263
+ private static function transition_set_up_app( $app_id, $app_secret, $user_access_token ) {
264
+ if ( ! $app_id ) {
265
+ throw new InvalidArgumentException( 'Missing App ID when authenticating the plugin' );
266
+ }
267
+ if ( ! $app_secret ) {
268
+ throw new InvalidArgumentException( 'Missing App Secret when authenticating the plugin' );
269
+ }
270
+ if ( ! $user_access_token ) {
271
+ throw new InvalidArgumentException( 'Missing Access Token when authenticating the plugin' );
272
+ }
273
+
274
+ Instant_Articles_Option_FB_App::update_option( array(
275
+ 'app_id' => $app_id,
276
+ 'app_secret' => $app_secret,
277
+ 'user_access_token' => $user_access_token
278
+ ) );
279
+ return update_option( 'instant-articles-current-state', self::STATE_PAGE_SELECTION );
280
+ }
281
+
282
+ /**
283
+ * Transition for when the user selects the page to connect to.
284
+ *
285
+ * This transition stores page info including the long-lived access token information.
286
+ */
287
+ private static function transition_select_page( $page_id ) {
288
+ if ( ! $page_id ) {
289
+ throw new InvalidArgumentException( 'Missing Page ID when selcting the page' );
290
+ }
291
+
292
+ $fb_helper = new Instant_Articles_Wizard_FB_Helper();
293
+ $pages = $fb_helper->get_pages();
294
+
295
+ if ( ! $pages[ $page_id ] ) {
296
+ throw new InvalidArgumentException( 'Invalid Page ID when selcting the page' );
297
+ }
298
+ if ( ! $pages[ $page_id ][ 'supports_instant_articles'] ) {
299
+ throw new InvalidArgumentException( 'Selected page is not signed up to Instant Articles' );
300
+ }
301
+
302
+ Instant_Articles_Option_FB_Page::update_option( $pages[ $page_id ] );
303
+
304
+ // You should always claim the URL after updating the FB Page option so the fb:pages meta tag is rendered.
305
+ self::claim_url();
306
+
307
+ return update_option( 'instant-articles-current-state', self::STATE_STYLE_SELECTION );
308
+ }
309
+
310
+ /**
311
+ * Transition for when the user confirms he has selected the style for the articles.
312
+ */
313
+ private static function transition_select_style() {
314
+ return update_option( 'instant-articles-current-state', self::STATE_REVIEW_SUBMISSION );
315
+ }
316
+
317
+ /**
318
+ * Transition for when the user decides to change the FB App.
319
+ */
320
+ private static function transition_edit_app() {
321
+ Instant_Articles_Option_FB_App::delete_option();
322
+ Instant_Articles_Option_FB_Page::delete_option();
323
+ return update_option( 'instant-articles-current-state', self::STATE_APP_SETUP );
324
+ }
325
+
326
+ /**
327
+ * Transition for when the user decides to change the selected page.
328
+ */
329
+ private static function transition_edit_page() {
330
+ Instant_Articles_Option_FB_Page::delete_option();
331
+
332
+ // For backwards compatibility, we transition one step back
333
+ // for users of the old versions that selected a page but the
334
+ // plugin didn't save the user access token. In this case
335
+ // we need the user to log in again.
336
+ $fb_app_settings = Instant_Articles_Option_FB_App::get_option_decoded();
337
+ $user_logged_in = ! empty( $fb_app_settings['user_access_token'] );
338
+ if ( ! $user_logged_in ) {
339
+ return update_option( 'instant-articles-current-state', self::STATE_APP_SETUP );
340
+ }
341
+
342
+ return update_option( 'instant-articles-current-state', self::STATE_PAGE_SELECTION );
343
+ }
344
+
345
+ /**
346
+ * Transition for when the user decides to edit the style.
347
+ */
348
+ private static function transition_edit_style() {
349
+ return update_option( 'instant-articles-current-state', self::STATE_STYLE_SELECTION );
350
+ }
351
+
352
+ }
wizard/class-instant-articles-wizard.php ADDED
@@ -0,0 +1,295 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Facebook Instant Articles for WP.
4
+ * This source code is licensed under the license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @package default
8
+ */
9
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-option-ads.php' );
10
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-option-analytics.php' );
11
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-option-fb-app.php' );
12
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-option-fb-page.php' );
13
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-option-publishing.php' );
14
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-option-styles.php' );
15
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-wizard-state.php' );
16
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-wizard-fb-helper.php' );
17
+ require_once( dirname( __FILE__ ) . '/class-instant-articles-wizard-review-submission.php' );
18
+
19
+ use Facebook\InstantArticles\Client\Client;
20
+ use Facebook\InstantArticles\Client\ClientException;
21
+
22
+ /**
23
+ * Controller for Set-up Wizard
24
+ *
25
+ * @since 3.1
26
+ */
27
+ class Instant_Articles_Wizard {
28
+
29
+ public static function init() {
30
+ add_action( 'admin_menu', array( 'Instant_Articles_Wizard', 'menu_items' ) );
31
+
32
+ add_filter( 'plugin_action_links_' . IA_PLUGIN_PATH, array( 'Instant_Articles_Wizard', 'add_settings_link_to_plugin_actions' ) );
33
+
34
+ add_action( 'admin_init', function () {
35
+ new Instant_Articles_Option_FB_App();
36
+ new Instant_Articles_Option_FB_Page();
37
+ new Instant_Articles_Option_Styles();
38
+ new Instant_Articles_Option_Ads();
39
+ new Instant_Articles_Option_Analytics();
40
+ new Instant_Articles_Option_Publishing();
41
+ });
42
+
43
+ add_action(
44
+ 'wp_ajax_instant_articles_wizard_transition',
45
+ array( 'Instant_Articles_Wizard', 'transition' )
46
+ );
47
+
48
+ add_action(
49
+ 'wp_ajax_instant_articles_wizard_save_app',
50
+ array( 'Instant_Articles_Wizard', 'save_app' )
51
+ );
52
+
53
+ add_action(
54
+ 'wp_ajax_instant_articles_wizard_edit_app',
55
+ array( 'Instant_Articles_Wizard', 'edit_app' )
56
+ );
57
+
58
+ add_action(
59
+ 'wp_ajax_instant_articles_wizard_is_page_signed_up',
60
+ array( 'Instant_Articles_Wizard', 'is_page_signed_up' )
61
+ );
62
+
63
+ add_action(
64
+ 'wp_ajax_instant_articles_wizard_submit_for_review',
65
+ array( 'Instant_Articles_Wizard', 'submit_for_review' )
66
+ );
67
+ }
68
+
69
+ public static function add_settings_link_to_plugin_actions( $links ) {
70
+ $link_text = __( 'Settings' );
71
+ $settings_href = self::get_url();
72
+ $settings_link = '<a href="' . esc_url( $settings_href ) . '">' . esc_html( $link_text ) . '</a>';
73
+ array_push( $links, $settings_link );
74
+ return $links;
75
+ }
76
+
77
+ public static function menu_items() {
78
+ add_menu_page(
79
+ 'Instant Articles Setup Wizard',
80
+ 'Instant Articles',
81
+ 'manage_options',
82
+ 'instant-articles-wizard',
83
+ array( 'Instant_Articles_Wizard', 'render' )
84
+ ,'dashicons-facebook'
85
+ );
86
+ // Hack to let the URL visible to ajax handlers
87
+ update_option( 'instant-articles-wizard-url', menu_page_url( 'instant-articles-wizard', false) );
88
+ }
89
+
90
+ public static function get_url() {
91
+ $url = menu_page_url( 'instant-articles-wizard', false );
92
+
93
+ // Needed when calling from ajax
94
+ if ( ! $url ) {
95
+ $url = get_option( 'instant-articles-wizard-url' );
96
+ }
97
+
98
+ return $url;
99
+ }
100
+
101
+
102
+ public static function get_admin_url() {
103
+ $url = parse_url( admin_url() );
104
+ return $url['host'];
105
+ }
106
+
107
+ public static function transition() {
108
+ if ( ! current_user_can( 'manage_options' ) ) {
109
+ wp_die( esc_html( 'You do not have sufficient permissions to access this page.' ) );
110
+ }
111
+
112
+ $new_state = sanitize_text_field( $_POST[ 'new_state' ] );
113
+
114
+ $params = $_POST[ 'params' ];
115
+ $params = json_decode( stripslashes( $params ), true );
116
+ foreach ( $params as $key => $param ) {
117
+ // escape every key
118
+ $params[ $key ] = sanitize_text_field( $param );
119
+ }
120
+
121
+ try {
122
+ Instant_Articles_Wizard_State::do_transition( $new_state, $params );
123
+ } catch ( Exception $e ) {
124
+ // If something went wrong, simply render the error + the same state.
125
+ echo '<div class="error settings-error notice is-dismissible"><p><strong>' . esc_html( $e->getMessage() ) . '</strong></p></div>';
126
+ }
127
+
128
+ self::render( true );
129
+ die();
130
+ }
131
+
132
+ /**
133
+ * Saves the App ID and App Secret.
134
+ *
135
+ * That does not trigger a state transition, as the state transition will
136
+ * only happen when the user logs in and we grab the access token.
137
+ */
138
+ public static function save_app() {
139
+ if ( ! current_user_can( 'manage_options' ) ) {
140
+ wp_die( esc_html( 'You do not have sufficient permissions to access this page.' ) );
141
+ }
142
+
143
+ $current_state = Instant_Articles_Wizard_State::get_current_state();
144
+ if ( $current_state !== Instant_Articles_Wizard_State::STATE_APP_SETUP ) {
145
+ die();
146
+ }
147
+
148
+ $app_id = sanitize_text_field( $_POST[ 'app_id' ] );
149
+ $app_secret = sanitize_text_field( $_POST[ 'app_secret' ] );
150
+
151
+ Instant_Articles_Option_FB_App::update_option( array(
152
+ 'app_id' => $app_id,
153
+ 'app_secret' => $app_secret
154
+ ) );
155
+
156
+ self::render( true );
157
+ die();
158
+ }
159
+
160
+ /**
161
+ * Submits the select page for review
162
+ */
163
+ public static function submit_for_review() {
164
+ if ( ! current_user_can( 'manage_options' ) ) {
165
+ wp_die( esc_html( 'You do not have sufficient permissions to access this page.' ) );
166
+ }
167
+
168
+ $current_state = Instant_Articles_Wizard_State::get_current_state();
169
+ if ( $current_state !== Instant_Articles_Wizard_State::STATE_REVIEW_SUBMISSION ) {
170
+ die();
171
+ }
172
+
173
+ $fb_app_settings = Instant_Articles_Option_FB_App::get_option_decoded();
174
+ $fb_page_settings = Instant_Articles_Option_FB_Page::get_option_decoded();
175
+
176
+ $client = Client::create(
177
+ $fb_app_settings[ 'app_id' ],
178
+ $fb_app_settings[ 'app_secret' ],
179
+ $fb_page_settings[ 'page_access_token' ],
180
+ $fb_page_settings[ 'page_id' ]
181
+ );
182
+
183
+ try {
184
+ // Bulk upload articles for review
185
+ $articles_for_review = Instant_Articles_Wizard_Review_Submission::getArticlesForReview();
186
+ foreach ( $articles_for_review as $post ) {
187
+ Instant_Articles_Publisher::submit_article( $post->ID, $post );
188
+ }
189
+
190
+ // Trigger review submission
191
+ $client->submitForReview();
192
+ } catch ( Exception $e ) {
193
+ // If something went wrong, simply render the error + the same state.
194
+ echo '<div class="error settings-error notice is-dismissible"><p><strong>' . esc_html( $e->getMessage() ) . '</strong></p></div>';
195
+ }
196
+
197
+ self::render( true );
198
+ die();
199
+ }
200
+
201
+ /**
202
+ * Edits the App ID and App Secret within the APP_SETUP state.
203
+ */
204
+ public static function edit_app() {
205
+ if ( ! current_user_can( 'manage_options' ) ) {
206
+ wp_die( esc_html( 'You do not have sufficient permissions to access this page.' ) );
207
+ }
208
+
209
+ $current_state = Instant_Articles_Wizard_State::get_current_state();
210
+ if ( $current_state !== Instant_Articles_Wizard_State::STATE_APP_SETUP ) {
211
+ die();
212
+ }
213
+
214
+ Instant_Articles_Option_FB_App::delete_option();
215
+
216
+ self::render( true );
217
+ die();
218
+ }
219
+
220
+ public static function is_page_signed_up() {
221
+ $page_id = sanitize_text_field( $_POST[ 'page_id' ] );
222
+
223
+ $fb_helper = new Instant_Articles_Wizard_FB_Helper();
224
+ $pages = $fb_helper->get_pages();
225
+
226
+ if ( isset( $pages[ $page_id ] ) && $pages[ $page_id ][ 'supports_instant_articles' ] ) {
227
+ die( 'yes' );
228
+ }
229
+ else {
230
+ die( 'no' );
231
+ }
232
+ }
233
+
234
+ public static function render( $ajax = false ) {
235
+ if ( ! current_user_can( 'manage_options' ) ) {
236
+ wp_die( esc_html( 'You do not have sufficient permissions to access this page.' ) );
237
+ }
238
+
239
+ // Read options (they are used on the templates)
240
+ $current_state = Instant_Articles_Wizard_State::get_current_state();
241
+ $fb_page_settings = Instant_Articles_Option_FB_Page::get_option_decoded();
242
+ $fb_app_settings = Instant_Articles_Option_FB_App::get_option_decoded();
243
+ $fb_helper = new Instant_Articles_Wizard_FB_Helper();
244
+ $settings_url = self::get_url();
245
+
246
+ // Handle redirection from Login flow
247
+ // ----------------------------------
248
+ // Only during STATE_APP_SETUP
249
+ if ( $current_state === Instant_Articles_Wizard_State::STATE_APP_SETUP ) {
250
+ $user_access_token = $fb_helper->get_fb_access_token();
251
+ $permissions = $fb_helper->get_fb_permissions( $user_access_token );
252
+
253
+ // Trigger transition if all needed permissions are granted
254
+ if ( $user_access_token && isset( $permissions[ 'pages_manage_instant_articles' ] ) && isset( $permissions[ 'pages_show_list' ] ) ) {
255
+ Instant_Articles_Wizard_State::do_transition( Instant_Articles_Wizard_State::STATE_PAGE_SELECTION, array(
256
+ 'app_id' => $fb_app_settings[ 'app_id' ],
257
+ 'app_secret' => $fb_app_settings[ 'app_secret' ],
258
+ 'user_access_token' => $user_access_token->getValue()
259
+ ) );
260
+ // Override step
261
+ $current_state = Instant_Articles_Wizard_State::get_current_state();
262
+ }
263
+ }
264
+ // ----------------------------------
265
+
266
+
267
+ // Grabs the current configured style
268
+ // ----------------------------------
269
+ // Only during STATE_STYLE_SELECTION
270
+ if ( $current_state === Instant_Articles_Wizard_State::STATE_STYLE_SELECTION ) {
271
+ $settings_style = Instant_Articles_Option_Styles::get_option_decoded();
272
+ if ( isset( $settings_style['article_style'] ) && ! empty( $settings_style['article_style'] ) ) {
273
+ $article_style = $settings_style['article_style'];
274
+ } else {
275
+ $article_style = 'default';
276
+ }
277
+ }
278
+ // ----------------------------------
279
+
280
+
281
+ // Check submission status
282
+ // ----------------------------------
283
+ // Only during STATE_REVIEW_SUBMISSION
284
+ if ( $current_state === Instant_Articles_Wizard_State::STATE_REVIEW_SUBMISSION ) {
285
+ $review_submission_status = Instant_Articles_Wizard_Review_Submission::getReviewSubmissionStatus();
286
+ if ( $review_submission_status === Instant_Articles_Wizard_Review_Submission::STATUS_NOT_SUBMITTED ) {
287
+ $articles_for_review = Instant_Articles_Wizard_Review_Submission::getArticlesForReview();
288
+ }
289
+ }
290
+ // ----------------------------------
291
+
292
+ include( dirname( __FILE__ ) . '/templates/wizard-template.php' );
293
+ }
294
+
295
+ }
wizard/state_machine.txt ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -----------------------------------------------------------------------------------------------------------------------
2
+ Facebook Instant Articles for WordPress
3
+ -----------------------------------------------------------------------------------------------------------------------
4
+ Setup Wizard State Machine
5
+ -----------------------------------------------------------------------------------------------------------------------
6
+
7
+
8
+ ┌──────────────────────────────────────────────edit_app───────────────────────────────────────────────┐
9
+ │ │
10
+ │ ┌──────────────────────────edit_app─────────────────────────────┐ │
11
+ │ │ │ │
12
+ │ │ ┌──────────edit_app──────────┐ │ │
13
+ │ │ │ │ │ │
14
+ ▼ ▼ ▼ │ │ │
15
+ ┏━━━━━━━━━┓ ┏━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━━━┓
16
+ ┃APP_SETUP┃───set_up_app───▶┃PAGE_SELECTION┃───select_page──▶┃STYLE_SELECTION┃───select_style──▶┃REVIEW_SUBMISSION┃
17
+ ┗━━━━━━━━━┛ ┗━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━━━┛
18
+ ▲ ▲ ▲ │ ▲ │ │
19
+ │ │ │ │ │ │ │
20
+ │ │ └──────────edit_page───────────┘ └──────────edit_style─────────┘ │
21
+ start_wizard │ │
22
+ │ └───────────────────────────edit_page────────────────────────────────┘
23
+
24
+ ┏━━━━━━━━┓
25
+ ┃OVERVIEW┃
26
+ ┗━━━━━━━━┛
wizard/templates/advanced-template.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Facebook Instant Articles for WP.
4
+ * This source code is licensed under the license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @package default
8
+ */
9
+
10
+ ?>
11
+
12
+ <div class="instant-articles-card">
13
+ <div class="instant-articles-card-content">
14
+ <div class="instant-articles-card-content-box instant-articles-card-content-full">
15
+ <form method="post" action="options.php">
16
+ <?php settings_fields( Instant_Articles_Option::PAGE_OPTION_GROUP ); ?>
17
+ <p>Configure settings for your styles, ads, analytics and publishing in Instant Articles. Review our <a href="https://developers.facebook.com/docs/instant-articles" target="_blank">developer documentation</a> to learn more.</p>
18
+ <hr />
19
+ <?php do_settings_sections( Instant_Articles_Option_Styles::OPTION_KEY ); ?>
20
+ <hr />
21
+ <?php do_settings_sections( Instant_Articles_Option_Ads::OPTION_KEY ); ?>
22
+ <hr />
23
+ <?php do_settings_sections( Instant_Articles_Option_Analytics::OPTION_KEY ); ?>
24
+ <hr />
25
+ <?php do_settings_sections( Instant_Articles_Option_Publishing::OPTION_KEY ); ?>
26
+ <hr />
27
+ <?php submit_button( __( 'Save changes' ) ); ?>
28
+ </form>
29
+ </div>
30
+ </div>
31
+ </div>
wizard/templates/all-steps-wizard.php ADDED
@@ -0,0 +1,288 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Facebook Instant Articles for WP.
4
+ * This source code is licensed under the license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @package default
8
+ */
9
+ ?>
10
+ <div class="instant-articles-card">
11
+ <div class="instant-articles-card-content">
12
+ <div class="instant-articles-card-content-box instant-articles-card-content-full">
13
+ <h3>Setting up Instant Articles</h3>
14
+ <div class="instant-articles-card-steps">
15
+ <div class="instant-articles-card-step">
16
+ <img src="/wp-content/plugins/facebook-instant-articles-wp/assets/key@2x.png">
17
+ <p>Enter Facebook App ID and connect to Facebook to Enable Plugin</p>
18
+ </div>
19
+ <div class="instant-articles-card-step">
20
+ <img src="/wp-content/plugins/facebook-instant-articles-wp/assets/connect@2x.png">
21
+ <p>Sign up for Instant Articles and select your Facebook Page</p>
22
+ </div>
23
+ <div class="instant-articles-card-step">
24
+ <img src="/wp-content/plugins/facebook-instant-articles-wp/assets/customize@2x.png">
25
+ <p>Choose how you want your Instant Articles to look using the Style Editor</p>
26
+ </div>
27
+ <div class="instant-articles-card-step">
28
+ <img src="/wp-content/plugins/facebook-instant-articles-wp/assets/check@2x.png">
29
+ <p>Submit your Instant Articles for review and start publishing</p>
30
+ </div>
31
+ </div>
32
+ <button class="instant-articles-button instant-articles-button-highlight instant-articles-button-centered">
33
+ <label>Next</label>
34
+ </button>
35
+ </div>
36
+ </div>
37
+ </div>
38
+
39
+
40
+ <div class="instant-articles-card-bullet-bar">
41
+ <div class="instant-articles-card-bullet-step instant-articles-card-bullet-step-completed">
42
+ <div class="instant-articles-card-bullet">✔</div>
43
+ <div class="instant-articles-card-bullet-path"></div>
44
+ <p>Enter Facebook App ID and connect to Facebook to Enable Plugin</p>
45
+ </div>
46
+ <div class="instant-articles-card-bullet-step instant-articles-card-bullet-step-completed">
47
+ <div class="instant-articles-card-bullet">✔</div>
48
+ <div class="instant-articles-card-bullet-path"></div>
49
+ <p>Sign up for Instant Articles and select your Facebook Page</p>
50
+ </div>
51
+ <div class="instant-articles-card-bullet-step instant-articles-card-bullet-step-current">
52
+ <div class="instant-articles-card-bullet"></div>
53
+ <div class="instant-articles-card-bullet-path"></div>
54
+ <p>Choose how you want your Instant Articles to look using the Style Editor</p>
55
+ </div>
56
+ <div class="instant-articles-card-bullet-step">
57
+ <div class="instant-articles-card-bullet"></div>
58
+ <p>Submit your Instant Articles for review and start publishing</p>
59
+ </div>
60
+ </div>
61
+
62
+ <div class="instant-articles-card">
63
+ <div class="instant-articles-card-title">
64
+ <h3>Authenticate Plugin</h3>
65
+ <div class="instant-articles-card-title-right">
66
+ <span class="instant-articles-card-title-checkmark">✔</span>
67
+ <label class="instant-articles-card-title-label">App connected:</label>
68
+ <span class="instant-articles-card-title-value">1273677375983360</span>
69
+ <a class="instant-articles-card-title-edit" href="#"></a>
70
+ <span class="instant-articles-card-title-step">Step 1 of 2</span>
71
+ </div>
72
+ </div>
73
+ <div class="instant-articles-card-content">
74
+ <div class="instant-articles-card-content-box instant-articles-card-content-left">
75
+ <label>Title of this section</label>
76
+ <p>Yo dawg, you need a Facebook App to enable this plugin. It's for really cool security reasons. If you already know what I'm sayin' and have an App ID and App Secret, <a href="#">go here</a> to grab it. If you don't have any idea what's going on then go ahead and get an App ID already.</p>
77
+ <button class="instant-articles-button">
78
+ <label>Get App ID</label>
79
+ </button>
80
+ </div>
81
+ <div class="instant-articles-card-content-box instant-articles-card-content-right">
82
+ <label class="instant-articles-label">App ID</label>
83
+ <input class="instant-articles-input-text"type="text"/>
84
+ <label class="instant-articles-label">App Secret</label>
85
+ <input class="instant-articles-input-text" type="password"/>
86
+ <button class="instant-articles-button instant-articles-button-highlight instant-articles-button-disabled">
87
+ <label>Submit</label>
88
+ </button>
89
+ </div>
90
+ </div>
91
+ </div>
92
+
93
+ <div class="instant-articles-card">
94
+ <div class="instant-articles-card-title">
95
+ <h3>Authenticate Plugin</h3>
96
+ <div class="instant-articles-card-title-right">
97
+ <span class="instant-articles-card-title-checkmark">✔</span>
98
+ <label class="instant-articles-card-title-label">App connected:</label>
99
+ <span class="instant-articles-card-title-value">1273677375983360</span>
100
+ <a class="instant-articles-card-title-edit" href="#"></a>
101
+ <span class="instant-articles-card-title-step">Step 2 of 2</span>
102
+ </div>
103
+ </div>
104
+ <div class="instant-articles-card-content">
105
+ <div class="instant-articles-card-content-box instant-articles-card-content-full">
106
+ <span class="instant-articles-card-title-checkmark">✔</span>
107
+ <label class="instant-articles-card-title-label">App connected:</label>
108
+ <span class="instant-articles-card-title-value">1273677375983360</span>
109
+ <a class="instant-articles-card-title-edit" href="#"></a>
110
+ <hr/>
111
+ <label>Connect your Facebook Account</label>
112
+ <p>Login with Facebook to finish activating the Instant Articles Plugin.</p>
113
+ <button class="instant-articles-button">
114
+ <span class="instant-articles-button-icon-facebook"></span>
115
+ <label>Login with Facebook</label>
116
+ </button>
117
+ </div>
118
+ </div>
119
+ </div>
120
+
121
+
122
+ <div class="instant-articles-card instant-articles-card-collapsed">
123
+ <div class="instant-articles-card-title">
124
+ <h3>Authenticate Plugin</h3>
125
+ <div class="instant-articles-card-title-right">
126
+ <span class="instant-articles-card-title-checkmark">✔</span>
127
+ <label class="instant-articles-card-title-label">App connected:</label>
128
+ <span class="instant-articles-card-title-value">1273677375983360</span>
129
+ <a class="instant-articles-card-title-edit" href="#"></a>
130
+ <span class="instant-articles-card-title-step">Step 1 of 2</span>
131
+ </div>
132
+ </div>
133
+ <div class="instant-articles-card-content">
134
+ <div class="instant-articles-card-content-box instant-articles-card-content-left">
135
+ <label>Title of this section</label>
136
+ <p>Yo dawg, you need a Facebook App to enable this plugin. It's for really cool security reasons. If you already know what I'm sayin' and have an App ID and App Secret, <a href="#">go here</a> to grab it. If you don't have any idea what's going on then go ahead and get an App ID already.</p>
137
+ <button class="instant-articles-button">
138
+ <label>Get App ID</label>
139
+ </button>
140
+ </div>
141
+ <div class="instant-articles-card-content-box instant-articles-card-content-right">
142
+ <label class="instant-articles-label">App ID</label>
143
+ <input class="instant-articles-input-text"type="text"/>
144
+ <label class="instant-articles-label">App Secret</label>
145
+ <input class="instant-articles-input-text" type="text"/>
146
+ <button class="instant-articles-button instant-articles-button-highlight instant-articles-button-disabled">
147
+ <label>Submit</label>
148
+ </button>
149
+ </div>
150
+ </div>
151
+ </div>
152
+
153
+ <div class="instant-articles-card">
154
+ <div class="instant-articles-card-title">
155
+ <h3>Select Facebook Page</h3>
156
+ </div>
157
+ <div class="instant-articles-card-content">
158
+ <div class="instant-articles-card-content-box instant-articles-card-content-full">
159
+ <p>Select the Facebook Page that you want to enable Instant Articles tools. Anyone with an admin role on that Page will also be able to access your Instant Articles tools.</p>
160
+ <ul>
161
+ <li>
162
+ <input type="radio" name="page-id"/>
163
+ <img class="instant-articles-page-img" src=""/>
164
+ <label>The Salty Shop</label>
165
+ </li>
166
+ <li>
167
+ <input type="radio" name="page-id"/>
168
+ <img class="instant-articles-page-img" src=""/>
169
+ <label>The Sweet Shop</label>
170
+ </li>
171
+ <li>
172
+ <input type="radio" name="page-id"/>
173
+ <img class="instant-articles-page-img" src=""/>
174
+ <label>Free Market street</label>
175
+ </li>
176
+ <li>
177
+ <input type="radio" name="page-id"/>
178
+ <img class="instant-articles-page-img" src="https://z-1-scontent-atl3-1.xx.fbcdn.net/v/t1.0-1/p160x160/943890_1119857341366295_1442705195804509998_n.jpg?oh=6e5a53e35daeb43b5f31f3d309dce08f&oe=582BADB5"/>
179
+ <label>The mall</label>
180
+ </li>
181
+ <li>
182
+ <input type="radio" name="page-id"/>
183
+ <img class="instant-articles-page-img" src="https://z-1-scontent-atl3-1.xx.fbcdn.net/v/t1.0-1/p160x160/12400471_1021643444544180_2130580769920124493_n.png?oh=3018153ee4db69a4737cd96197691d7f&oe=582BE583"/>
184
+ <label>Hilo CrossFit</label>
185
+ </li>
186
+ <li>
187
+ <input type="radio" name="page-id"/>
188
+ <img class="instant-articles-page-img" src=""/>
189
+ <label>Street Dancer</label>
190
+ </li>
191
+ </ul>
192
+ <button class="instant-articles-button instant-articles-button-highlight">
193
+ <label>Select</label>
194
+ </button>
195
+ </div>
196
+ </div>
197
+ </div>
198
+
199
+ <div class="instant-articles-card instant-articles-card-collapsed">
200
+ <div class="instant-articles-card-title">
201
+ <h3>Facebook Page Enabled</h3>
202
+ <div class="instant-articles-card-title-right">
203
+ <a href="" class="instant-articles-card-title-link">Hilo CrossFit</a>
204
+ <img class="instant-articles-page-img" src=""/>
205
+ <a class="instant-articles-card-title-edit" href="#"></a>
206
+ </div>
207
+ </div>
208
+ <div class="instant-articles-card-content">
209
+ <div class="instant-articles-card-content-box instant-articles-card-content-full">
210
+ <p>Select the Facebook Page that you want to enable Instant Articles tools. Anyone with an admin role on that Page will also be able to access your Instant Articles tools.</p>
211
+ <ul>
212
+ <li>
213
+ <input type="radio" name="page-id"/>
214
+ <img class="instant-articles-page-img" src=""/>
215
+ <label>The Salty Shop</label>
216
+ </li>
217
+ <li>
218
+ <input type="radio" name="page-id"/>
219
+ <img class="instant-articles-page-img" src=""/>
220
+ <label>The Sweet Shop</label>
221
+ </li>
222
+ <li>
223
+ <input type="radio" name="page-id"/>
224
+ <img class="instant-articles-page-img" src=""/>
225
+ <label>Free Market street</label>
226
+ </li>
227
+ <li>
228
+ <input type="radio" name="page-id"/>
229
+ <img class="instant-articles-page-img" src="https://z-1-scontent-atl3-1.xx.fbcdn.net/v/t1.0-1/p160x160/943890_1119857341366295_1442705195804509998_n.jpg?oh=6e5a53e35daeb43b5f31f3d309dce08f&oe=582BADB5"/>
230
+ <label>The mall</label>
231
+ </li>
232
+ <li class="instant-articles-radio-selected">
233
+ <input type="radio" name="page-id" checked="checked"/>
234
+ <img class="instant-articles-page-img" src="https://z-1-scontent-atl3-1.xx.fbcdn.net/v/t1.0-1/p160x160/12400471_1021643444544180_2130580769920124493_n.png?oh=3018153ee4db69a4737cd96197691d7f&oe=582BE583"/>
235
+ <label>Hilo CrossFit</label>
236
+ </li>
237
+ <li>
238
+ <input type="radio" name="page-id"/>
239
+ <img class="instant-articles-page-img" src=""/>
240
+ <label>Street Dancer</label>
241
+ </li>
242
+ </ul>
243
+ <button class="instant-articles-button instant-articles-button-highlight">
244
+ <label>Select</label>
245
+ </button>
246
+ </div>
247
+ </div>
248
+ </div>
249
+
250
+ <div class="instant-articles-card">
251
+ <div class="instant-articles-card-title">
252
+ <h3>Style &amp; Preview Articles</h3>
253
+ </div>
254
+ <div class="instant-articles-card-content">
255
+ <div class="instant-articles-card-content-box instant-articles-card-content-full">
256
+ <p>Lorem ipsum dolor sit amet, consectetuer adipscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis notoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim.</p>
257
+ <button class="instant-articles-button">
258
+ <label>Choose style</label>
259
+ </button>
260
+ </div>
261
+ </div>
262
+ </div>
263
+
264
+ <div class="instant-articles-card instant-articles-card-collapsed">
265
+ <div class="instant-articles-card-title">
266
+ <h3>Style Selected</h3>
267
+ <div class="instant-articles-card-title-right">
268
+ <a href="" class="instant-articles-card-title-link">Sweet Shop Style 01</a>
269
+ <a class="instant-articles-card-title-edit" href="#"></a>
270
+ </div>
271
+ </div>
272
+ </div>
273
+
274
+ <div class="instant-articles-card">
275
+ <div class="instant-articles-card-title">
276
+ <h3>Submit for Review</h3>
277
+ </div>
278
+ <div class="instant-articles-card-content">
279
+ <div class="instant-articles-card-content-box instant-articles-card-content-full">
280
+ <p>Lorem ipsum dolor sit amet, consectetuer adipscing elit. Aenean commodo ligula eget dolor. Aenean massa. Cum sociis notoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Donec quam felis, ultricies nec, pellentesque eu, pretium quis, sem. Nulla consequat massa quis enim.</p>
281
+ <button class="instant-articles-button instant-articles-button-highlight">
282
+ <label>Submit for Review</label>
283
+ </button>
284
+ </div>
285
+ </div>
286
+ </div>
287
+
288
+ <a class="instant-articles-advanced-settings" href="#">► Advanced Settings</a>
wizard/templates/app-setup-template.php ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Facebook Instant Articles for WP.
4
+ * This source code is licensed under the license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @package default
8
+ */
9
+ ?>
10
+ <?php if ( empty( $fb_app_settings['app_id'] ) || empty( $fb_app_settings['app_secret'] ) ) : ?>
11
+
12
+ <!-- Step 1: grab your App ID and App Secret -->
13
+ <div class="instant-articles-card">
14
+ <div class="instant-articles-card-title">
15
+ <h3>Log In with Your Developers App and Facebook Account</h3>
16
+ <div class="instant-articles-card-title-right">
17
+ <span class="instant-articles-card-title-step">Step 1 of 2</span>
18
+ </div>
19
+ </div>
20
+ <div class="instant-articles-card-content">
21
+ <div class="instant-articles-card-content-box instant-articles-card-content-left">
22
+ <label>First, log in with your Facebook Developers App</label>
23
+
24
+ <p>You'll need to log in with your Facebook Developers App ID to connect your plugin to the Facebook Page you'll use to publish your Instant Articles.</p>
25
+
26
+ <p><strong>Already have an App ID and Secret?</strong> Just enter them to the right.</p>
27
+
28
+ <p><strong>Need to create one?</strong></p>
29
+
30
+ <p>Click on the 'Get App ID' button below to begin the process of getting your App ID and Secret. This will open the App set up page in a new tab. Then, follow these steps:</p>
31
+
32
+ <ol>
33
+ <li>Click on the green '+ Add a New App' button in the upper right corner of the page.</li>
34
+ <li>If you are prompted to select a platform, select 'basic setup.' If you don't see this prompt, don't worry; just go directly to Step 3.</li>
35
+ <li>Create an app name (it can be whatever you want it to be), input your email and select 'Apps for Pages' in the 'Category' dropdown menu. Click 'Create App ID' when you’re ready.</li>
36
+ <li>Click on 'Settings' in the left nav bar.</li>
37
+ <li>Click '+Add Platform' at the bottom of the page and select 'Website.'</li>
38
+ <li>Under both the 'App Domains' in the main section and 'Site URL' in the 'Website' section, enter this domain: <b><?php echo esc_url( self::get_admin_url() ); ?></b></li>
39
+ <li>Click 'Save Changes' in the lower right corner.</li>
40
+ <li>Select 'Show' to see your App Secret. Copy your App ID and Secret and enter them to the right.</li>
41
+ </ol>
42
+
43
+ <p><strong>Need more help?</strong> <a href="https://developers.facebook.com/docs/instant-articles/wordpress-quickstart#appid" target="_blank">See detailed instructions on setting up your App ID in our documentation</a>.</p>
44
+
45
+ <a class="instant-articles-button" href="https://developers.facebook.com/apps" target="_blank">
46
+ Get App ID
47
+ </a>
48
+ </div>
49
+ <div class="instant-articles-card-content-box instant-articles-card-content-right">
50
+ <label class="instant-articles-label">App ID</label>
51
+ <input name="app_id" class="instant-articles-input-text" type="text"/>
52
+ <label class="instant-articles-label">App Secret</label>
53
+ <input name="app_secret" class="instant-articles-input-text" type="password"/>
54
+ <button id="instant-articles-wizard-save-app" class="instant-articles-button instant-articles-button-highlight instant-articles-button-disabled">
55
+ <label>Log In</label>
56
+ </button>
57
+ </div>
58
+ <br clear="both" />
59
+ </div>
60
+ </div>
61
+
62
+ <?php else: ?>
63
+
64
+ <!-- Step 2: log in with Facebook -->
65
+ <div class="instant-articles-card">
66
+ <div class="instant-articles-card-title">
67
+ <h3>Log In with Your Developers App and Facebook Account</h3>
68
+ <div class="instant-articles-card-title-right">
69
+ <span class="instant-articles-card-title-step">Step 2 of 2</span>
70
+ </div>
71
+ </div>
72
+ <div class="instant-articles-card-content">
73
+ <div class="instant-articles-card-content-box instant-articles-card-content-full">
74
+ <span class="instant-articles-card-title-checkmark">✔</span>
75
+ <label class="instant-articles-card-title-label">App connected:</label>
76
+ <span class="instant-articles-card-title-value"><?php echo esc_html( $fb_app_settings[ 'app_id' ] ); ?></span>
77
+ <a id="instant-articles-wizard-edit-app" class="instant-articles-card-title-edit" href="#"></a>
78
+ <hr/>
79
+ <label>Next, log into your Facebook account</label>
80
+ <p>Log in with Facebook to finish connecting the Instant Articles Plugin to your account.</p>
81
+ <?php
82
+ if (
83
+ $user_access_token &&
84
+ (
85
+ ! isset( $permissions['pages_manage_instant_articles'] ) ||
86
+ ! isset( $permissions['pages_show_list'] )
87
+ )
88
+ ) :
89
+ ?>
90
+ <p>In order to finish the activation, you need to grant all the requested permissions:</p>
91
+ <ul>
92
+ <?php if ( ! isset( $permissions['pages_show_list'] ) ) : ?>
93
+ <li>
94
+ <b>Show a list of the Pages you manage</b>: allows the plugin to show the list of your
95
+ pages for you to select one.
96
+ </li>
97
+ <?php endif; ?>
98
+ <?php if ( ! isset( $permissions['pages_manage_instant_articles'] ) ) : ?>
99
+ <li>
100
+ <b>Manage Instant Articles for your Pages</b>: allows us to publish
101
+ Instant Articles to your selected page.
102
+ </li>
103
+ <?php endif; ?>
104
+ </ul>
105
+ <?php endif;?>
106
+
107
+ <a href="<?php echo esc_attr( $fb_helper->get_login_url() ); ?>" class="instant-articles-button">
108
+ <span class="instant-articles-button-icon-facebook"></span>
109
+ <label>Login with Facebook</label>
110
+ </a>
111
+ </div>
112
+ <div clear="both" />
113
+ </div>
114
+ </div>
115
+ <?php endif; ?>
wizard/templates/overview-template.php ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Facebook Instant Articles for WP.
4
+ * This source code is licensed under the license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @package default
8
+ */
9
+ ?>
10
+ <div class="instant-articles-card">
11
+ <div class="instant-articles-card-content">
12
+ <div class="instant-articles-card-content-box instant-articles-card-content-full">
13
+ <h3>Get Started with the Instant Articles WordPress Plugin</h3>
14
+ <div class="instant-articles-card-steps">
15
+ <div class="instant-articles-card-step">
16
+ <img src="<?php echo esc_url( plugin_dir_url( __FILE__ ) ); ?>../../assets/key@2x.png">
17
+ <h4>Set Up and Log In</h4>
18
+ <p>If you don't have one already, set up your Facebook Developers App. Then log in to connect your plugin.</p>
19
+ </div>
20
+ <div class="instant-articles-card-step">
21
+ <img src="<?php echo esc_url( plugin_dir_url( __FILE__ ) ); ?>../../assets/connect@2x.png">
22
+ <h4>Select Your Page</h4>
23
+ <p>Select the Page you'll use to publish your Instant Articles.</p>
24
+ </div>
25
+ <div class="instant-articles-card-step">
26
+ <img src="<?php echo esc_url( plugin_dir_url( __FILE__ ) ); ?>../../assets/customize@2x.png">
27
+ <h4>Customize Your Style</h4>
28
+ <p>Use our Style Editor to make your Instant Articles look just how you want them to.</p>
29
+ </div>
30
+ <div class="instant-articles-card-step">
31
+ <img src="<?php echo esc_url( plugin_dir_url( __FILE__ ) ); ?>../../assets/check@2x.png">
32
+ <h4>Submit for Review</h4>
33
+ <p>Submit your Instant Articles for review and start publishing.</p>
34
+ </div>
35
+ </div>
36
+ <button
37
+ class="instant-articles-button instant-articles-button-highlight instant-articles-button-centered instant-articles-wizard-transition"
38
+ data-new-state="STATE_APP_SETUP">
39
+ <label>Get Started</label>
40
+ </button>
41
+ </div>
42
+ </div>
43
+ </div>
wizard/templates/page-selection-template.php ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Facebook Instant Articles for WP.
4
+ * This source code is licensed under the license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @package default
8
+ */
9
+ ?>
10
+ <div class="instant-articles-card instant-articles-card-collapsed">
11
+ <div class="instant-articles-card-title">
12
+ <h3>Logged In</h3>
13
+ <div class="instant-articles-card-title-right">
14
+ <span class="instant-articles-card-title-checkmark">✔</span>
15
+ <label class="instant-articles-card-title-label">App connected:</label>
16
+ <span class="instant-articles-card-title-value"><?php echo esc_html( $fb_app_settings[ 'app_id' ] ); ?></span>
17
+ <a class="instant-articles-wizard-transition instant-articles-card-title-edit" href="#" data-new-state="<?php echo esc_attr( Instant_Articles_Wizard_State::STATE_APP_SETUP ); ?>"></a>
18
+ </div>
19
+ </div>
20
+ </div>
21
+
22
+ <div class="instant-articles-card">
23
+ <div class="instant-articles-card-title">
24
+ <h3>Which Page Would You Like to Use for Instant Articles?</h3>
25
+ </div>
26
+ <div class="instant-articles-card-content">
27
+ <div class="instant-articles-card-content-box instant-articles-card-content-full">
28
+ <p>
29
+ Select the Page you'd like to use to access the Instant Articles tools.
30
+ Anyone with an admin role will also be able to use the tools.
31
+ Don't have a Page yet?
32
+ <strong><a href="https://www.facebook.com/pages/create" target="_blank">Create one</a>.</strong>
33
+ </p>
34
+ <ul class="instant-articles-wizard-page-selection">
35
+ <?php foreach ( $fb_helper->get_pages() as $page ) { ?>
36
+ <li>
37
+ <input
38
+ type="radio"
39
+ name="page_id"
40
+ value="<?php echo esc_attr( $page[ 'page_id' ] ) ?>"
41
+ data-signed-up="<?php echo $page[ 'supports_instant_articles' ] ? 'yes' : 'no'; ?>"
42
+ />
43
+ <img class="instant-articles-page-img" src="<?php echo esc_attr( $page[ 'page_picture' ] ) ?>"/>
44
+ <label>
45
+ <?php echo esc_html( $page[ 'page_name' ] ) ?>
46
+ <?php if ( $page[ 'supports_instant_articles' ] ) : ?>
47
+ <span class="page-enabled">✔ Enabled</span>
48
+ <?php else : ?>
49
+ <span class="page-not-enabled">
50
+ This page has not been signed up yet.
51
+ <a href="https://www.facebook.com/instant_articles/signup?redirect_uri=<?php echo urlencode( $settings_url ) ?>&page_id=<?php echo urlencode( $page[ 'page_id' ] ) ?>">Sign Up</a>.
52
+ </span>
53
+ <?php endif; ?>
54
+ </label>
55
+ </li>
56
+ <?php } ?>
57
+ </ul>
58
+ <button id="instant-articles-wizard-select-page" class="instant-articles-button instant-articles-button-highlight instant-articles-button-disabled">
59
+ <label>Select</label>
60
+ </button>
61
+ </div>
62
+ </div>
63
+ </div>
wizard/templates/review-submission-template.php ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Facebook Instant Articles for WP.
4
+ * This source code is licensed under the license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @package default
8
+ */
9
+ ?>
10
+ <div class="instant-articles-card instant-articles-card-collapsed">
11
+ <div class="instant-articles-card-title">
12
+ <h3>Logged In</h3>
13
+ <div class="instant-articles-card-title-right">
14
+ <span class="instant-articles-card-title-checkmark">✔</span>
15
+ <label class="instant-articles-card-title-label">App connected:</label>
16
+ <span class="instant-articles-card-title-value"><?php echo esc_html( $fb_app_settings[ 'app_id' ] ); ?></span>
17
+ <a class="instant-articles-wizard-transition instant-articles-card-title-edit" href="#" data-new-state="<?php echo esc_attr( Instant_Articles_Wizard_State::STATE_APP_SETUP ); ?>"></a>
18
+ </div>
19
+ </div>
20
+ </div>
21
+
22
+ <div class="instant-articles-card instant-articles-card-collapsed">
23
+ <div class="instant-articles-card-title">
24
+ <h3>Facebook Page Enabled</h3>
25
+ <div class="instant-articles-card-title-right">
26
+ <a href="" class="instant-articles-card-title-link"><?php echo esc_html( $fb_page_settings[ 'page_name' ] ) ?></a>
27
+ <?php if ( $fb_page_settings[ 'page_picture' ] ) : ?>
28
+ <img class="instant-articles-page-img" src="<?php echo esc_attr( $fb_page_settings[ 'page_picture' ] ) ?>"/>
29
+ <?php endif; ?>
30
+ <a class="instant-articles-wizard-transition instant-articles-card-title-edit" href="#" data-new-state="<?php echo esc_attr( Instant_Articles_Wizard_State::STATE_PAGE_SELECTION ); ?>"></a>
31
+ </div>
32
+ </div>
33
+ </div>
34
+
35
+ <div class="instant-articles-card instant-articles-card-collapsed">
36
+ <div class="instant-articles-card-title">
37
+ <h3>Style Customized</h3>
38
+ <div class="instant-articles-card-title-right">
39
+ <a class="instant-articles-wizard-transition instant-articles-card-title-edit" href="#" data-new-state="<?php echo esc_attr( Instant_Articles_Wizard_State::STATE_STYLE_SELECTION ); ?>"></a>
40
+ </div>
41
+ </div>
42
+ </div>
43
+
44
+ <?php
45
+ switch ( $review_submission_status ) :
46
+
47
+ case Instant_Articles_Wizard_Review_Submission::STATUS_NOT_SUBMITTED : ?>
48
+
49
+ <?php if ( count($articles_for_review) >= Instant_Articles_Wizard_Review_Submission::MIN_ARTICLES ) : ?>
50
+ <div class="instant-articles-card">
51
+ <div class="instant-articles-card-title">
52
+ <h3>Submit for Review</h3>
53
+ </div>
54
+ <div class="instant-articles-card-content">
55
+ <div class="instant-articles-card-content-box instant-articles-card-content-full">
56
+ <p>
57
+ The Instant Articles team will review a sample batch of your Instant Articles before you can begin to publish.
58
+ Click the 'Submit for Review" button below to send the last <?php echo esc_html( Instant_Articles_Wizard_Review_Submission::MIN_ARTICLES ); ?>
59
+ articles you've published to the team for review.
60
+ </p>
61
+ <p>
62
+ It will take us 2 business days to complete the review.
63
+ Once we've had a chance to take a look, we'll let you know if you're ready to start publishing or if you need to make some updates.
64
+ </p>
65
+ <button id="instant-articles-wizard-submit-for-review" class="instant-articles-button instant-articles-button-highlight">
66
+ <label>Submit for Review</label>
67
+ </button>
68
+ </div>
69
+ </div>
70
+ </div>
71
+ <?php else: ?>
72
+ <div class="instant-articles-card">
73
+ <div class="instant-articles-card-title">
74
+ <h3>Create More Articles to Submit for Review</h3>
75
+ </div>
76
+ <div class="instant-articles-card-content">
77
+ <div class="instant-articles-card-content-box instant-articles-card-content-full">
78
+ <p>
79
+ In order to begin publishing Instant Articles, our team needs to review a sample batch of <?php echo esc_html( Instant_Articles_Wizard_Review_Submission::MIN_ARTICLES ); ?>
80
+ of your Instant Articles. It looks like you don't have <?php echo esc_html( Instant_Articles_Wizard_Review_Submission::MIN_ARTICLES ); ?> articles available yet.
81
+ </p>
82
+ <p>
83
+ Once you've created the additional articles and have <?php echo esc_html( Instant_Articles_Wizard_Review_Submission::MIN_ARTICLES ); ?> available to send,
84
+ please return to this page and click the 'Submit for Review" button below.
85
+ </p>
86
+ <button id="instant-articles-wizard-submit-for-review" class="instant-articles-button-disabled instant-articles-button instant-articles-button-highlight">
87
+ <label>Submit for Review</label>
88
+ </button>
89
+ </div>
90
+ </div>
91
+ </div>
92
+ <?php endif; ?>
93
+
94
+
95
+ <?php break; ?>
96
+
97
+ <?php case Instant_Articles_Wizard_Review_Submission::STATUS_APPROVED : ?>
98
+ <div class="instant-articles-card instant-articles-card-success">
99
+ <div class="instant-articles-card-title">
100
+ <h3>Review Complete: Begin Publishing Your Instant Articles</h3>
101
+ </div>
102
+ <div class="instant-articles-card-content">
103
+ <div class="instant-articles-card-content-box instant-articles-card-content-full">
104
+ <p>
105
+ We reviewed some of your articles and they look great.
106
+ You're now ready to begin publishing your Instant Articles to Facebook and sharing them with your audience.
107
+ </p>
108
+ <p>
109
+ Next, set up monetization and analytics in Advanced Settings. Or explore the
110
+ <a href="<?php echo esc_url( 'https://www.facebook.com/' . $fb_page_settings['page_id'] . '/publishing_tools/?section=INSTANT_ARTICLES_SETTINGS' ); ?>" target="_blank">Instant Articles publishing tools</a>
111
+ through your selected Facebook Page.
112
+ </p>
113
+ </div>
114
+ </div>
115
+ </div>
116
+ <?php break; ?>
117
+
118
+ <?php case Instant_Articles_Wizard_Review_Submission::STATUS_REJECTED : ?>
119
+ <div class="instant-articles-card instant-articles-card-fail">
120
+ <div class="instant-articles-card-title">
121
+ <h3>Review Complete: Updates Needed</h3>
122
+ </div>
123
+ <div class="instant-articles-card-content">
124
+ <div class="instant-articles-card-content-box instant-articles-card-content-full">
125
+ <p>
126
+ We reviewed some of your articles and found a few things that need to be updated.
127
+ </p>
128
+ <p>
129
+ Please go to your selected
130
+ <a href="<?php echo esc_url( 'https://www.facebook.com/' . $fb_page_settings['page_id'] . '/publishing_tools/?section=INSTANT_ARTICLES_SETTINGS#Step-2' ); ?>" target="_blank">Facebook Page's Publishing Tools</a>
131
+ to get more specifics on the issues identified. Once these have been cleared up in WordPress, you'll be ready to begin publishing your Instant Articles.
132
+ </p>
133
+ </div>
134
+ </div>
135
+ </div>
136
+ <?php break; ?>
137
+
138
+ <?php case Instant_Articles_Wizard_Review_Submission::STATUS_PENDING : ?>
139
+ <div class="instant-articles-card">
140
+ <div class="instant-articles-card-title">
141
+ <h3>Review Articles Submitted</h3>
142
+ </div>
143
+ <div class="instant-articles-card-content">
144
+ <div class="instant-articles-card-content-box instant-articles-card-content-full">
145
+ <p>
146
+ Your articles have been submitted for review.
147
+ </p>
148
+ <p>
149
+ It will take us 2 business days to complete the review. Check back here to see the status of your review submission.
150
+ </p>
151
+ </div>
152
+ </div>
153
+ </div>
154
+ <?php break; ?>
155
+
156
+ <?php endswitch; ?>
wizard/templates/style-selection-template.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Facebook Instant Articles for WP.
4
+ * This source code is licensed under the license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @package default
8
+ */
9
+ ?>
10
+ <div class="instant-articles-card instant-articles-card-collapsed">
11
+ <div class="instant-articles-card-title">
12
+ <h3>Logged In</h3>
13
+ <div class="instant-articles-card-title-right">
14
+ <span class="instant-articles-card-title-checkmark">✔</span>
15
+ <label class="instant-articles-card-title-label">App connected:</label>
16
+ <span class="instant-articles-card-title-value"><?php echo esc_html( $fb_app_settings[ 'app_id' ] ); ?></span>
17
+ <a class="instant-articles-wizard-transition instant-articles-card-title-edit" href="#" data-new-state="<?php echo esc_attr( Instant_Articles_Wizard_State::STATE_APP_SETUP ); ?>"></a>
18
+ </div>
19
+ </div>
20
+ </div>
21
+
22
+ <div class="instant-articles-card instant-articles-card-collapsed">
23
+ <div class="instant-articles-card-title">
24
+ <h3>Page Selected</h3>
25
+ <div class="instant-articles-card-title-right">
26
+ <a href="" class="instant-articles-card-title-link"><?php echo esc_html( $fb_page_settings[ 'page_name' ] ) ?></a>
27
+ <?php if ( $fb_page_settings[ 'page_picture' ] ) : ?>
28
+ <img class="instant-articles-page-img" src="<?php echo esc_url( $fb_page_settings[ 'page_picture' ] ) ?>"/>
29
+ <?php endif; ?>
30
+ <a class="instant-articles-wizard-transition instant-articles-card-title-edit" href="#" data-new-state="<?php echo esc_attr( Instant_Articles_Wizard_State::STATE_PAGE_SELECTION ); ?>"></a>
31
+ </div>
32
+ </div>
33
+ </div>
34
+
35
+ <div class="instant-articles-card">
36
+ <div class="instant-articles-card-title">
37
+ <h3>Customize Your Style with Style Editor</h3>
38
+ </div>
39
+ <div class="instant-articles-card-content">
40
+ <div class="instant-articles-card-content-box instant-articles-card-content-full">
41
+ <p>
42
+ Customize the look and feel of your articles with one or more unique styles.
43
+ Try to make your Instant Articles look as much like your mobile web articles as possible
44
+ and be sure to upload your publication or blog's logo.
45
+ <a href="https://developers.facebook.com/docs/instant-articles/guides/design" target="_blank">Learn more in our design guidelines</a>.
46
+ </p>
47
+ <p>
48
+ Want to preview your style? Download Facebook Pages Manager on your
49
+ <a href="https://itunes.apple.com/us/app/facebook-pages-manager/id514643583?mt=8" target="_blank">iPhone</a>
50
+ or
51
+ <a href="https://play.google.com/store/apps/details?id=com.facebook.pages.app" target="_blank">Android</a>
52
+ phone.
53
+ </p>
54
+ <a
55
+ id="instant-articles-wizard-customize-style"
56
+ href="<?php echo esc_url( 'https://www.facebook.com/' . $fb_page_settings['page_id'] . '/publishing_tools/?section=INSTANT_ARTICLES_SETTINGS&initial_data[style]=' . $article_style ); ?>"
57
+ class="instant-articles-button instant-articles-button-highlight"
58
+ target="_blank">
59
+ Customize
60
+ </a>
61
+ <button id="instant-articles-wizard-customize-style-next" class="instant-articles-button instant-articles-wizard-transition" data-new-state="<?php echo esc_attr( Instant_Articles_Wizard_State::STATE_REVIEW_SUBMISSION ); ?>">
62
+ <label>Next</label>
63
+ </button>
64
+ </div>
65
+ </div>
66
+ </div>
wizard/templates/wizard-template.php ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Facebook Instant Articles for WP.
4
+ * This source code is licensed under the license found in the
5
+ * LICENSE file in the root directory of this source tree.
6
+ *
7
+ * @package default
8
+ */
9
+ ?>
10
+ <?php if ( ! $ajax ) : ?>
11
+ <h1>Facebook Instant Articles Settings</h1>
12
+ <div id="instant_articles_wizard_messages"><?php settings_errors(); ?></div>
13
+ <div id="instant_articles_wizard">
14
+ <?php endif; ?>
15
+
16
+ <?php if ( $current_state !== Instant_Articles_Wizard_State::STATE_OVERVIEW ): ?>
17
+ <?php
18
+ // Calculate classes for the timeline
19
+ $state_css_classes = array();
20
+ foreach ( Instant_Articles_Wizard_State::$timeline as $state => $order ) {
21
+ switch ( Instant_Articles_Wizard_State::get_timeline_position( $state ) ) {
22
+ case Instant_Articles_Wizard_State::TIMELINE_PAST:
23
+ $state_css_classes[ $state ] = 'instant-articles-card-bullet-step-completed';
24
+ break;
25
+ case Instant_Articles_Wizard_State::TIMELINE_CURRENT:
26
+ $state_css_classes[ $state ] = 'instant-articles-card-bullet-step-current';
27
+ break;
28
+ case Instant_Articles_Wizard_State::TIMELINE_FUTURE:
29
+ $state_css_classes[ $state ] = '';
30
+ break;
31
+ }
32
+ }
33
+ ?>
34
+
35
+ <div class="instant-articles-card-bullet-bar">
36
+ <div class="instant-articles-card-bullet-step <?php echo esc_attr( $state_css_classes[ Instant_Articles_Wizard_State::STATE_APP_SETUP ] ); ?>">
37
+ <div class="instant-articles-card-bullet"></div>
38
+ <div class="instant-articles-card-bullet-path"></div>
39
+ <?php if ( Instant_Articles_Wizard_State::get_timeline_position( Instant_Articles_Wizard_State::STATE_APP_SETUP ) === Instant_Articles_Wizard_State::TIMELINE_PAST ) : ?>
40
+ <h4>Logged In</h4>
41
+ <?php else : ?>
42
+ <h4>Set Up and Log In</h4>
43
+ <?php endif; ?>
44
+ <p>If you don't have one already, set up your Facebook Developers App. This will allow you to log in and connect your plugin.</p>
45
+ </div>
46
+ <div class="instant-articles-card-bullet-step <?php echo esc_attr( $state_css_classes[ Instant_Articles_Wizard_State::STATE_PAGE_SELECTION ] ); ?>">
47
+ <div class="instant-articles-card-bullet"></div>
48
+ <div class="instant-articles-card-bullet-path"></div>
49
+ <?php if ( Instant_Articles_Wizard_State::get_timeline_position( Instant_Articles_Wizard_State::STATE_PAGE_SELECTION ) === Instant_Articles_Wizard_State::TIMELINE_PAST ) : ?>
50
+ <h4>Page Selected</h4>
51
+ <?php else : ?>
52
+ <h4>Select Your Page</h4>
53
+ <?php endif; ?>
54
+ <p>Select the Page you'll use to publish your Instant Articles.</p>
55
+ </div>
56
+ <div class="instant-articles-card-bullet-step <?php echo esc_attr( $state_css_classes[ Instant_Articles_Wizard_State::STATE_STYLE_SELECTION ] ); ?>">
57
+ <div class="instant-articles-card-bullet"></div>
58
+ <div class="instant-articles-card-bullet-path"></div>
59
+ <?php if ( Instant_Articles_Wizard_State::get_timeline_position( Instant_Articles_Wizard_State::STATE_STYLE_SELECTION ) === Instant_Articles_Wizard_State::TIMELINE_PAST ) : ?>
60
+ <h4>Style Customized</h4>
61
+ <?php else : ?>
62
+ <h4>Customize Your Style</h4>
63
+ <?php endif; ?>
64
+ <p>Use our Style Editor to make your Instant Articles look just how you want them to.</p>
65
+ </div>
66
+ <div class="instant-articles-card-bullet-step <?php echo esc_attr( $state_css_classes[ Instant_Articles_Wizard_State::STATE_REVIEW_SUBMISSION ] ); ?>">
67
+ <div class="instant-articles-card-bullet"></div>
68
+ <?php if ( Instant_Articles_Wizard_State::get_timeline_position( Instant_Articles_Wizard_State::STATE_REVIEW_SUBMISSION ) === Instant_Articles_Wizard_State::TIMELINE_PAST ) : ?>
69
+ <h4>Review Complete</h4>
70
+ <?php else : ?>
71
+ <h4>Submit for Review</h4>
72
+ <?php endif; ?>
73
+ <p>Submit your Instant Articles for review and start publishing.</p>
74
+ </div>
75
+ </div>
76
+
77
+ <?php endif; ?>
78
+
79
+ <?php
80
+ switch ( $current_state ) {
81
+ case Instant_Articles_Wizard_State::STATE_OVERVIEW:
82
+ include( dirname( __FILE__ ) . '/overview-template.php' );
83
+ break;
84
+ case Instant_Articles_Wizard_State::STATE_APP_SETUP:
85
+ include( dirname( __FILE__ ) . '/app-setup-template.php' );
86
+ break;
87
+ case Instant_Articles_Wizard_State::STATE_PAGE_SELECTION:
88
+ include( dirname( __FILE__ ) . '/page-selection-template.php' );
89
+ break;
90
+ case Instant_Articles_Wizard_State::STATE_STYLE_SELECTION:
91
+ include( dirname( __FILE__ ) . '/style-selection-template.php' );
92
+ break;
93
+ case Instant_Articles_Wizard_State::STATE_REVIEW_SUBMISSION:
94
+ include( dirname( __FILE__ ) . '/review-submission-template.php' );
95
+ break;
96
+ }
97
+ ?>
98
+
99
+ <?php if ( ! $ajax ) : ?>
100
+ </div>
101
+ <?php if ( ! empty( get_settings_errors() ) ) : ?>
102
+ <a class="instant-articles-advanced-settings instant-articles-wizard-toggle" href="#">▼ Advanced Settings</a>
103
+ <div class="instant-articles-wizard-advanced-settings-box" style="display: block;">
104
+ <?php include( dirname( __FILE__ ) . '/advanced-template.php' ); ?>
105
+ </div>
106
+ <?php else: ?>
107
+ <a class="instant-articles-advanced-settings instant-articles-wizard-toggle" href="#">► Advanced Settings</a>
108
+ <div class="instant-articles-wizard-advanced-settings-box" style="display: none;">
109
+ <?php include( dirname( __FILE__ ) . '/advanced-template.php' ); ?>
110
+ </div>
111
+ <?php endif; ?>
112
+ <?php endif; ?>