From dd64aa5c8d71bf3add74dc5b5129260fa2722dc0 Mon Sep 17 00:00:00 2001 From: William Tam Date: Wed, 20 May 2020 16:43:41 +0000 Subject: [PATCH 01/23] import externalAssets from a story into postmeta --- classes/NPRAPIWordpress.php | 28 ++++++++++++++++++++++++++++ ds-npr-api.php | 2 ++ 2 files changed, 30 insertions(+) diff --git a/classes/NPRAPIWordpress.php b/classes/NPRAPIWordpress.php index 3a0f328..d8bee77 100644 --- a/classes/NPRAPIWordpress.php +++ b/classes/NPRAPIWordpress.php @@ -244,6 +244,34 @@ function update_posts_from_stories( $publish = TRUE, $qnum = false ) { $metas[NPR_AUDIO_META_KEY] = implode( ',', $mp3_array ); $metas[NPR_AUDIO_M3U_META_KEY] = implode( ',', $m3u_array ); } + //get exernal assets like youtube + if (isset($story->externalAsset) ) { + $oembeds_array = array(); + if (isset($story->externalAsset->type)) { + $oembeds_array[] = $story->externalAsset; + } else { + // sometimes there are multiple objects + foreach ( (array) $story->externalAsset as $extasset ) { + if (isset($extasset->type)) { + $oembeds_array[] = $extasset; + } + } + } + // parse those external assets into sub-arrays by type + foreach ($oembeds_array as $embed) { + if (!empty($embed->type)) { + $embed_type = strtolower($embed->type); + unset($embed->type); + unset($embed->id); + $translated_embed = array(); + foreach ( (array) $embed as $k => $v) { + $translated_embed[$k] = $v->value; + } + $metas[NPR_OEMBED_META_KEY_PREFIX . $embed_type][] = $translated_embed; + } + } + } + if ( $existing ) { $created = false; $args[ 'ID' ] = $existing->ID; diff --git a/ds-npr-api.php b/ds-npr-api.php index a2a4282..36ee8ac 100644 --- a/ds-npr-api.php +++ b/ds-npr-api.php @@ -43,6 +43,8 @@ define( 'NPR_IMAGE_AGENCY_META_KEY', 'npr_image_agency'); define( 'NPR_IMAGE_CAPTION_META_KEY', 'npr_image_caption'); +define( 'NPR_OEMBED_META_KEY_PREFIX', 'npr_ds_oembeds_'); + define( 'NPR_PUSH_STORY_ERROR', 'npr_push_story_error'); define( 'NPR_MAX_QUERIES', 10 ); From 94df1411f880a8e9925fc1fef56cd1e26e76613d Mon Sep 17 00:00:00 2001 From: William Tam Date: Wed, 20 May 2020 17:07:41 +0000 Subject: [PATCH 02/23] comment typo fix --- classes/NPRAPIWordpress.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/classes/NPRAPIWordpress.php b/classes/NPRAPIWordpress.php index d8bee77..8839f44 100644 --- a/classes/NPRAPIWordpress.php +++ b/classes/NPRAPIWordpress.php @@ -244,7 +244,7 @@ function update_posts_from_stories( $publish = TRUE, $qnum = false ) { $metas[NPR_AUDIO_META_KEY] = implode( ',', $mp3_array ); $metas[NPR_AUDIO_M3U_META_KEY] = implode( ',', $m3u_array ); } - //get exernal assets like youtube + //get external assets like youtube if (isset($story->externalAsset) ) { $oembeds_array = array(); if (isset($story->externalAsset->type)) { @@ -272,6 +272,7 @@ function update_posts_from_stories( $publish = TRUE, $qnum = false ) { } } + if ( $existing ) { $created = false; $args[ 'ID' ] = $existing->ID; From 7df7e0b0e056c86acd5939fc7f7d8bfbead733c6 Mon Sep 17 00:00:00 2001 From: William Tam Date: Wed, 20 May 2020 18:16:31 +0000 Subject: [PATCH 03/23] append htmlAssets to end of post body --- classes/NPRAPIWordpress.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/classes/NPRAPIWordpress.php b/classes/NPRAPIWordpress.php index 8839f44..c9667a9 100644 --- a/classes/NPRAPIWordpress.php +++ b/classes/NPRAPIWordpress.php @@ -272,6 +272,23 @@ function update_posts_from_stories( $publish = TRUE, $qnum = false ) { } } + // get htmlAssets -- typically interactives -- and append to the story body + if (isset($story->htmlAsset) ) { + $htmls_array = array(); + if (isset($story->htmlAsset->id)) { + $story->body .= $story->htmlAsset->value; + } else { + // sometimes there are multiple objects + foreach ( (array) $story->htmlAsset as $hasset ) { + if (isset($hasset->id)) { + $story->body .= $hasset->value; + } + } + } + } + + + if ( $existing ) { $created = false; From 98cfbeae218419bcb7943521f44a89c88933f9e8 Mon Sep 17 00:00:00 2001 From: William Tam Date: Tue, 2 Jun 2020 20:25:18 +0000 Subject: [PATCH 04/23] put htmlAssets into a postmeta npr_html_assets INSTEAD of appending to end of body --- classes/NPRAPIWordpress.php | 8 ++++---- ds-npr-api.php | 1 + 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/classes/NPRAPIWordpress.php b/classes/NPRAPIWordpress.php index c9667a9..e546239 100644 --- a/classes/NPRAPIWordpress.php +++ b/classes/NPRAPIWordpress.php @@ -273,20 +273,20 @@ function update_posts_from_stories( $publish = TRUE, $qnum = false ) { } // get htmlAssets -- typically interactives -- and append to the story body + $html_assets = ''; if (isset($story->htmlAsset) ) { - $htmls_array = array(); if (isset($story->htmlAsset->id)) { - $story->body .= $story->htmlAsset->value; + $html_assets .= $story->htmlAsset->value; } else { // sometimes there are multiple objects foreach ( (array) $story->htmlAsset as $hasset ) { if (isset($hasset->id)) { - $story->body .= $hasset->value; + $html_assets .= $hasset->value; } } } } - + $metas[NPR_HTML_ASSETS_META_KEY] = $html_assets; diff --git a/ds-npr-api.php b/ds-npr-api.php index 36ee8ac..dd66499 100644 --- a/ds-npr-api.php +++ b/ds-npr-api.php @@ -32,6 +32,7 @@ define( 'NPR_BYLINE_LINK_META_KEY', 'npr_byline_link' ); define( 'NPR_MULTI_BYLINE_META_KEY', 'npr_multi_byline' ); define( 'NPR_IMAGE_GALLERY_META_KEY', 'npr_image_gallery'); +define( 'NPR_HTML_ASSETS_META_KEY', 'npr_html_assets'); define( 'NPR_AUDIO_META_KEY', 'npr_audio'); define( 'NPR_AUDIO_M3U_META_KEY', 'npr_audio_m3u'); define( 'NPR_PUB_DATE_META_KEY', 'npr_pub_date'); From 6b37c5be92f0b50752ef677bbeaf920918d7615b Mon Sep 17 00:00:00 2001 From: William Tam Date: Mon, 27 Jul 2020 16:08:25 +0000 Subject: [PATCH 05/23] create a use npr layout setting and make it available to update stories function --- classes/NPRAPIWordpress.php | 1 + settings.php | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/classes/NPRAPIWordpress.php b/classes/NPRAPIWordpress.php index e546239..bb05de3 100644 --- a/classes/NPRAPIWordpress.php +++ b/classes/NPRAPIWordpress.php @@ -102,6 +102,7 @@ function update_posts_from_stories( $publish = TRUE, $qnum = false ) { if ( empty( $pull_post_type ) ) { $pull_post_type = 'post'; } + $use_npr_layout = !empty(get_option( 'dp_npr_query_use_layout' )) ? TRUE : FALSE; $post_id = null; diff --git a/settings.php b/settings.php index 7778224..067916a 100644 --- a/settings.php +++ b/settings.php @@ -91,9 +91,15 @@ function nprstory_settings_init() { add_settings_field( 'dp_npr_query_run_multi', 'Run the queries on saving changes', 'nprstory_query_run_multi_callback', 'ds_npr_api_get_multi_settings', 'ds_npr_api_get_multi_settings' ); register_setting( 'ds_npr_api_get_multi_settings', 'dp_npr_query_run_multi' , 'nprstory_validation_callback_checkbox'); + add_settings_field( 'dp_npr_query_multi_cron_interval', 'Interval to run Get Multi cron', 'nprstory_query_multi_cron_interval_callback', 'ds_npr_api_get_multi_settings', 'ds_npr_api_get_multi_settings' ); register_setting( 'ds_npr_api_get_multi_settings', 'dp_npr_query_multi_cron_interval', 'intval' ); + + add_settings_field( 'dp_npr_query_use_layout', 'Use rich layout on pulled posts if available', 'nprstory_query_use_layout_callback', 'ds_npr_api_get_multi_settings', 'ds_npr_api_get_multi_settings' ); + register_setting( 'ds_npr_api_get_multi_settings', 'dp_npr_query_use_layout' , 'nprstory_validation_callback_checkbox'); + + add_settings_field( 'ds_npr_pull_post_type', 'NPR Pull Post Type', 'nprstory_pull_post_type_callback', 'ds_npr_api', 'ds_npr_api_settings' ); register_setting( 'ds_npr_api', 'ds_npr_pull_post_type', 'nprstory_validation_callback_select' ); @@ -151,6 +157,19 @@ function nprstory_query_run_multi_callback() { wp_nonce_field( 'nprstory_nonce_ds_npr_query_run_multi', 'nprstory_nonce_ds_npr_query_run_multi_name', true, true ); } +function nprstory_query_use_layout_callback() { + $use_layout = get_option('dp_npr_query_use_layout'); + $check_box_string = "If layout is available will render any YouTube, Tweets, images, or JavaScript-based widgets within the post in the order they appear. CAUTION: This will allow the 'admin' user to post unfiltered content such as JavaScript.

