111 | />
112 | Submit this article even with warnings
113 |
114 |
115 |
116 |
117 |
118 |
119 | This post was transformed into an Instant Article with no warnings
120 | [Toggle debug information]
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
--------------------------------------------------------------------------------
/src/embeds.php:
--------------------------------------------------------------------------------
1 | get_provider( $url );
44 |
45 | $provider_name = false;
46 | if ( false !== strpos( $provider_url, 'instagram.com' ) ) {
47 | $provider_name = 'instagram';
48 | } elseif ( false !== strpos( $provider_url, 'twitter.com' ) ) {
49 | $provider_name = 'twitter';
50 | } elseif ( false !== strpos( $provider_url, 'youtube.com' ) ) {
51 | $provider_name = 'youtube';
52 | } elseif( false !== strpos( $provider_url, 'vimeo.com' ) ) {
53 | $provider_name = 'vimeo';
54 | } elseif( false !== strpos( $provider_url, 'vine.co' ) ) {
55 | $provider_name = 'vine';
56 | } elseif( false !== strpos( $provider_url, 'facebook.com' ) ) {
57 | $provider_name = 'facebook';
58 | }
59 |
60 | $provider_name = apply_filters( 'instant_articles_social_embed_type', $provider_name, $url );
61 |
62 | if ( false === $provider_name ) {
63 | // We cannot properly cache `false`, so let's use a different value we can check for.
64 | set_transient( $cache_key, 'no_provider', HOUR_IN_SECONDS * 12 );
65 | } else {
66 | set_transient( $cache_key, $provider_name );
67 | }
68 | }
69 |
70 | // Change cacheable `'no_provider'` to `false`.
71 | if ( 'no_provider' === $provider_name ) {
72 | $provider_name = false;
73 | }
74 |
75 | if ( $provider_name ) {
76 | $html = instant_articles_embed_get_html( $provider_name, $html, $url, $attr, $post_id );
77 | delete_transient( $cache_key );
78 | }
79 |
80 | return $html;
81 |
82 | }
83 | add_filter( 'embed_oembed_html', 'instant_articles_embed_oembed_html', 10, 4 );
84 |
85 |
86 | /**
87 | * Filter the embed results for embeds.
88 | *
89 | * @since 0.1
90 | * @param string $provider_name The name of the embed provider. E.g. “instagram” or “youtube”.
91 | * @param string $html The original HTML returned from the external oembed/embed provider.
92 | * @param string $url The URL found in the content.
93 | * @param mixed $attr An array with extra attributes.
94 | * @param int $post_id The post ID.
95 | * @return string The filtered HTML.
96 | */
97 | function instant_articles_embed_get_html( $provider_name, $html, $url, $attr, $post_id ) {
98 |
99 | // Don't try to fix embeds unless we're in Instant Articles context.
100 | // This prevents mangled output on frontend.
101 | if ( ! is_transforming_instant_article() ) {
102 | return $html;
103 | }
104 |
105 | /**
106 | * Filter the HTML that will go into the Instant Article Social Embed markup.
107 | *
108 | * @since 0.1
109 | * @param string $html The HTML.
110 | * @param string $url The URL found in the content.
111 | * @param mixed $attr An array with extra attributes.
112 | * @param int $post_id The post ID.
113 | */
114 | $html = apply_filters( "instant_articles_social_embed_{$provider_name}", $html, $url, $attr, $post_id );
115 |
116 | $html = sprintf( '
%s
', $html );
117 |
118 | /**
119 | * Filter the Instant Article Social Embed markup.
120 | *
121 | * @since 0.1
122 | * @param string $html The Social Embed markup.
123 | * @param string $url The URL found in the content.
124 | * @param mixed $attr An array with extra attributes.
125 | * @param int $post_id The post ID.
126 | */
127 | $html = apply_filters( 'instant_articles_social_embed', $html, $url, $attr, $post_id );
128 |
129 | return $html;
130 | }
131 |
--------------------------------------------------------------------------------
/src/wizard/class-instant-articles-option-analytics.php:
--------------------------------------------------------------------------------
1 | 'Analytics',
19 | 'description' => '
Enable 3rd-party analytics to be used with Instant Articles.
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. Learn more about Analytics in Instant Articles.
Select which analytics services you'd like to use with Instant Articles.
109 | $field_value ) {
126 | $field = self::$fields[ $field_id ];
127 |
128 | if ( $field_id === 'embed_code' ) {
129 | if ( isset( $field_values['embed_code_enabled'] ) && $field_values['embed_code_enabled'] ) {
130 | $document = new DOMDocument();
131 | $fragment = $document->createDocumentFragment();
132 | if ( ! @$fragment->appendXML( $field_value ) ) {
133 | add_settings_error(
134 | 'embed_code',
135 | 'invalid_markup',
136 | 'Invalid HTML markup provided for custom analytics tracker code'
137 | );
138 | }
139 | }
140 | }
141 | }
142 |
143 | return $field_values;
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/src/compat/class-instant-articles-jetpack.php:
--------------------------------------------------------------------------------
1 | _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 | /**
22 | * Remove the YouTube embed handling in Jetpack
23 | *
24 | */
25 | private function _fix_youtube_embed() {
26 |
27 | /**
28 | * Do not "fix" bare URLs on their own line of the form
29 | * http://www.youtube.com/v/9FhMMmqzbD8?fs=1&hl=en_US
30 | * as we have oEmbed to handle those
31 | * Registered in jetpack/modules/shortcodes/youtube.php
32 | */
33 | wp_embed_unregister_handler( 'wpcom_youtube_embed_crazy_url' );
34 | }
35 |
36 | /**
37 | * Fix the Facebook embed handling
38 | *
39 | */
40 | private function _fix_facebook_embed() {
41 |
42 | // Don't try to fix facebook embeds unless we're in Instant Articles context.
43 | // This prevents mangled output on frontend.
44 | if ( ! is_transforming_instant_article() ) {
45 | return;
46 | }
47 |
48 | // All of these are registered in jetpack/modules/shortcodes/facebook.php
49 |
50 | if ( defined( 'JETPACK_FACEBOOK_EMBED_REGEX' ) ) {
51 | wp_embed_unregister_handler( 'facebook' );
52 | wp_embed_register_handler( 'facebook', JETPACK_FACEBOOK_EMBED_REGEX, array( __CLASS__, 'facebook_embed_handler' ) );
53 | }
54 | if ( defined( 'JETPACK_FACEBOOK_ALTERNATE_EMBED_REGEX' ) ) {
55 | wp_embed_unregister_handler( 'facebook-alternate' );
56 | wp_embed_register_handler( 'facebook-alternate', JETPACK_FACEBOOK_ALTERNATE_EMBED_REGEX, array( __CLASS__, 'facebook_embed_handler' ) );
57 | }
58 | if ( defined( 'JETPACK_FACEBOOK_PHOTO_EMBED_REGEX' ) ) {
59 | wp_embed_unregister_handler( 'facebook-photo' );
60 | wp_embed_register_handler( 'facebook-photo', JETPACK_FACEBOOK_PHOTO_EMBED_REGEX, array( __CLASS__, 'facebook_embed_handler' ) );
61 | }
62 | if ( defined( 'JETPACK_FACEBOOK_PHOTO_ALTERNATE_EMBED_REGEX' ) ) {
63 | wp_embed_unregister_handler( 'facebook-alternate-photo' );
64 | wp_embed_register_handler( 'facebook-alternate-photo', JETPACK_FACEBOOK_PHOTO_ALTERNATE_EMBED_REGEX, array( __CLASS__, 'facebook_embed_handler' ) );
65 | }
66 | if ( defined( 'JETPACK_FACEBOOK_VIDEO_EMBED_REGEX' ) ) {
67 | wp_embed_unregister_handler( 'facebook-video' );
68 | wp_embed_register_handler( 'facebook-video', JETPACK_FACEBOOK_VIDEO_EMBED_REGEX, array( __CLASS__, 'facebook_embed_handler' ) );
69 | }
70 | if ( defined( 'JETPACK_FACEBOOK_VIDEO_ALTERNATE_EMBED_REGEX' ) ) {
71 | wp_embed_unregister_handler( 'facebook-alternate-video' );
72 | wp_embed_register_handler( 'facebook-alternate-video', JETPACK_FACEBOOK_VIDEO_ALTERNATE_EMBED_REGEX, array( __CLASS__, 'facebook_embed_handler' ) );
73 | }
74 | }
75 |
76 | public static function facebook_embed_handler( $matches, $attr, $url ) {
77 |
78 | $locale = get_locale();
79 |
80 | // Source: https://www.facebook.com/translations/FacebookLocales.xml
81 | $fb_locales = array( 'af_ZA', 'ak_GH', 'am_ET', 'ar_AR', 'as_IN', 'ay_BO', 'az_AZ', 'be_BY', 'bg_BG', 'bn_IN', 'br_FR', 'bs_BA', 'ca_ES', 'cb_IQ', 'ck_US', 'co_FR', 'cs_CZ', 'cx_PH', 'cy_GB', 'da_DK', 'de_DE', 'el_GR', 'en_GB', 'en_IN', 'en_PI', 'en_UD', 'en_US', 'eo_EO', 'es_CL', 'es_CO', 'es_ES', 'es_LA', 'es_MX', 'es_VE', 'et_EE', 'eu_ES', 'fa_IR', 'fb_LT', 'ff_NG', 'fi_FI', 'fo_FO', 'fr_CA', 'fr_FR', 'fy_NL', 'ga_IE', 'gl_ES', 'gn_PY', 'gu_IN', 'gx_GR', 'ha_NG', 'he_IL', 'hi_IN', 'hr_HR', 'ht_HT', 'hu_HU', 'hy_AM', 'id_ID', 'ig_NG', 'is_IS', 'it_IT', 'ja_JP', 'ja_KS', 'jv_ID', 'ka_GE', 'kk_KZ', 'km_KH', 'kn_IN', 'ko_KR', 'ku_TR', 'ky_KG', 'la_VA', 'lg_UG', 'li_NL', 'ln_CD', 'lo_LA', 'lt_LT', 'lv_LV', 'mg_MG', 'mi_NZ', 'mk_MK', 'ml_IN', 'mn_MN', 'mr_IN', 'ms_MY', 'mt_MT', 'my_MM', 'nb_NO', 'nd_ZW', 'ne_NP', 'nl_BE', 'nl_NL', 'nn_NO', 'ny_MW', 'or_IN', 'pa_IN', 'pl_PL', 'ps_AF', 'pt_BR', 'pt_PT', 'qc_GT', 'qu_PE', 'rm_CH', 'ro_RO', 'ru_RU', 'rw_RW', 'sa_IN', 'sc_IT', 'se_NO', 'si_LK', 'sk_SK', 'sl_SI', 'sn_ZW', 'so_SO', 'sq_AL', 'sr_RS', 'sv_SE', 'sw_KE', 'sy_SY', 'sz_PL', 'ta_IN', 'te_IN', 'tg_TJ', 'th_TH', 'tk_TM', 'tl_PH', 'tl_ST', 'tr_TR', 'tt_RU', 'tz_MA', 'uk_UA', 'ur_PK', 'uz_UZ', 'vi_VN', 'wo_SN', 'xh_ZA', 'yi_DE', 'yo_NG', 'zh_CN', 'zh_HK', 'zh_TW', 'zu_ZA', 'zz_TR' );
82 |
83 | // If our locale isn’t supported by Facebook, we’ll fall back to en_US
84 | if ( ! in_array( $locale, $fb_locales, true ) ) {
85 | $locale = 'en_US';
86 | }
87 |
88 | return '';
89 | }
90 |
91 | public static function transformer_loaded( $transformer ) {
92 | // Appends more rules to transformer
93 | $file_path = __DIR__ . '/jetpack-rules-configuration.json';
94 | $configuration = file_get_contents( $file_path );
95 | $transformer->loadRules( $configuration );
96 |
97 | return $transformer;
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/wizard/class-instant-articles-option-publishing.php:
--------------------------------------------------------------------------------
1 | 'Publishing Settings',
19 | );
20 |
21 | public static $fields = array(
22 |
23 | 'dev_mode' => array(
24 | 'label' => 'Development Mode',
25 | '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 one-time review.',
26 | 'render' => 'checkbox',
27 | 'default' => false,
28 | 'checkbox_label' => 'Enable development mode',
29 | ),
30 |
31 | 'custom_rules_enabled' => array(
32 | 'label' => 'Custom transformer rules',
33 | 'render' => 'checkbox',
34 | 'checkbox_label' => 'Enable custom transformer rules',
35 | 'description' => 'Define your own rules to customize the transformation of your content into Instant Articles',
36 | 'default' => '',
37 | ),
38 |
39 | 'custom_rules' => array(
40 | 'label' => '',
41 | 'render' => 'textarea',
42 | 'placeholder' => '{ "rules": [{ "class": "BoldRule", "selector": "span.bold" }, ... ] }',
43 | 'description' => 'Read more about defining your own custom rules to extend/override the built-in ruleset. If you\'ve defined a common rule which you think this plugin should include by default, tell us about it!',
44 | 'default' => '',
45 | ),
46 |
47 | 'publish_with_warnings' => array(
48 | 'label' => 'Transformation warnings',
49 | 'description' => 'With this option disabled, articles which contain warnings in their transformation process won\'t be available as Instant Articles by default — this can be overridden on individual articles. Note: It is recommended that all transformation warnings be fixed.',
50 | 'render' => 'checkbox',
51 | 'default' => false,
52 | 'checkbox_label' => 'Publish articles containing warnings',
53 | ),
54 |
55 | 'display_warning_column' => array(
56 | 'label' => 'FB IA Status column',
57 | 'description' => 'With this option enabled, a column will be added to post indexes to quickly see whether an article transformation failed, succeeded, or had warnings.',
58 | 'render' => 'checkbox',
59 | 'default' => false,
60 | 'checkbox_label' => 'Enable column "FB IA Status"',
61 | )
62 |
63 | );
64 |
65 | /**
66 | * Constructor.
67 | *
68 | * @since 0.4
69 | */
70 | public function __construct() {
71 | parent::__construct(
72 | self::OPTION_KEY,
73 | self::$sections,
74 | self::$fields
75 | );
76 | wp_localize_script( 'instant-articles-option-publishing', 'INSTANT_ARTICLES_OPTION_PUBLISHING', array(
77 | 'option_field_id_custom_rules_enabled' => self::OPTION_KEY . '-custom_rules_enabled',
78 | 'option_field_id_custom_rules' => self::OPTION_KEY . '-custom_rules',
79 | ) );
80 | }
81 |
82 | /**
83 | * Sanitize and return all the field values.
84 | *
85 | * This method receives a payload containing all value for its fields and
86 | * should return the same payload after having been sanitized.
87 | *
88 | * Do not encode the payload as this is performed by the
89 | * universal_sanitize_and_encode_handler() of the parent class.
90 | *
91 | * @param array $field_values array map with key field_id => value.
92 | * @since 0.5
93 | */
94 | public function sanitize_option_fields( $field_values ) {
95 | foreach ( $field_values as $field_id => $field_value ) {
96 | $field = self::$fields[ $field_id ];
97 |
98 | switch ( $field_id ) {
99 | case 'dev_mode':
100 | $field_values[ $field_id ] = $field_value
101 | ? '1'
102 | : (string) $field['default'];
103 | break;
104 |
105 | case 'custom_rules':
106 | if ( isset( $field_values['custom_rules_enabled'] ) && $field_values['custom_rules_enabled'] ) {
107 | $custom_rules_json = json_decode( $field_values['custom_rules'] );
108 | if ( null === $custom_rules_json ) {
109 | $field_values['custom_rules'] = $field['default'];
110 | add_settings_error(
111 | 'custom_embed',
112 | 'invalid_json',
113 | 'Invalid JSON provided for custom rules code'
114 | );
115 | }
116 | }
117 | break;
118 |
119 | default:
120 | // Should never happen.
121 | break;
122 | }
123 | }
124 |
125 | return $field_values;
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/src/class-instant-articles-amp-markup.php:
--------------------------------------------------------------------------------
1 | should_submit_post() ) {
65 | return;
66 | }
67 |
68 | $url = $adapter->get_canonical_url();
69 | $url = add_query_arg( self::QUERY_ARG, '1', $url );
70 |
71 | ?>
72 |
73 | $post->ID,
116 | 'post_type' => 'attachment',
117 | 'numberposts' => 100,
118 | 'post_mime_type' => 'image',
119 | ];
120 | $image_children = get_children( $query_args );
121 |
122 | foreach ( $image_children as $img_id => $img ) {
123 | $meta = wp_get_attachment_metadata( $img_id );
124 |
125 | // Removes the file name from the URL
126 | $url_chunks = explode( '/', $img->guid, -1 );
127 | $base_image_url = implode( '/', $url_chunks ) . '/';
128 |
129 | // This is the uploaded original file
130 | $media_sizes[ $img->guid ] = [ $meta['width'], $meta['height'] ];
131 |
132 | // These are the possible redimensions
133 | foreach ( $meta['sizes'] as $size ) {
134 | $size_url = $base_image_url . $size['file'];
135 | $media_sizes[ $size_url ] = [ $size['width'], $size['height'] ];
136 | }
137 | }
138 |
139 | $properties[ AMPArticle::MEDIA_SIZES_KEY ] = $media_sizes;
140 |
141 | // Transform the post to an Instant Article.
142 | $adapter = new Instant_Articles_Post( $post );
143 | $article = $adapter->to_instant_article();
144 | $article_html = $article->render();
145 | $amp = AMPArticle::create( $article_html, $properties );
146 | echo $amp->render();
147 |
148 | die();
149 | }
150 |
151 | /**
152 | * Helper function to validate the json string
153 | *
154 | * @param $json_str string JSON string
155 | *
156 | * @return bool true for valid JSON
157 | * @since 4.0
158 | */
159 | public static function validate_json( $json_str ) {
160 | if ( Type::isTextEmpty( $json_str ) ) {
161 | return false;
162 | }
163 |
164 | json_decode( $json_str );
165 |
166 | return json_last_error() == JSON_ERROR_NONE;
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/bin/install-wp-tests.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | if [ $# -lt 3 ]; then
4 | echo "usage: $0 [db-host] [wp-version] [skip-database-creation]"
5 | exit 1
6 | fi
7 |
8 | DB_NAME=$1
9 | DB_USER=$2
10 | DB_PASS=$3
11 | DB_HOST=${4-localhost}
12 | if [ -z "${WP_VERSION}" ]; then
13 | WP_VERSION=${5-latest}
14 | fi
15 | SKIP_DB_CREATE=${6-false}
16 |
17 | TMPDIR=${TMPDIR-/tmp}
18 | TMPDIR=$(echo $TMPDIR | sed -e "s/\/$//")
19 | WP_TESTS_DIR=${WP_TESTS_DIR-$TMPDIR/wordpress-tests-lib}
20 | WP_CORE_DIR=${WP_CORE_DIR-$TMPDIR/wordpress/}
21 |
22 | download() {
23 | if [ `which curl` ]; then
24 | curl -s "$1" > "$2";
25 | elif [ `which wget` ]; then
26 | wget -nv -O "$2" "$1"
27 | fi
28 | }
29 |
30 | if [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+\-(beta|RC)[0-9]+$ ]]; then
31 | WP_BRANCH=${WP_VERSION%\-*}
32 | WP_TESTS_TAG="branches/$WP_BRANCH"
33 |
34 | elif [[ $WP_VERSION =~ ^[0-9]+\.[0-9]+$ ]]; then
35 | WP_TESTS_TAG="branches/$WP_VERSION"
36 | elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0-9]+ ]]; then
37 | if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then
38 | # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x
39 | WP_TESTS_TAG="tags/${WP_VERSION%??}"
40 | else
41 | WP_TESTS_TAG="tags/$WP_VERSION"
42 | fi
43 | elif [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
44 | WP_TESTS_TAG="trunk"
45 | else
46 | # http serves a single offer, whereas https serves multiple. we only want one
47 | download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json
48 | grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json
49 | LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//')
50 | if [[ -z "$LATEST_VERSION" ]]; then
51 | echo "Latest WordPress version could not be found"
52 | exit 1
53 | fi
54 | WP_TESTS_TAG="tags/$LATEST_VERSION"
55 | fi
56 | set -ex
57 |
58 | install_wp() {
59 |
60 | if [ -d $WP_CORE_DIR ]; then
61 | return;
62 | fi
63 |
64 | mkdir -p $WP_CORE_DIR
65 |
66 | if [[ $WP_VERSION == 'nightly' || $WP_VERSION == 'trunk' ]]; then
67 | mkdir -p $TMPDIR/wordpress-nightly
68 | download https://wordpress.org/nightly-builds/wordpress-latest.zip $TMPDIR/wordpress-nightly/wordpress-nightly.zip
69 | unzip -q $TMPDIR/wordpress-nightly/wordpress-nightly.zip -d $TMPDIR/wordpress-nightly/
70 | mv $TMPDIR/wordpress-nightly/wordpress/* $WP_CORE_DIR
71 | else
72 | if [ $WP_VERSION == 'latest' ]; then
73 | local ARCHIVE_NAME='latest'
74 | elif [[ $WP_VERSION =~ [0-9]+\.[0-9]+ ]]; then
75 | # https serves multiple offers, whereas http serves single.
76 | download https://api.wordpress.org/core/version-check/1.7/ $TMPDIR/wp-latest.json
77 | if [[ $WP_VERSION =~ [0-9]+\.[0-9]+\.[0] ]]; then
78 | # version x.x.0 means the first release of the major version, so strip off the .0 and download version x.x
79 | LATEST_VERSION=${WP_VERSION%??}
80 | else
81 | # otherwise, scan the releases and get the most up to date minor version of the major release
82 | local VERSION_ESCAPED=`echo $WP_VERSION | sed 's/\./\\\\./g'`
83 | LATEST_VERSION=$(grep -o '"version":"'$VERSION_ESCAPED'[^"]*' $TMPDIR/wp-latest.json | sed 's/"version":"//' | head -1)
84 | fi
85 | if [[ -z "$LATEST_VERSION" ]]; then
86 | local ARCHIVE_NAME="wordpress-$WP_VERSION"
87 | else
88 | local ARCHIVE_NAME="wordpress-$LATEST_VERSION"
89 | fi
90 | else
91 | local ARCHIVE_NAME="wordpress-$WP_VERSION"
92 | fi
93 | download https://wordpress.org/${ARCHIVE_NAME}.tar.gz $TMPDIR/wordpress.tar.gz
94 | tar --strip-components=1 -zxmf $TMPDIR/wordpress.tar.gz -C $WP_CORE_DIR
95 | fi
96 |
97 | download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php
98 | }
99 |
100 | install_test_suite() {
101 | # portable in-place argument for both GNU sed and Mac OSX sed
102 | if [[ $(uname -s) == 'Darwin' ]]; then
103 | local ioption='-i.bak'
104 | else
105 | local ioption='-i'
106 | fi
107 |
108 | # set up testing suite if it doesn't yet exist
109 | if [ ! -d $WP_TESTS_DIR ]; then
110 | # set up testing suite
111 | mkdir -p $WP_TESTS_DIR
112 | svn co --quiet --ignore-externals https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes
113 | svn co --quiet --ignore-externals https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/data/ $WP_TESTS_DIR/data
114 | fi
115 |
116 | if [ ! -f wp-tests-config.php ]; then
117 | download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php
118 | # remove all forward slashes in the end
119 | WP_CORE_DIR=$(echo $WP_CORE_DIR | sed "s:/\+$::")
120 | sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR/':" "$WP_TESTS_DIR"/wp-tests-config.php
121 | sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php
122 | sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php
123 | sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php
124 | sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php
125 | fi
126 |
127 | }
128 |
129 | install_db() {
130 |
131 | if [ ${SKIP_DB_CREATE} = "true" ]; then
132 | return 0
133 | fi
134 |
135 | # parse DB_HOST for port or socket references
136 | local PARTS=(${DB_HOST//\:/ })
137 | local DB_HOSTNAME=${PARTS[0]};
138 | local DB_SOCK_OR_PORT=${PARTS[1]};
139 | local EXTRA=""
140 |
141 | if ! [ -z $DB_HOSTNAME ] ; then
142 | if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then
143 | EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp"
144 | elif ! [ -z $DB_SOCK_OR_PORT ] ; then
145 | EXTRA=" --socket=$DB_SOCK_OR_PORT"
146 | elif ! [ -z $DB_HOSTNAME ] ; then
147 | EXTRA=" --host=$DB_HOSTNAME --protocol=tcp"
148 | fi
149 | fi
150 |
151 | # create database
152 | mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA
153 | }
154 |
155 | install_wp
156 | install_test_suite
157 | install_db
158 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/Automattic/fb-instant-articles/actions/workflows/cs-lint.yml) [](https://github.com/Automattic/fb-instant-articles/actions/workflows/tests.yml) [](https://github.com/Automattic/fb-instant-articles/actions/workflows/stale.yml)
2 | # Instant Articles for WP
3 |
4 | Enable [Instant Articles for Facebook](https://developers.facebook.com/docs/instant-articles) on your WordPress site.
5 |
6 | ## Maintenance 🚩
7 | Support for this plugin is provided for PHP 8 migrations, but both Automattic/WPVIP and Facebook/Meta have stopped all active development. At this point in the product lifecycle, publishers should be advised to stop using this plugin, and instead, use direct site traffic for Facebook content.
8 | Meta have announced that Instant Articles will have all support dropped at April 2023.
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.
13 |
14 | With the plugin active, a new menu will be available for you to connect to your Facebook Page and start publishing your Instant Articles. You'll also see the status of each Instant Articles submission on the edit page of your posts.
15 |
16 | A best effort is made to generate valid Instant Article markup from your posts' content/metadata and publish it to Facebook. The plugin knows how to transform your posts' markup from a set of rules which forms a mapping between elements in you *source markup* and the valid *Instant Article components*. We refer to this “glue” between the two as the ***Transformer Rules***.
17 |
18 | Built-in to the plugin are many [pre-defined transformer rules](https://github.com/Automattic/facebook-instant-articles-wp/blob/master/rules-configuration.json) which aims to cover standard WordPress installations. If your WordPress content contains elements which are not covered by the built-in ruleset, you can define your own additional rules to extend or override the defaults in the Settings of this plugin, under: **Plugin Configuration** > **Publishing Settings** > **Custom transformer rules**.
19 |
20 | ## Access to Instant Articles
21 |
22 | The current criteria for access to Instant Articles are:
23 |
24 | - your Facebook Page must have an established presence of at least 90 days
25 | - your content adheres to the [Instant Article Policies](https://developers.facebook.com/docs/instant-articles/policy/)
26 |
27 | Before your Instant Articles can be published on Facebook, your feed must undergo an initial review and approval. Facebook requires a minimum number of 10 articles in your feed before being eligible for review. The review process checks that your draft Instant Articles are properly formatted, have content consistency with their mobile web counterparts, and adhere to the [community standards](https://www.facebook.com/communitystandards/) and [content policies](https://www.facebook.com/help/publisher/1348682518563619).
28 |
29 | It's important to note that if you use meta fields to add extra text, images or videos to your Posts, Facebook will expect you to add these to your Instant Articles output too. This plugin includes hooks to help you do that.
30 |
31 | [See Facebook's documentation for full details of the submission process.](https://developers.facebook.com/docs/instant-articles)
32 |
33 | Once your feed has been approved, new posts will automatically be taken live on Instant Articles, and existing posts will be taken live once you update them. In order to stay in the Instant Articles program, you must remain active by creating new content and maintaining a minimal readership.
34 |
35 | ## Installation
36 |
37 | 1. Run `composer install` on the root of the plugin folder. Make sure you have [Composer](https://github.com/composer/composer) installed.
38 | 2. Upload the folder to the `/wp-content/plugins/` directory
39 | 3. Activate the plugin through the 'Plugins' menu in WordPress
40 |
41 | ## Frequently Asked Questions
42 |
43 | **Why is there content from my post missing in the generated Instant Article?**
44 |
45 | More likely than not, this is because there is markup in the body of your post that is not mapped to a recognized Instant Article component. On the “Edit Post” screen for your post, look for additional information about the *transformed* output shown within the **Facebook Instant Articles** module located at the bottom of the screen.
46 |
47 | **Why doesn't my post appear in the list of Instant Articles in the publisher tools?**
48 | Your posts are imported to your library when they are shared on Facebook for the first time.
49 |
50 | Alternatively, you can trigger a manual scrape by pasting your URL on our [Share Debugger](http://developers.facebook.com/tools/debug)
51 |
52 | Only Instant Articles with URLs in [domains you have claimed](https://developers.facebook.com/docs/instant-articles/guides/publishertools#connect) will show up in the Publishing Tools section.
53 | **In the Instant Articles module for my post, what does the “This post was transformed into an Instant Article with some warnings” message mean?**
54 |
55 | When transforming your post into an Instant Article, this plugin will show warnings when it encounters content which might not be valid when published to Facebook. When you see this message, it is recommended to resolve each warning individually.
56 |
57 | **What does the “No rules defined for ____ in the context of ____” warning mean?**
58 |
59 | This plugin transforms your post into an Instant Article by matching markup in your content to one of the [components available](https://github.com/facebook/facebook-instant-articles-sdk-php/blob/master/docs/QuickStart.md#transformer-classes) in Instant Articles markup. Although the plugin contains many [built-in rules](https://github.com/Automattic/facebook-instant-articles-wp/blob/master/rules-configuration.json) to handle common cases, there may be markup in your post which is not recognized by these existing rules. In this case, you may be required to define some of your own rules. See below for more details about where and how.
60 |
61 | **How do I define my own transformer rules so that content from my site is rendered appropriately in an Instant Article?**
62 |
63 | Your custom rules can be defined in the Settings of this plugin, under: **Plugin Configuration** > **Publishing Settings** > **Custom transformer rules**. More detailed instructions about all the options available is documented in the [Custom Transformer Rules](https://github.com/facebook/facebook-instant-articles-sdk-php/blob/master/docs/QuickStart.md#custom-transformer-rules) section of the Facebook Instant Articles SDK.
64 |
65 | **I know of a custom transformer rule which is pretty common in the community. How can it be included by default in the plugin?**
66 |
67 | You can propose popular transformer rules to be included in the plugin by [suggesting it on GitHub](https://github.com/Automattic/facebook-instant-articles-wp/issues/new).
68 |
69 | **How do I post articles to Instant Articles after plugin is installed?**
70 |
71 | In order to import your posts to your Instant Articles library on Facebook you need to [Connect Your Site](https://developers.facebook.com/docs/instant-articles/guides/publishertools#connect) first. Then either share your posts on your page or use the [Sharing Debugger](https://developers.intern.facebook.com/tools/debug/sharing/) to scrape them. After you have 10 articles imported, you will be able to submit them for review.
72 |
73 | **How do I change the feed slug/URL if I'm using the RSS integration?**
74 |
75 | To change the feed slug, set the constant INSTANT_ARTICLES_SLUG to whatever you like. If you do, remember to flush the rewrite rules afterwards.
76 | By default it is set to `instant-articles` which usually will give you a feed URL set to `/feed/instant-articles`
77 |
78 | **How do I flush the rewrite rules after changing the feed slug?**
79 |
80 | Usually simply visiting the permalinks settings page in the WordPress dashboard will do the trick (/wp-admin/options-permalink.php)
81 |
82 | ## Changelog
83 |
84 | Please visit the [changelog](https://github.com/Automattic/fb-instant-articles/blob/develop/CHANGELOG.md).
85 |
--------------------------------------------------------------------------------
/src/wizard/class-instant-articles-option-ads.php:
--------------------------------------------------------------------------------
1 | 'Ads',
19 | 'description' => '
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 advertising in Instant Articles.