"; + wp_nonce_field( 'nprstory_nonce_ds_npr_query_use_layout', 'nprstory_nonce_ds_npr_query_use_layout_name', true, true ); +} + function nprstory_query_multi_cron_interval_callback() { $option = get_option( 'dp_npr_query_multi_cron_interval' ); echo "

How often, in minutes, should the Get Multi function run? (default = 60)"; From c2634541b7355d41f03f4b21cd49ea5cd09eb6a3 Mon Sep 17 00:00:00 2001 From: William Tam Date: Mon, 27 Jul 2020 16:10:01 +0000 Subject: [PATCH 06/23] remove unused code for importing externalAssets and htmlAssets as metafields --- classes/NPRAPIWordpress.php | 44 ------------------------------------- 1 file changed, 44 deletions(-) diff --git a/classes/NPRAPIWordpress.php b/classes/NPRAPIWordpress.php index bb05de3..cdadd9f 100644 --- a/classes/NPRAPIWordpress.php +++ b/classes/NPRAPIWordpress.php @@ -245,50 +245,6 @@ function update_posts_from_stories( $publish = TRUE, $qnum = false ) { $metas[NPR_AUDIO_META_KEY] = implode( ',', $mp3_array ); $metas[NPR_AUDIO_M3U_META_KEY] = implode( ',', $m3u_array ); } - //get external assets like youtube - if (isset($story->externalAsset) ) { - $oembeds_array = array(); - if (isset($story->externalAsset->type)) { - $oembeds_array[] = $story->externalAsset; - } else { - // sometimes there are multiple objects - foreach ( (array) $story->externalAsset as $extasset ) { - if (isset($extasset->type)) { - $oembeds_array[] = $extasset; - } - } - } - // parse those external assets into sub-arrays by type - foreach ($oembeds_array as $embed) { - if (!empty($embed->type)) { - $embed_type = strtolower($embed->type); - unset($embed->type); - unset($embed->id); - $translated_embed = array(); - foreach ( (array) $embed as $k => $v) { - $translated_embed[$k] = $v->value; - } - $metas[NPR_OEMBED_META_KEY_PREFIX . $embed_type][] = $translated_embed; - } - } - } - - // get htmlAssets -- typically interactives -- and append to the story body - $html_assets = ''; - if (isset($story->htmlAsset) ) { - if (isset($story->htmlAsset->id)) { - $html_assets .= $story->htmlAsset->value; - } else { - // sometimes there are multiple objects - foreach ( (array) $story->htmlAsset as $hasset ) { - if (isset($hasset->id)) { - $html_assets .= $hasset->value; - } - } - } - } - $metas[NPR_HTML_ASSETS_META_KEY] = $html_assets; - if ( $existing ) { From d0317c0c0820028d9324d6126f15c4ca7cd07bc6 Mon Sep 17 00:00:00 2001 From: William Tam Date: Mon, 27 Jul 2020 21:48:13 +0000 Subject: [PATCH 07/23] process the textWithHtml, images, externalAssets, and htmlAssets and return a formatted body --- classes/NPRAPIWordpress.php | 200 +++++++++++++++++++++++++++++++++++- 1 file changed, 199 insertions(+), 1 deletion(-) diff --git a/classes/NPRAPIWordpress.php b/classes/NPRAPIWordpress.php index cdadd9f..41e0aa5 100644 --- a/classes/NPRAPIWordpress.php +++ b/classes/NPRAPIWordpress.php @@ -139,7 +139,16 @@ function update_posts_from_stories( $publish = TRUE, $qnum = false ) { } else { $existing = $existing_status = null; } - + + $has_npr_layout = FALSE; + if ($use_npr_layout) { + // get the "NPR layout" version if available and the "use rich layout" option checked in settings + $body_with_layout = $this->get_body_with_layout($story); + if (!empty($body_with_layout)) { + $story->body = $body_with_layout; + $has_npr_layout = TRUE; + } + } //add the transcript $story->body .= $this->get_transcript_body($story); @@ -667,4 +676,193 @@ function get_transcript_body( $story ) { return $transcript_body; } + + + /** + * + * This function will check a story to see if it has a layout object, if there is + * we'll return the body with any externalAssets or htmlAssets inserted in the order they are in the layout + * + * @param string $story + * @return string + */ + function get_body_with_layout( $story ) { + $body_with_layout = ""; + if ( ! empty( $story->layout ) ) { + // simplify the arrangement of the storytext object + $layoutarry = array(); + foreach($story->layout->storytext as $type => $elements) { + if (!is_array($elements)) { + $elements = array($elements); + } + foreach ($elements as $element) { + $num = $element->num; + $reference = $element->refId; + if ($type == 'text') { + $reference = $element->paragraphNum; + } + $layoutarry[(int)$num] = array('type'=>$type, 'reference' => $reference); + } + } + ksort($layoutarry); + + $paragraphs = array(); + $num = 1; + foreach ($story->textWithHtml->paragraphs as $paragraph) { + $partext = (string) $paragraph->value; + $paragraphs[$num] = $partext; + $num++; + } + + $storyimages = array(); + if (isset($story->image) ) { + $storyimages_array = array(); + if (isset($story->image->id)) { + $storyimages_array[] = $story->image; + } else { + // sometimes there are multiple objects + foreach ( (array) $story->image as $stryimage ) { + if (isset($stryimage->id)) { + $storyimages_array[] = $stryimage; + } + } + } + foreach ($storyimages_array as $image) { + if ( ! empty( $image->enlargement ) ) { + $image_url = $image->enlargement->src; + } else { + if ( ! empty( $image->crop ) && is_array( $image->crop ) ) { + foreach ( $image->crop as $crop ) { + if ( empty( $crop->type ) ) { + continue; + } + if ( 'enlargement' === $crop->type ) { + $image_url = $crop->src; + } + } + if ( empty( $image_url ) ) { + foreach ( $image->crop as $crop ) { + if ( empty( $crop->type ) ) { + continue; + } + if ( 'standard' === $crop->type ) { + $image_url = $crop->src; + } + } + } + } + } + if ( empty( $image_url ) && ! empty( $image->src ) ) { + $image_url = $image->src; + } + // add resizing to any npr-hosted image + if (strpos($image_url, 'media.npr.org')) { + // remove any existing querystring + if (strpos($image_url)) { + $image_url = substr($image_url, strpos($image_url, 0, '?')); + } + $image_url .= '?s=6'; + } + $storyimages[$image->id] = (array) $image; + $storyimages[$image->id]['image_url'] = $image_url; + } + } + + + + $externalAssets = array(); + if (isset($story->externalAsset) ) { + $externals_array = array(); + if (isset($story->externalAsset->type)) { + $externals_array[] = $story->externalAsset; + } else { + // sometimes there are multiple objects + foreach ( (array) $story->externalAsset as $extasset ) { + if (isset($extasset->type)) { + $externals_array[] = $extasset; + } + } + } + foreach ($externals_array as $embed) { + $externalAssets[$embed->id] = (array) $embed; + } + } + $htmlAssets = array(); + if (isset($story->htmlAsset) ) { + if (isset($story->htmlAsset->id)) { + $htmlAssets[$story->htmlAsset->id] = $story->htmlAsset->value; + } else { + // sometimes there are multiple objects + foreach ( (array) $story->htmlAsset as $hasset ) { + if (isset($hasset->id)) { + $htmlAssets[$hasset->id] = $hasset->value; + } + } + } + } + + foreach ($layoutarry as $ordernum => $element) { + $reference = $element['reference']; + switch ($element['type']) { + case 'text': + if (!empty($paragraphs[$reference])) { + $body_with_layout .= "

" . $paragraphs[$reference] . "

\n"; + } + break; + case 'staticHtml': + if (!empty($htmlAssets[$reference])) { + $body_with_layout .= $htmlAssets[$reference] . "\n\n"; + } + break; + case 'externalAsset': + if (!empty($externalAssets[$reference])) { + $body_with_layout .= $externalAssets[$reference]['url'] . "\n"; + if (!empty( (string)$externalAssets[$reference]['credit'])) { + $body_with_layout .= "" . (string) $externalAssets[$reference]['credit'] . "\n"; + } + if (!empty( (string)$externalAssets[$reference]['caption'])) { + $body_with_layout .= "

" . (string) $externalAssets[$reference]['caption'] . "

\n"; + } + $body_with_layout .= "\n"; + } + break; + case 'image': + if (!empty($storyimages[$reference])) { + $figclass="npr-featured-image"; + $thisimg = $storyimages[$reference]; + $fightml = !empty( (string)$thisimg['image_url']) ? '' . $thiscaption . '' : ''; + $figcaption = (!empty($fightml) && !empty( $thiscaption)) ? '
' . $thiscaption : ''; + if (!empty($figcaption)) { + $cites = ''; + foreach (array('producer', 'provider', 'copyright') as $item) { + $thisitem = (string)$thisimg[$item]; + if (!empty($thisitem)) { + $cites .= !empty($cites) ? ' | ' . $thisitem : $thisitem; + } + } + $figcaption .= !empty($cites) ? "$cites" : ''; + } + $figcapton .= !empty($figcaption) ? '
' : ''; + $fightml .= (!empty($fightml) && !empty($figcaption)) ? $figcaption : ''; + $body_with_layout .= (!empty($fightml)) ? "
$fightml
\n\n" : ''; + } + break; + } + } + + } + return $body_with_layout; + } + + + + + + + + + } From 68d08d19a4ea742f0ad3faaa89f5d3f6d1b0b248 Mon Sep 17 00:00:00 2001 From: William Tam Date: Mon, 27 Jul 2020 21:49:07 +0000 Subject: [PATCH 08/23] disable WPs kses filters when pulling a post from the NPR Story API so javascript etc can be saved --- classes/NPRAPIWordpress.php | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/classes/NPRAPIWordpress.php b/classes/NPRAPIWordpress.php index 41e0aa5..2041b34 100644 --- a/classes/NPRAPIWordpress.php +++ b/classes/NPRAPIWordpress.php @@ -275,10 +275,21 @@ function update_posts_from_stories( $publish = TRUE, $qnum = false ) { * @param NPRMLEntity $story Story object created during import * @param bool $created true if not pre-existing, false otherwise */ + + if ($use_npr_layout) { + // keep WP from stripping content from NPR posts + kses_remove_filters(); + } + $args = apply_filters( 'npr_pre_insert_post', $args, $post_id, $story, $created ); $post_id = wp_insert_post( $args ); + if ($use_npr_layout) { + // re-enable the built-in content stripping + kses_init_filters(); + } + //now that we have an id, we can add images //this is the way WP seems to do it, but we couldn't call media_sideload_image or media_ because that returned only the URL //for the attachment, and we want to be able to set the primary image, so we had to use this method to get the attachment ID. @@ -452,9 +463,20 @@ function update_posts_from_stories( $publish = TRUE, $qnum = false ) { * @param int $post_id Post ID or NULL if no post ID. * @param NPRMLEntity $story Story object created during import */ + + if ($use_npr_layout) { + // keep WP from stripping content from NPR posts + kses_remove_filters(); + } + $args = apply_filters( 'npr_pre_update_post', $args, $post_id, $story ); $post_id = wp_insert_post( $args ); + + if ($use_npr_layout) { + // re-enable content stripping + kses_init_filters(); + } } //set categories for story From 511f6f25172c0c2f979c406de900ca8003153c51 Mon Sep 17 00:00:00 2001 From: William Tam Date: Tue, 28 Jul 2020 16:47:19 +0000 Subject: [PATCH 09/23] redoing get_body_with_layout to return an array and call for it to expect array --- classes/NPRAPIWordpress.php | 30 +++++++++++++++++++----------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/classes/NPRAPIWordpress.php b/classes/NPRAPIWordpress.php index 2041b34..f4eaaca 100644 --- a/classes/NPRAPIWordpress.php +++ b/classes/NPRAPIWordpress.php @@ -140,13 +140,15 @@ function update_posts_from_stories( $publish = TRUE, $qnum = false ) { $existing = $existing_status = null; } - $has_npr_layout = FALSE; + $npr_has_layout = FALSE; + $npr_has_video = FALSE; if ($use_npr_layout) { // get the "NPR layout" version if available and the "use rich layout" option checked in settings - $body_with_layout = $this->get_body_with_layout($story); - if (!empty($body_with_layout)) { - $story->body = $body_with_layout; - $has_npr_layout = TRUE; + $npr_layout = $this->get_body_with_layout($story); + if (!empty($npr_layout['body'])) { + $story->body = $npr_layout['body']; + $npr_has_layout = TRUE; + $npr_has_video = $npr_layout['has_video']; } } //add the transcript @@ -703,12 +705,14 @@ function get_transcript_body( $story ) { /** * * This function will check a story to see if it has a layout object, if there is - * we'll return the body with any externalAssets or htmlAssets inserted in the order they are in the layout + * we'll format the body with any images, externalAssets, or htmlAssets inserted in the order they are in the layout + * and return an array of the transformed body and flags for what sort of elements are returned * - * @param string $story - * @return string + * @param NPRMLEntity $story Story object created during import + * @return array with reconstructed body and flags describing returned elements */ function get_body_with_layout( $story ) { + $returnary = array('body' => FALSE, 'has_layout' => FALSE, 'has_image' => FALSE, 'has_video' => FALSE, 'has_external' => FALSE); $body_with_layout = ""; if ( ! empty( $story->layout ) ) { // simplify the arrangement of the storytext object @@ -720,14 +724,16 @@ function get_body_with_layout( $story ) { foreach ($elements as $element) { $num = $element->num; $reference = $element->refId; - if ($type == 'text') { + if ($type == 'text') { + // only paragraphs don't have a refId, they use num instead $reference = $element->paragraphNum; } $layoutarry[(int)$num] = array('type'=>$type, 'reference' => $reference); } } ksort($layoutarry); - + $returnary['has_layout'] = TRUE; + $paragraphs = array(); $num = 1; foreach ($story->textWithHtml->paragraphs as $paragraph) { @@ -876,7 +882,9 @@ function get_body_with_layout( $story ) { } } - return $body_with_layout; + $returnary['body']= $body_with_layout; + + return $returnary; } From bc82c6f8d3666dbc9030d7bfd2c2303bb6f9a294 Mon Sep 17 00:00:00 2001 From: William Tam Date: Tue, 28 Jul 2020 16:48:32 +0000 Subject: [PATCH 10/23] fixing layout html to be more semantically correct and better fit WP standards --- classes/NPRAPIWordpress.php | 52 +++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 19 deletions(-) diff --git a/classes/NPRAPIWordpress.php b/classes/NPRAPIWordpress.php index f4eaaca..90ebaf2 100644 --- a/classes/NPRAPIWordpress.php +++ b/classes/NPRAPIWordpress.php @@ -840,40 +840,54 @@ function get_body_with_layout( $story ) { case 'staticHtml': if (!empty($htmlAssets[$reference])) { $body_with_layout .= $htmlAssets[$reference] . "\n\n"; + $returnary['has_external'] = TRUE; + if (strpos($htmlAssets[$reference], 'jwplayer.com')) { + $returnary['has_video'] = TRUE; + } } break; case 'externalAsset': if (!empty($externalAssets[$reference])) { - $body_with_layout .= $externalAssets[$reference]['url'] . "\n"; - if (!empty( (string)$externalAssets[$reference]['credit'])) { - $body_with_layout .= "" . (string) $externalAssets[$reference]['credit'] . "\n"; + $figclass = "wp-block-embed"; + if (!empty( (string)$externalAssets[$reference]['type']) && strtolower((string)$externalAssets[$reference]['type']) == 'youtube') { + $returnary['has_video'] = TRUE; + $figclass .= " is-type-video"; } - if (!empty( (string)$externalAssets[$reference]['caption'])) { - $body_with_layout .= "

" . (string) $externalAssets[$reference]['caption'] . "

\n"; + $fightml = "
"; + $fightml .= "\n" . $externalAssets[$reference]['url'] . "\n"; + $figcaption = ''; + if (!empty( (string)$externalAssets[$reference]['credit']) || !empty( (string)$externalAssets[$reference]['caption'] ) ) { + if (!empty( trim((string)$externalAssets[$reference]['credit']))) { + $figcaption .= "" . trim((string) $externalAssets[$reference]['credit']) . ""; + } + if (!empty( (string)$externalAssets[$reference]['caption'])) { + $figcaption .= trim((string) $externalAssets[$reference]['caption']); + } + $figcaption = !empty($figcaption) ? "
$figcaption
" : ""; } - $body_with_layout .= "\n"; + $fightml .= "
$figcaption
\n"; + $body_with_layout .= $fightml; } break; case 'image': if (!empty($storyimages[$reference])) { - $figclass="npr-featured-image"; + $figclass = "wp-block-image size-large"; $thisimg = $storyimages[$reference]; $fightml = !empty( (string)$thisimg['image_url']) ? '' . $thiscaption . '' : ''; - $figcaption = (!empty($fightml) && !empty( $thiscaption)) ? '
' . $thiscaption : ''; - if (!empty($figcaption)) { - $cites = ''; - foreach (array('producer', 'provider', 'copyright') as $item) { - $thisitem = (string)$thisimg[$item]; - if (!empty($thisitem)) { - $cites .= !empty($cites) ? ' | ' . $thisitem : $thisitem; - } + $figcaption = (!empty($fightml) && !empty( $thiscaption)) ? $thiscaption : ''; + $cites = ''; + foreach (array('producer', 'provider', 'copyright') as $item) { + $thisitem = trim( (string)$thisimg[$item] ); + if (!empty($thisitem)) { + $cites .= !empty($cites) ? ' | ' . $thisitem : $thisitem; } - $figcaption .= !empty($cites) ? "$cites" : ''; - } - $figcapton .= !empty($figcaption) ? '
' : ''; + } + $cites = !empty($cites) ? "$cites" : ''; + $thiscaption .= $cites; + $figcaption = (!empty($fightml) && !empty( $thiscaption)) ? "
$thiscaption
" : ''; $fightml .= (!empty($fightml) && !empty($figcaption)) ? $figcaption : ''; $body_with_layout .= (!empty($fightml)) ? "
$fightml
\n\n" : ''; } From c74185610db0ed6d2e1541bacfb445ca5e488aae Mon Sep 17 00:00:00 2001 From: William Tam Date: Tue, 28 Jul 2020 16:55:25 +0000 Subject: [PATCH 11/23] removing unused meta key assignment --- ds-npr-api.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/ds-npr-api.php b/ds-npr-api.php index dd66499..c0593cb 100644 --- a/ds-npr-api.php +++ b/ds-npr-api.php @@ -44,8 +44,6 @@ define( 'NPR_IMAGE_AGENCY_META_KEY', 'npr_image_agency'); define( 'NPR_IMAGE_CAPTION_META_KEY', 'npr_image_caption'); -define( 'NPR_OEMBED_META_KEY_PREFIX', 'npr_ds_oembeds_'); - define( 'NPR_PUSH_STORY_ERROR', 'npr_push_story_error'); define( 'NPR_MAX_QUERIES', 10 ); From 524b73fda1da488654f6025055fa9be0bdfa4199 Mon Sep 17 00:00:00 2001 From: William Tam Date: Tue, 28 Jul 2020 16:58:02 +0000 Subject: [PATCH 12/23] assign npr_has_layout and npr_has_video postmeta if rich layout used or video part of story --- classes/NPRAPIWordpress.php | 2 ++ ds-npr-api.php | 3 +++ 2 files changed, 5 insertions(+) diff --git a/classes/NPRAPIWordpress.php b/classes/NPRAPIWordpress.php index 90ebaf2..598beae 100644 --- a/classes/NPRAPIWordpress.php +++ b/classes/NPRAPIWordpress.php @@ -238,6 +238,8 @@ function update_posts_from_stories( $publish = TRUE, $qnum = false ) { NPR_PUB_DATE_META_KEY => $story->pubDate->value, NPR_STORY_DATE_MEATA_KEY => $story->storyDate->value, NPR_LAST_MODIFIED_DATE_KEY => $story->lastModifiedDate->value, + NPR_STORY_HAS_LAYOUT_META_KEY => $npr_has_layout, + NPR_STORY_HAS_VIDEO_META_KEY => $npr_has_video, ); //get audio if ( isset($story->audio) ) { diff --git a/ds-npr-api.php b/ds-npr-api.php index c0593cb..45053f8 100644 --- a/ds-npr-api.php +++ b/ds-npr-api.php @@ -44,6 +44,9 @@ define( 'NPR_IMAGE_AGENCY_META_KEY', 'npr_image_agency'); define( 'NPR_IMAGE_CAPTION_META_KEY', 'npr_image_caption'); +define( 'NPR_STORY_HAS_LAYOUT_META_KEY', 'npr_has_layout'); +define( 'NPR_STORY_HAS_VIDEO_META_KEY', 'npr_has_video'); + define( 'NPR_PUSH_STORY_ERROR', 'npr_push_story_error'); define( 'NPR_MAX_QUERIES', 10 ); From e6802fec275f761f842d39548e3bf7ada448d61e Mon Sep 17 00:00:00 2001 From: William Tam Date: Tue, 28 Jul 2020 17:05:26 +0000 Subject: [PATCH 13/23] better caution language on settings screen about wp_kses disabling --- settings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.php b/settings.php index 067916a..5663233 100644 --- a/settings.php +++ b/settings.php @@ -166,7 +166,7 @@ function nprstory_query_use_layout_callback() { } $check_box_string .= "/>"; - echo $check_box_string . "

If layout is available will render any YouTube, Tweets, images, or JavaScript-based widgets within the post in the order they appear. CAUTION: This will allow the 'admin' user to post unfiltered content such as JavaScript.

"; + echo $check_box_string . "

If layout is available will render any YouTube, Tweets, images, or JavaScript-based widgets within the post in the order they appear. CAUTION: This disables the normal 'wp_kses' filtering for imported posts that prevents any JavaScript from being included in the post. We assume that NPR Story API posts will not have malicious scripts.

"; wp_nonce_field( 'nprstory_nonce_ds_npr_query_use_layout', 'nprstory_nonce_ds_npr_query_use_layout_name', true, true ); } From 5a7ab6a19b0bcc0b9857077cce05a4beb29c2e81 Mon Sep 17 00:00:00 2001 From: William Tam Date: Tue, 28 Jul 2020 19:23:10 +0000 Subject: [PATCH 14/23] strip tags from image caption before using as an alt tag --- classes/NPRAPIWordpress.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/NPRAPIWordpress.php b/classes/NPRAPIWordpress.php index 598beae..7cb5fbd 100644 --- a/classes/NPRAPIWordpress.php +++ b/classes/NPRAPIWordpress.php @@ -877,7 +877,7 @@ function get_body_with_layout( $story ) { $thisimg = $storyimages[$reference]; $fightml = !empty( (string)$thisimg['image_url']) ? '' . $thiscaption . '' : ''; $figcaption = (!empty($fightml) && !empty( $thiscaption)) ? $thiscaption : ''; $cites = ''; From 0f2b19c494419bd421dd9374292eb66d844e017a Mon Sep 17 00:00:00 2001 From: William Tam Date: Tue, 28 Jul 2020 19:24:11 +0000 Subject: [PATCH 15/23] set portrait/custom size images to alignright --- classes/NPRAPIWordpress.php | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/classes/NPRAPIWordpress.php b/classes/NPRAPIWordpress.php index 7cb5fbd..b73903d 100644 --- a/classes/NPRAPIWordpress.php +++ b/classes/NPRAPIWordpress.php @@ -758,28 +758,21 @@ function get_body_with_layout( $story ) { } } foreach ($storyimages_array as $image) { + $image_url = FALSE; + $is_portrait = FALSE; if ( ! empty( $image->enlargement ) ) { $image_url = $image->enlargement->src; - } else { - if ( ! empty( $image->crop ) && is_array( $image->crop ) ) { - foreach ( $image->crop as $crop ) { - if ( empty( $crop->type ) ) { - continue; - } - if ( 'enlargement' === $crop->type ) { - $image_url = $crop->src; - } + } + if ( ! empty( $image->crop ) && is_array( $image->crop ) ) { + foreach ( $image->crop as $crop ) { + if (empty($crop->primary)) { + continue; } - if ( empty( $image_url ) ) { - foreach ( $image->crop as $crop ) { - if ( empty( $crop->type ) ) { - continue; - } - if ( 'standard' === $crop->type ) { - $image_url = $crop->src; - } - } + $image_url = $crop->src; + if ($crop->type == 'custom' || ((int)$crop->height > (int)$crop->width)) { + $is_portrait = TRUE; } + break; } } if ( empty( $image_url ) && ! empty( $image->src ) ) { @@ -791,10 +784,11 @@ function get_body_with_layout( $story ) { if (strpos($image_url)) { $image_url = substr($image_url, strpos($image_url, 0, '?')); } - $image_url .= '?s=6'; + $image_url .= !$is_portrait ? '?s=6' : '?s=12'; } $storyimages[$image->id] = (array) $image; $storyimages[$image->id]['image_url'] = $image_url; + $storyimages[$image->id]['is_portrait'] = $is_portrait; } } @@ -876,6 +870,10 @@ function get_body_with_layout( $story ) { $figclass = "wp-block-image size-large"; $thisimg = $storyimages[$reference]; $fightml = !empty( (string)$thisimg['image_url']) ? '' . strip_tags($thiscaption) . '' : ''; From f26f0633f2dafff4641e5994dbd49444088a266f Mon Sep 17 00:00:00 2001 From: William Tam Date: Tue, 28 Jul 2020 21:15:29 +0000 Subject: [PATCH 16/23] silly error on substr --- classes/NPRAPIWordpress.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/classes/NPRAPIWordpress.php b/classes/NPRAPIWordpress.php index b73903d..cefcf4f 100644 --- a/classes/NPRAPIWordpress.php +++ b/classes/NPRAPIWordpress.php @@ -781,8 +781,8 @@ function get_body_with_layout( $story ) { // add resizing to any npr-hosted image if (strpos($image_url, 'media.npr.org')) { // remove any existing querystring - if (strpos($image_url)) { - $image_url = substr($image_url, strpos($image_url, 0, '?')); + if (strpos($image_url, '?')) { + $image_url = substr($image_url, 0, strpos($image_url, '?')); } $image_url .= !$is_portrait ? '?s=6' : '?s=12'; } From 0de0d97009451e0f5ab4fc8caf86dffe5aadaa91 Mon Sep 17 00:00:00 2001 From: William Tam Date: Tue, 28 Jul 2020 21:16:32 +0000 Subject: [PATCH 17/23] add the primary image to a page in place of a gallery --- classes/NPRAPIWordpress.php | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/classes/NPRAPIWordpress.php b/classes/NPRAPIWordpress.php index cefcf4f..9e54f49 100644 --- a/classes/NPRAPIWordpress.php +++ b/classes/NPRAPIWordpress.php @@ -865,7 +865,21 @@ function get_body_with_layout( $story ) { $body_with_layout .= $fightml; } break; - case 'image': + default: + // handles both 'list' and 'image' since it will reset the type and then assign the reference + if ($element['type'] == 'list') { + foreach ($storyimages as $image) { + if ($image['type'] != 'primary') { + continue; + } + $reference = $image['id']; + $element['type'] = 'image'; + break; + } + } + if ($element['type'] != 'image') { + break; + } if (!empty($storyimages[$reference])) { $figclass = "wp-block-image size-large"; $thisimg = $storyimages[$reference]; @@ -889,7 +903,9 @@ function get_body_with_layout( $story ) { $thiscaption .= $cites; $figcaption = (!empty($fightml) && !empty( $thiscaption)) ? "
$thiscaption
" : ''; $fightml .= (!empty($fightml) && !empty($figcaption)) ? $figcaption : ''; - $body_with_layout .= (!empty($fightml)) ? "
$fightml
\n\n" : ''; + $body_with_layout .= (!empty($fightml)) ? "
$fightml
\n\n" : ''; + // make sure it doesn't get reused; + unset($storyimages[$reference]); } break; } From e1fb87da5db6162682cc5f3ea24a33508ed3d75d Mon Sep 17 00:00:00 2001 From: William Tam Date: Wed, 29 Jul 2020 16:59:43 +0000 Subject: [PATCH 18/23] only make custom crops with height > width portrait --- classes/NPRAPIWordpress.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/NPRAPIWordpress.php b/classes/NPRAPIWordpress.php index 9e54f49..073d5ab 100644 --- a/classes/NPRAPIWordpress.php +++ b/classes/NPRAPIWordpress.php @@ -769,7 +769,7 @@ function get_body_with_layout( $story ) { continue; } $image_url = $crop->src; - if ($crop->type == 'custom' || ((int)$crop->height > (int)$crop->width)) { + if ($crop->type == 'custom' && ((int)$crop->height > (int)$crop->width)) { $is_portrait = TRUE; } break; From ac3672f207d73402c129f522b9070a8325599cce Mon Sep 17 00:00:00 2001 From: William Tam Date: Wed, 29 Jul 2020 17:03:47 +0000 Subject: [PATCH 19/23] skip the sideloading of non-primary images if using the npr layout to SERIOUSLY improve import performance on multi-image posts --- classes/NPRAPIWordpress.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/classes/NPRAPIWordpress.php b/classes/NPRAPIWordpress.php index 073d5ab..1b2f73a 100644 --- a/classes/NPRAPIWordpress.php +++ b/classes/NPRAPIWordpress.php @@ -312,6 +312,12 @@ function update_posts_from_stories( $publish = TRUE, $qnum = false ) { $attached_images = get_children( $image_args ); } foreach ( (array) $story->image as $image ) { + + // only sideload the primary image if using the npr layout + if ( ($image->type != 'primary') && $use_npr_layout ) { + continue; + } + $image_url = ''; //check the and then the crops, in this order "enlargement", "standard" if they don't exist, just get the image->src if ( ! empty( $image->enlargement ) ) { From 7846a629f869aa2dd3940c5dd7ae5562ac92bde3 Mon Sep 17 00:00:00 2001 From: William Tam Date: Wed, 29 Jul 2020 20:34:29 +0000 Subject: [PATCH 20/23] properly process crops when theres only one of them --- classes/NPRAPIWordpress.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/classes/NPRAPIWordpress.php b/classes/NPRAPIWordpress.php index 1b2f73a..a4e09f8 100644 --- a/classes/NPRAPIWordpress.php +++ b/classes/NPRAPIWordpress.php @@ -769,7 +769,12 @@ function get_body_with_layout( $story ) { if ( ! empty( $image->enlargement ) ) { $image_url = $image->enlargement->src; } - if ( ! empty( $image->crop ) && is_array( $image->crop ) ) { + if ( ! empty( $image->crop )) { + if (!is_array( $image->crop ) ) { + $cropobj = $image->crop; + unset($image->crop); + $image->crop = array($cropobj); + } foreach ( $image->crop as $crop ) { if (empty($crop->primary)) { continue; From 90de775dd82c437b3da506ea07895663b0a98ba2 Mon Sep 17 00:00:00 2001 From: William Tam Date: Thu, 30 Jul 2020 15:03:30 +0000 Subject: [PATCH 21/23] base layout conditionals on whether a layout was retrieved, not just on whether the admin selected to use the layout feature --- classes/NPRAPIWordpress.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/classes/NPRAPIWordpress.php b/classes/NPRAPIWordpress.php index a4e09f8..d391e76 100644 --- a/classes/NPRAPIWordpress.php +++ b/classes/NPRAPIWordpress.php @@ -280,7 +280,7 @@ function update_posts_from_stories( $publish = TRUE, $qnum = false ) { * @param bool $created true if not pre-existing, false otherwise */ - if ($use_npr_layout) { + if ($npr_has_layout) { // keep WP from stripping content from NPR posts kses_remove_filters(); } @@ -289,7 +289,7 @@ function update_posts_from_stories( $publish = TRUE, $qnum = false ) { $post_id = wp_insert_post( $args ); - if ($use_npr_layout) { + if ($npr_has_layout) { // re-enable the built-in content stripping kses_init_filters(); } @@ -314,7 +314,7 @@ function update_posts_from_stories( $publish = TRUE, $qnum = false ) { foreach ( (array) $story->image as $image ) { // only sideload the primary image if using the npr layout - if ( ($image->type != 'primary') && $use_npr_layout ) { + if ( ($image->type != 'primary') && $npr_has_layout ) { continue; } @@ -474,7 +474,7 @@ function update_posts_from_stories( $publish = TRUE, $qnum = false ) { * @param NPRMLEntity $story Story object created during import */ - if ($use_npr_layout) { + if ($npr_has_layout) { // keep WP from stripping content from NPR posts kses_remove_filters(); } @@ -483,7 +483,7 @@ function update_posts_from_stories( $publish = TRUE, $qnum = false ) { $post_id = wp_insert_post( $args ); - if ($use_npr_layout) { + if ($npr_has_layout) { // re-enable content stripping kses_init_filters(); } From 0159a6eb8d6fcd2aafe311db1429257c4f71793a Mon Sep 17 00:00:00 2001 From: William Tam Date: Thu, 30 Jul 2020 15:40:23 +0000 Subject: [PATCH 22/23] further doc tweak for settings page --- settings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings.php b/settings.php index 5663233..753ed7c 100644 --- a/settings.php +++ b/settings.php @@ -166,7 +166,7 @@ function nprstory_query_use_layout_callback() { } $check_box_string .= "/>"; - echo $check_box_string . "

If layout is available will render any YouTube, Tweets, images, or JavaScript-based widgets within the post in the order they appear. CAUTION: This disables the normal 'wp_kses' filtering for imported posts that prevents any JavaScript from being included in the post. We assume that NPR Story API posts will not have malicious scripts.

"; + echo $check_box_string . "

If 'layout' is available in the NPR Story API output for your key, checking this box will import posts with more complex HTML to render any images, YouTube videos, Tweets, iframes, or JavaScript-based widgets within the post in the order they appeared on the NPR website. Only the 'primary' image for the story will be sideloaded into the Media Library, all other images will be linked from NPR. CAUTION: This disables the normal 'wp_kses' filtering for imported posts that prevents any JavaScript from being included in the post. We assume that NPR Story API posts will not have malicious scripts.

"; wp_nonce_field( 'nprstory_nonce_ds_npr_query_use_layout', 'nprstory_nonce_ds_npr_query_use_layout_name', true, true ); } From 5bc21977a8e49d8658bee933ef46b776da0fcec9 Mon Sep 17 00:00:00 2001 From: William Tam Date: Thu, 30 Jul 2020 16:01:40 +0000 Subject: [PATCH 23/23] only check delete NPR API permissions on posts that will be pushed to NPR API --- push_story.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/push_story.php b/push_story.php index a964698..6ecb6de 100644 --- a/push_story.php +++ b/push_story.php @@ -82,13 +82,6 @@ function nprstory_api_push ( $post_ID, $post ) { * @param unknown_type $post_ID */ function nprstory_api_delete ( $post_ID ) { - if ( ! current_user_can( 'delete_others_posts' ) ) { - wp_die( - __('You do not have permission to delete posts in the NPR API. Users that can delete other users\' posts have that ability: administrators and editors.'), - __('NPR Story API Error'), - 403 - ); - } $push_post_type = get_option( 'ds_npr_push_post_type' ); if ( empty( $push_post_type ) ) { @@ -106,6 +99,15 @@ function nprstory_api_delete ( $post_ID ) { //if the push url isn't set, don't even try to delete. $push_url = get_option( 'ds_npr_api_push_url' ); if ( $post->post_type == $push_post_type && ! empty( $push_url ) && ! empty( $api_id ) ) { + // don't let a non-admin/editor push a delete to the API + if ( ! current_user_can( 'delete_others_posts' ) ) { + wp_die( + __('You do not have permission to delete posts in the NPR API. Users that can delete other users\' posts have that ability: administrators and editors.'), + __('NPR Story API Error'), + 403 + ); + } + // For now, only submit regular posts, and only on publish. if ( $post->post_type != 'post' || $post->post_status != 'publish' ) { return;