Plugin Directory

Changeset 3384901


Ignore:
Timestamp:
10/27/2025 12:56:10 AM (7 weeks ago)
Author:
anthonyeden
Message:

Version 1.6.0

Location:
restful-syndication
Files:
3 edited
2 copied

Legend:

Unmodified
Added
Removed
  • restful-syndication/tags/1.6.0/index.php

    r3180974 r3384901  
    44Plugin URI: https://mediarealm.com.au/
    55Description: Import content from the Wordpress REST API on another Wordpress site
    6 Version: 1.5.0
     6Version: 1.6.0
    77Author: Media Realm
    88Author URI: https://www.mediarealm.com.au/
     
    106106    public $errors_logged = array();
    107107
     108    private $push_remote_auth_key = null;
     109    private $push_remote_domain = null;
     110
    108111    public function __construct() {
    109112        add_action('admin_menu', array($this, 'admin_menu'));
     
    122125            ));
    123126        });
     127
     128        if(is_plugin_active('advanced-custom-fields/acf.php') || is_plugin_active('advanced-custom-fields-pro/acf.php')) {
     129            if(apply_filters('restful_syndication_allow_image_credit_fields', true) === true) {
     130                $this->settings['fields_image_credits'] = array(
     131                    "title" => "Enable Image Credit Fields",
     132                    "type" => "checkbox",
     133                );
     134
     135                $options = get_option($this->settings_prefix . 'settings');
     136                if($options['fields_image_credits'] == "true") {
     137                    add_action('acf/include_fields',                    array($this, 'fields_image_credits_acf'));
     138                    add_filter('restful_syndication_api_headers',       array($this, 'fields_image_credits_api_header'));
     139                }
     140            }
     141           
     142        }
    124143    }
    125144
     
    314333        }
    315334
     335        $headers = array(
     336            'X-RESTful-Syndication-Version' => get_file_data(__FILE__, array('Version' => 'Version'), false)['Version'],
     337        );
     338
     339        $headers = apply_filters('restful_syndication_api_headers', $headers);
     340
    316341        if(!empty($options['auth_username']) && !empty($options['auth_password'])) {
    317             $headers = array(
    318                 'Authorization' => 'Basic ' . base64_encode($options['auth_username'] . ':' . $options['auth_password']),
    319             );
    320         } else {
    321             $headers = array();
     342            $headers['Authorization'] = 'Basic ' . base64_encode($options['auth_username'] . ':' . $options['auth_password']);
     343        }
     344
     345        if($full === true && $this->push_remote_domain !== null && $this->push_remote_auth_key !== null && strpos($url, "restful_push_auth_key=") === false) {
     346            // Add the Push Auth Key back to the URL as a parameter for subsequent calls
     347            // This is needed for taxonomies, authors, images, etc.
     348
     349            $query = parse_url($url, PHP_URL_QUERY);
     350            if ($query) {
     351                $url .= '&restful_push_auth_key=' . urlencode($this->push_remote_auth_key);
     352            } else {
     353                $url .= '?restful_push_auth_key=' . urlencode($this->push_remote_auth_key);
     354            }
    322355        }
    323356
     
    335368        }
    336369
    337         $data = json_decode(wp_remote_retrieve_body($response), true);
     370        $response_body = wp_remote_retrieve_body($response);
     371        $data = json_decode($response_body, true);
     372
     373        if($data == false) {
     374            $this->log("rest_fetch() - JSON not decoded when fetching ".$url.".");
     375        }
    338376
    339377        return $data;
     
    405443        $existing_post_id = 0;
    406444
     445        if(!is_array($post)) {
     446            $this->log("syndicate_one() - post is not an array. Failing.");
     447            return;
     448        }
     449
    407450        // Have we already ingested this post?
    408451        if($allow_overwrite === true && $this->post_guid_exists($post['guid']['rendered']) !== null) {
     
    411454        } else if($this->post_guid_exists($post['guid']['rendered']) !== null) {
    412455            // Already exists on this site - skip over this post
     456            $this->log("syndicate_one() - Post GUID already exists. Skipping.");
    413457            return;
    414458        }
     
    418462            if(isset($options['earliest_post_date']) && strtotime($options['earliest_post_date']) !== false && strtotime($post['date']) <= strtotime($options['earliest_post_date'])) {
    419463                // This post is earlier than the specified start date
     464                $this->log("syndicate_one() - Post is earlier than earliest post cutoff date. Skipping.");
    420465                return;
    421466            }
     
    455500
    456501            // Parse the HTML
    457             $dom = new domDocument;
    458             $dom->loadHTML('<?xml encoding="utf-8" ?>' . $html, LIBXML_HTML_NOIMPLIED|LIBXML_HTML_NODEFDTD);
     502            if(class_exists('\Dom\HTMLDocument')) {
     503                // PHP 8.4+
     504                $dom = \Dom\HTMLDocument::createFromString($html, LIBXML_HTML_NOIMPLIED|LIBXML_NOERROR);
     505                $dom->strictErrorChecking = false;
     506            } else {
     507                $dom = new domDocument;
     508                $html = str_replace("<hr>", "<hr />", $html); // Workaround for certain libxml versions breaking on this unclosed tag
     509                $dom->loadHTML('<?xml encoding="utf-8" ?>' . $html, LIBXML_HTML_NOIMPLIED|LIBXML_HTML_NODEFDTD);
     510            }
    459511
    460512            // Find and download any embedded images found in the HTML
     
    465517                // Download the image and attach it
    466518                $url = $img->getAttribute('src');
    467                 $attachment_id = $this->ingest_image($url);
    468                
     519                $classes = $img->getAttribute('class');
     520                $image_id = $this->get_image_id_from_classes($classes);
     521                $attachment_id = $this->ingest_image($url, null, null, $image_id, $post);
     522
    469523                // Update the SRC in the HTML
    470524                $url_new = wp_get_attachment_image_src($attachment_id, $options['image_embed_size']);
     
    512566                $audio_shortcode = $dom->createElement('div');
    513567                $audio_shortcode->setAttribute('class', 'audio-filter');
    514                 $audio_shortcode->nodeValue = '[audio src="'.esc_url($url).'"]';
     568                $audio_shortcode->textContent = '[audio src="'.esc_url($url).'"]';
    515569
    516570                // Replace the original <audio> tag with this new <div class="audio-filter">[audio]</div> arrangement
     
    539593                    $embed_shortcode = $dom->createElement('div');
    540594                    $embed_shortcode->setAttribute('class', 'video-filter');
    541                     $embed_shortcode->nodeValue = '[embed]'.esc_url($url_new).'[/embed]';
     595                    $embed_shortcode->textContent = '[embed]'.esc_url($url_new).'[/embed]';
    542596
    543597                    // Replace the original <div class="embed_youtube"> tag with this new <div>[embed]url[/embed]</div> arrangement
     
    576630                // Create a new paragraph, and insert the iframe shortcode
    577631                $embed_shortcode = $dom->createElement('p');
    578                 $embed_shortcode->nodeValue = '[restful_syndication_iframe src="'.esc_url($url).'" width="'.esc_attr($width).'" height="'.esc_attr($height).'"]';
     632                $embed_shortcode->textContent = '[restful_syndication_iframe src="'.esc_url($url).'" width="'.esc_attr($width).'" height="'.esc_attr($height).'"]';
    579633
    580634                // Replace the original <iframe> tag with this new <p>[restful_syndication_iframe src="url"]</p> arrangement
     
    609663                // Create a new paragraph, and insert the iframe shortcode
    610664                $embed_shortcode = $dom->createElement('p');
    611                 $embed_shortcode->nodeValue = '[restful_syndication_iframe src="'.esc_url($url).'" width="'.esc_attr($width).'" height="'.esc_attr($height).'"]';
     665                $embed_shortcode->textContent = '[restful_syndication_iframe src="'.esc_url($url).'" width="'.esc_attr($width).'" height="'.esc_attr($height).'"]';
    612666
    613667                // Replace the original <iframe> tag with this new <p>[restful_syndication_iframe src="url"]</p> arrangement
     
    677731                                if(is_array($tag)) {
    678732                                    $tags[] = $tag['term_id'];
     733                                }
     734                            }
     735                        }
     736                    }
     737                }
     738            }
     739        }
     740
     741        // Find local matching author taxonomy
     742        $author_terms = array();
     743
     744        if(isset($post['_links']['wp:term']) && taxonomy_exists('author')) {
     745            foreach($post['_links']['wp:term'] as $term_link) {
     746                if($term_link['taxonomy'] == 'author') {
     747                    $term_data_all = $this->rest_fetch($term_link['href'], true);
     748
     749                    foreach($term_data_all as $term_data) {
     750                        if(isset($term_data['name']) && !empty($term_data['name'])) {
     751                            $term = get_term_by('name', $term_data['name'], 'author');
     752
     753                            if($term !== false) {
     754                                // Term already exists
     755                                $author_terms[] = $term->term_id;
     756                            } else {
     757                                // Create the author taxonomy term
     758                                $term = wp_insert_term($term_data['name'], 'author');
     759
     760                                if(is_array($term)) {
     761                                    $author_terms[] = $term['term_id'];
    679762                                }
    680763                            }
     
    750833        ), false);
    751834
     835        if($post_id > 0 && count($author_terms) > 0) {
     836            // Set author taxonomy terms
     837            wp_set_object_terms($post_id, $author_terms, 'author');
     838        }
     839
    752840        if($post_id > 0 && isset($post['_links']['wp:featuredmedia'][0]['href'])) {
    753841            // Download and attach featured image
     
    757845
    758846            if(isset($attachment['media_details']['sizes']['full']['source_url'])) {
    759                 $featured_attachment_id = $this->ingest_image($attachment['media_details']['sizes']['full']['source_url'], $post_id);
     847                $featured_attachment_id = $this->ingest_image($attachment['media_details']['sizes']['full']['source_url'], $post_id, $attachment);
    760848                set_post_thumbnail($post_id, $featured_attachment_id);
    761849            } elseif(isset($attachment['source_url'])) {
    762                 $featured_attachment_id = $this->ingest_image($attachment['source_url'], $post_id);
     850                $featured_attachment_id = $this->ingest_image($attachment['source_url'], $post_id, $attachment);
    763851                set_post_thumbnail($post_id, $featured_attachment_id);
    764852            } else {
    765                 $this->log("Attachment full image not found: " . $api_url);
    766             }
     853                $this->log("Attachment full image not found: " . $api_url . " - " . print_r($attachment, true));
     854            }
     855        } else {
     856            $this->log("Featured Media Link not found. " . $post_id);
    767857        }
    768858
     
    809899        $supplied_url_info = parse_url($_POST['restful_push_url']);
    810900        $supplied_domain = $supplied_url_info['host'];
     901        $query_string =  $supplied_url_info['query'];
     902        parse_str($query_string, $query_string_parsed);
    811903
    812904        foreach($domains as $key => $domain) {
     
    818910        }
    819911
     912        if(isset($query_string_parsed['restful_push_auth_key'])) {
     913            $this->push_remote_auth_key = $query_string_parsed['restful_push_auth_key'];
     914            $this->push_remote_domain = $supplied_domain;
     915        }
     916
    820917        // Request data
    821918        $payload = $this->rest_fetch($_POST['restful_push_url'], true);
    822919
    823920        if(empty($payload) || $payload == null) {
    824             return array("error" => 'Failed to fetch post data from API', "errors" => $errors_logged);
     921            return array("error" => 'Failed to fetch post data from API', "errors" => $this->errors_logged);
    825922        }
    826923
     
    833930            if($_POST['post_status'] == 'future' && isset($_POST['schedule_date'])) {
    834931                $payload['date'] = wp_date('Y-m-d H:i:s', $this->wp_strtotime($_POST['schedule_date']));
     932                $payload['date_gmt'] = date('Y-m-d H:i:s', $this->wp_strtotime($_POST['schedule_date']));
    835933            }
    836934        }
    837935
    838936        // Process data
    839         $post_id = $this->syndicate_one($payload, true, $force_publish, true, $post_status, true);
    840 
    841         return array("post_id" => $post_id);
     937        $post_id = $this->syndicate_one($payload, true, false, true, $post_status, true);
     938
     939        return array(
     940            "post_id" => $post_id,
     941            "errors" => $this->errors_logged,
     942            'post_status' => $post_status,
     943            'post_date' => $payload['date'],
     944        );
    842945    }
    843946
     
    10341137    }
    10351138
    1036     private function ingest_image($url, $parent_post_id = null) {
     1139    private function ingest_image($url, $parent_post_id = null, $attachment_rest_data = null, $original_attachment_id = null, $post_rest_data = null) {
    10371140        // Download a file from a URL, and add it to the media library
    10381141
    10391142        require_once(ABSPATH . 'wp-admin/includes/image.php');
    1040 
    1041         $filename = basename($url);
    1042         $response = wp_remote_get($url,
    1043             array(
    1044                 "timeout" => 30,
    1045             )
    1046         );
    1047 
    1048         if(is_wp_error($response)) {
    1049             $this->log("HTTP request failed when downloading image. " . $response->get_error_message());
    1050             return;
    1051         }
    1052 
    1053         $image_data = wp_remote_retrieve_body($response);
    1054 
    1055         if(empty($image_data)) {
    1056             $this->log("Failed to download image " . $url);
    1057             return null;
    1058         }
    1059 
    1060         $upload_dir = wp_upload_dir();
    1061 
    1062         if(wp_mkdir_p($upload_dir['path'])) {
    1063             $file = $upload_dir['path'] . '/' . $filename;
    1064         } else {
    1065             $file = $upload_dir['basedir'] . '/' . $filename;
    1066         }
    10671143
    10681144        // Lookup existing file by meta field
     
    10781154        }
    10791155
     1156        $filename = basename($url);
     1157
     1158        // Check extension - uses get_allowed_mime_types() for mime type list
     1159        $check_filetype = wp_check_filetype($filename);
     1160
     1161        if($check_filetype['type'] == false) {
     1162            $this->log("Stopped download of image due to invalid file type: " . $url);
     1163            return null;
     1164        }
     1165
     1166        $response = wp_remote_get($url,
     1167            array(
     1168                "timeout" => 30,
     1169            )
     1170        );
     1171
     1172        if(is_wp_error($response)) {
     1173            $this->log("HTTP request failed when downloading image. " . $response->get_error_message());
     1174            return;
     1175        }
     1176
     1177        $image_data = wp_remote_retrieve_body($response);
     1178
     1179        if(empty($image_data)) {
     1180            $this->log("Failed to download image " . $url);
     1181            return null;
     1182        }
     1183
     1184        $upload_dir = wp_upload_dir();
     1185
     1186        if(wp_mkdir_p($upload_dir['path'])) {
     1187            $file = $upload_dir['path'] . '/' . $filename;
     1188        } else {
     1189            $file = $upload_dir['basedir'] . '/' . $filename;
     1190        }
     1191
    10801192        if(file_exists($file)) {
    10811193            // File already exists - get the attachment ID
     
    10931205            }
    10941206
    1095             $wp_filetype = wp_check_filetype($filename, null);
     1207            // Check if file is an image
     1208            $check_filetype_and_ext = wp_check_filetype_and_ext($file, $filename);
     1209
     1210            if($check_filetype_and_ext['type'] == false) {
     1211                $this->log("Downloaded file is not a valid image: " . $file . ". Deleting file from disk.");
     1212                unlink($file);
     1213                return null;
     1214            }
    10961215
    10971216            $attachment = array(
    1098                 'post_mime_type' => $wp_filetype['type'],
     1217                'post_mime_type' => $check_filetype_and_ext['type'],
    10991218                'post_title' => sanitize_file_name($filename),
    11001219                'post_content' => '',
     
    11091228            update_post_meta($attach_id, 'restfulsyndication_original_url', $url);
    11101229
     1230            // Fetch rest data for image
     1231            // We will want to save some metadata
     1232            if(empty($attachment_rest_data)) {
     1233                if(!empty($original_attachment_id) && !empty($post_rest_data)) {
     1234                    if(isset($post_rest_data['_links']['self'][0]['href'])) {
     1235                        $post_rest_url = $post_rest_data['_links']['self'][0]['href'];
     1236                        $attachment_rest_url = str_replace("wp/v2/posts/", "wp/v2/media/", $post_rest_url);
     1237                        $attachment_rest_url = substr($attachment_rest_url, 0, strpos($attachment_rest_url, "/media/") + 7) . $original_attachment_id;
     1238                        $attachment_rest_data = $this->rest_fetch($attachment_rest_url, true);
     1239                    }
     1240                }
     1241            }
     1242
     1243            if(isset($attachment_rest_data['alt_text'])) {
     1244                update_post_meta($attach_id, '_wp_attachment_image_alt', $attachment_rest_data['alt_text']);
     1245            }
     1246
     1247            if(isset($attachment_rest_data['title']['rendered'])) {
     1248                $update = array(
     1249                    'ID' => $attach_id,
     1250                    'post_title' => strip_tags($attachment_rest_data['title']['rendered']),
     1251                );
     1252                wp_update_post($update);
     1253            }
     1254
     1255            if(isset($attachment_rest_data['caption']['rendered'])) {
     1256                $update = array(
     1257                    'ID' => $attach_id,
     1258                    'post_excerpt' => strip_tags($attachment_rest_data['caption']['rendered']),
     1259                );
     1260                wp_update_post($update);
     1261            }
     1262
     1263            if(isset($attachment_rest_data['description']['rendered'])) {
     1264                $update = array(
     1265                    'ID' => $attach_id,
     1266                    'post_content' => strip_tags($attachment_rest_data['description']['rendered']),
     1267                );
     1268                wp_update_post($update);
     1269            }
     1270
     1271            // Image Credit fields
     1272            $fields = array(
     1273                'media_credit_text',
     1274                'media_credit_url',
     1275                'media_credit_supplied',
     1276                'supplied_notes',
     1277                'media_credit_original',
     1278                'media_credit_ai_generated',
     1279            );
     1280
     1281            foreach($fields as $field) {
     1282                if(isset($attachment_rest_data['image_credit'][$field])) {
     1283                    if($attachment_rest_data['image_credit'][$field] === true) {
     1284                        $attachment_rest_data['image_credit'][$field] = '1';
     1285                    } elseif($attachment_rest_data['image_credit'][$field] === false) {
     1286                        $attachment_rest_data['image_credit'][$field] = '0';
     1287                    }
     1288                    update_post_meta($attach_id, $field, sanitize_text_field($attachment_rest_data['image_credit'][$field]));
     1289                }
     1290            }
     1291
    11111292            if(!file_exists($file)) {
    11121293                $this->log("Image doesn't exist after processing " . $file);
     
    11151296
    11161297        }
    1117 
    1118        
    11191298
    11201299        return $attach_id;
     
    11651344    }
    11661345
     1346    private function get_image_id_from_classes($class_string) {
     1347        // If given a string of classes, return just the Image ID (if it exists)
     1348        if(preg_match('/wp-image-(\d+)/', $class_string, $matches)) {
     1349            return intval($matches[1]);
     1350        }
     1351        return null;
     1352    }
     1353
     1354    public function fields_image_credits_acf() {
     1355        if (!function_exists('acf_add_local_field_group')) {
     1356            return;
     1357        }
     1358
     1359        acf_add_local_field_group(array(
     1360            'key' => 'group_683a92f3ef691',
     1361            'title' => 'Media Source',
     1362            'fields' => array(
     1363                array(
     1364                    'key' => 'field_683a92f4ab28d',
     1365                    'label' => 'Media Credit',
     1366                    'name' => 'media_credit_text',
     1367                    'aria-label' => '',
     1368                    'type' => 'text',
     1369                    'instructions' => 'Store the name of the photographer or stock photo website here.',
     1370                    'required' => 0,
     1371                    'conditional_logic' => 0,
     1372                    'wrapper' => array(
     1373                        'width' => '',
     1374                        'class' => '',
     1375                        'id' => '',
     1376                    ),
     1377                    'relevanssi_exclude' => 1,
     1378                    'default_value' => '',
     1379                    'maxlength' => '',
     1380                    'allow_in_bindings' => 0,
     1381                    'placeholder' => '',
     1382                    'prepend' => '',
     1383                    'append' => '',
     1384                ),
     1385                array(
     1386                    'key' => 'field_683a9338ab28e',
     1387                    'label' => 'Media Credit URL',
     1388                    'name' => 'media_credit_url',
     1389                    'aria-label' => '',
     1390                    'type' => 'url',
     1391                    'instructions' => 'Paste the link to the original media source.',
     1392                    'required' => 0,
     1393                    'conditional_logic' => 0,
     1394                    'wrapper' => array(
     1395                        'width' => '',
     1396                        'class' => '',
     1397                        'id' => '',
     1398                    ),
     1399                    'relevanssi_exclude' => 1,
     1400                    'default_value' => '',
     1401                    'allow_in_bindings' => 0,
     1402                    'placeholder' => '',
     1403                ),
     1404                array(
     1405                    'key' => 'field_683d2a1832a6d',
     1406                    'label' => 'Supplied?',
     1407                    'name' => 'media_credit_supplied',
     1408                    'aria-label' => '',
     1409                    'type' => 'true_false',
     1410                    'instructions' => 'If this image has been supplied with permission to use it on your website, tick this box. This is sometimes the case with press releases. However, you should still ensure you have permission to use it from the original creator or photographer. If you downloaded this photo from a stock website, do not tick this box.',
     1411                    'required' => 0,
     1412                    'conditional_logic' => 0,
     1413                    'wrapper' => array(
     1414                        'width' => '',
     1415                        'class' => '',
     1416                        'id' => '',
     1417                    ),
     1418                    'relevanssi_exclude' => 1,
     1419                    'message' => '',
     1420                    'default_value' => 0,
     1421                    'allow_in_bindings' => 0,
     1422                    'ui' => 0,
     1423                    'ui_on_text' => '',
     1424                    'ui_off_text' => '',
     1425                ),
     1426                array(
     1427                    'key' => 'field_6853aa7b51fdd',
     1428                    'label' => 'Supplied Notes',
     1429                    'name' => 'supplied_notes',
     1430                    'aria-label' => '',
     1431                    'type' => 'textarea',
     1432                    'instructions' => 'Please note who who gave you permission to use this image. This is an internal field and won\'t be displayed to end-users of your website.',
     1433                    'required' => 0,
     1434                    'conditional_logic' => array(
     1435                        array(
     1436                            array(
     1437                                'field' => 'field_683d2a1832a6d',
     1438                                'operator' => '==',
     1439                                'value' => '1',
     1440                            ),
     1441                        ),
     1442                    ),
     1443                    'wrapper' => array(
     1444                        'width' => '',
     1445                        'class' => '',
     1446                        'id' => '',
     1447                    ),
     1448                    'relevanssi_exclude' => 0,
     1449                    'default_value' => '',
     1450                    'maxlength' => '',
     1451                    'allow_in_bindings' => 0,
     1452                    'rows' => '',
     1453                    'placeholder' => '',
     1454                    'new_lines' => '',
     1455                ),
     1456                array(
     1457                    'key' => 'field_683d2af832a6e',
     1458                    'label' => 'Original?',
     1459                    'name' => 'media_credit_original',
     1460                    'aria-label' => '',
     1461                    'type' => 'true_false',
     1462                    'instructions' => 'If you have created this image yourself, tick this box.',
     1463                    'required' => 0,
     1464                    'conditional_logic' => 0,
     1465                    'wrapper' => array(
     1466                        'width' => '',
     1467                        'class' => '',
     1468                        'id' => '',
     1469                    ),
     1470                    'relevanssi_exclude' => 1,
     1471                    'message' => '',
     1472                    'default_value' => 0,
     1473                    'allow_in_bindings' => 0,
     1474                    'ui' => 0,
     1475                    'ui_on_text' => '',
     1476                    'ui_off_text' => '',
     1477                ),
     1478                array(
     1479                    'key' => 'field_683d2b2732a6f',
     1480                    'label' => 'AI Generated?',
     1481                    'name' => 'media_credit_ai_generated',
     1482                    'aria-label' => '',
     1483                    'type' => 'true_false',
     1484                    'instructions' => 'If this image was created with Artificial Intelligence (AI) tools, tick this box. Usage of AI media creation tools is still a grey area, and caution should be exercised.',
     1485                    'required' => 0,
     1486                    'conditional_logic' => 0,
     1487                    'wrapper' => array(
     1488                        'width' => '',
     1489                        'class' => '',
     1490                        'id' => '',
     1491                    ),
     1492                    'relevanssi_exclude' => 1,
     1493                    'message' => '',
     1494                    'default_value' => 0,
     1495                    'allow_in_bindings' => 0,
     1496                    'ui' => 0,
     1497                    'ui_on_text' => '',
     1498                    'ui_off_text' => '',
     1499                ),
     1500            ),
     1501            'location' => array(
     1502                array(
     1503                    array(
     1504                        'param' => 'attachment',
     1505                        'operator' => '==',
     1506                        'value' => 'all',
     1507                    ),
     1508                ),
     1509            ),
     1510            'menu_order' => 0,
     1511            'position' => 'normal',
     1512            'style' => 'seamless',
     1513            'label_placement' => 'top',
     1514            'instruction_placement' => 'label',
     1515            'hide_on_screen' => '',
     1516            'active' => true,
     1517            'description' => '',
     1518            'show_in_rest' => 0,
     1519        ));
     1520    }
     1521
     1522    public function fields_image_credits_api_header($headers) {
     1523        // Header to disable embedding image credits in
     1524        $headers['X-RESTful-Syndication-Hide-Image-Credits'] = 'True';
     1525
     1526        return $headers;
     1527    }
     1528
    11671529    private function random_str($length, $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()-=+`~') {
    11681530        // From https://stackoverflow.com/a/31284266/2888733
  • restful-syndication/tags/1.6.0/readme.txt

    r3214629 r3384901  
    11=== RESTful Content Syndication ===
    22Contributors: anthonyeden
    3 Tags: syndication, rest, wp-rest
    4 Stable Tag: 1.5.0
     3Tags: syndication, rest, wp-rest, wp-json, json
     4Stable Tag: 1.6.0
    55Requires at least: 6.0.0
    6 Tested up to: 6.7.1
     6Tested up to: 6.8.3
    77Requires PHP: 7.4.0
    88License: GPLv2 or later
     
    5959
    6060== Changelog ==
     61
     62= 1.6.0 =
     63
     64* Image Ingest: Import meta fields where available (title, caption, alt text)
     65* Image Credits: Add option to enable these new media credit fields via the plugin. This option is only available if you have the Advanced Custom Fields plugin active on your site.
     66* Add support for Author taxonomy
     67* Content Push: Return additional fields upon successful push
     68* Content Push: GMT date should align with local date
     69* Content Push: Provide auth key back to source site for subsequent resource pulls
     70* Use \Dom\HTMLDocument where available (in PHP 8.4)
     71* Add new filter: restful_syndication_api_headers
     72* Security: Ensure only image files are downloaded and stored locally (thanks Wordfence/kr0d for responsibly disclosing)
    6173
    6274= 1.5.0 =
  • restful-syndication/trunk/index.php

    r3180974 r3384901  
    44Plugin URI: https://mediarealm.com.au/
    55Description: Import content from the Wordpress REST API on another Wordpress site
    6 Version: 1.5.0
     6Version: 1.6.0
    77Author: Media Realm
    88Author URI: https://www.mediarealm.com.au/
     
    106106    public $errors_logged = array();
    107107
     108    private $push_remote_auth_key = null;
     109    private $push_remote_domain = null;
     110
    108111    public function __construct() {
    109112        add_action('admin_menu', array($this, 'admin_menu'));
     
    122125            ));
    123126        });
     127
     128        if(is_plugin_active('advanced-custom-fields/acf.php') || is_plugin_active('advanced-custom-fields-pro/acf.php')) {
     129            if(apply_filters('restful_syndication_allow_image_credit_fields', true) === true) {
     130                $this->settings['fields_image_credits'] = array(
     131                    "title" => "Enable Image Credit Fields",
     132                    "type" => "checkbox",
     133                );
     134
     135                $options = get_option($this->settings_prefix . 'settings');
     136                if($options['fields_image_credits'] == "true") {
     137                    add_action('acf/include_fields',                    array($this, 'fields_image_credits_acf'));
     138                    add_filter('restful_syndication_api_headers',       array($this, 'fields_image_credits_api_header'));
     139                }
     140            }
     141           
     142        }
    124143    }
    125144
     
    314333        }
    315334
     335        $headers = array(
     336            'X-RESTful-Syndication-Version' => get_file_data(__FILE__, array('Version' => 'Version'), false)['Version'],
     337        );
     338
     339        $headers = apply_filters('restful_syndication_api_headers', $headers);
     340
    316341        if(!empty($options['auth_username']) && !empty($options['auth_password'])) {
    317             $headers = array(
    318                 'Authorization' => 'Basic ' . base64_encode($options['auth_username'] . ':' . $options['auth_password']),
    319             );
    320         } else {
    321             $headers = array();
     342            $headers['Authorization'] = 'Basic ' . base64_encode($options['auth_username'] . ':' . $options['auth_password']);
     343        }
     344
     345        if($full === true && $this->push_remote_domain !== null && $this->push_remote_auth_key !== null && strpos($url, "restful_push_auth_key=") === false) {
     346            // Add the Push Auth Key back to the URL as a parameter for subsequent calls
     347            // This is needed for taxonomies, authors, images, etc.
     348
     349            $query = parse_url($url, PHP_URL_QUERY);
     350            if ($query) {
     351                $url .= '&restful_push_auth_key=' . urlencode($this->push_remote_auth_key);
     352            } else {
     353                $url .= '?restful_push_auth_key=' . urlencode($this->push_remote_auth_key);
     354            }
    322355        }
    323356
     
    335368        }
    336369
    337         $data = json_decode(wp_remote_retrieve_body($response), true);
     370        $response_body = wp_remote_retrieve_body($response);
     371        $data = json_decode($response_body, true);
     372
     373        if($data == false) {
     374            $this->log("rest_fetch() - JSON not decoded when fetching ".$url.".");
     375        }
    338376
    339377        return $data;
     
    405443        $existing_post_id = 0;
    406444
     445        if(!is_array($post)) {
     446            $this->log("syndicate_one() - post is not an array. Failing.");
     447            return;
     448        }
     449
    407450        // Have we already ingested this post?
    408451        if($allow_overwrite === true && $this->post_guid_exists($post['guid']['rendered']) !== null) {
     
    411454        } else if($this->post_guid_exists($post['guid']['rendered']) !== null) {
    412455            // Already exists on this site - skip over this post
     456            $this->log("syndicate_one() - Post GUID already exists. Skipping.");
    413457            return;
    414458        }
     
    418462            if(isset($options['earliest_post_date']) && strtotime($options['earliest_post_date']) !== false && strtotime($post['date']) <= strtotime($options['earliest_post_date'])) {
    419463                // This post is earlier than the specified start date
     464                $this->log("syndicate_one() - Post is earlier than earliest post cutoff date. Skipping.");
    420465                return;
    421466            }
     
    455500
    456501            // Parse the HTML
    457             $dom = new domDocument;
    458             $dom->loadHTML('<?xml encoding="utf-8" ?>' . $html, LIBXML_HTML_NOIMPLIED|LIBXML_HTML_NODEFDTD);
     502            if(class_exists('\Dom\HTMLDocument')) {
     503                // PHP 8.4+
     504                $dom = \Dom\HTMLDocument::createFromString($html, LIBXML_HTML_NOIMPLIED|LIBXML_NOERROR);
     505                $dom->strictErrorChecking = false;
     506            } else {
     507                $dom = new domDocument;
     508                $html = str_replace("<hr>", "<hr />", $html); // Workaround for certain libxml versions breaking on this unclosed tag
     509                $dom->loadHTML('<?xml encoding="utf-8" ?>' . $html, LIBXML_HTML_NOIMPLIED|LIBXML_HTML_NODEFDTD);
     510            }
    459511
    460512            // Find and download any embedded images found in the HTML
     
    465517                // Download the image and attach it
    466518                $url = $img->getAttribute('src');
    467                 $attachment_id = $this->ingest_image($url);
    468                
     519                $classes = $img->getAttribute('class');
     520                $image_id = $this->get_image_id_from_classes($classes);
     521                $attachment_id = $this->ingest_image($url, null, null, $image_id, $post);
     522
    469523                // Update the SRC in the HTML
    470524                $url_new = wp_get_attachment_image_src($attachment_id, $options['image_embed_size']);
     
    512566                $audio_shortcode = $dom->createElement('div');
    513567                $audio_shortcode->setAttribute('class', 'audio-filter');
    514                 $audio_shortcode->nodeValue = '[audio src="'.esc_url($url).'"]';
     568                $audio_shortcode->textContent = '[audio src="'.esc_url($url).'"]';
    515569
    516570                // Replace the original <audio> tag with this new <div class="audio-filter">[audio]</div> arrangement
     
    539593                    $embed_shortcode = $dom->createElement('div');
    540594                    $embed_shortcode->setAttribute('class', 'video-filter');
    541                     $embed_shortcode->nodeValue = '[embed]'.esc_url($url_new).'[/embed]';
     595                    $embed_shortcode->textContent = '[embed]'.esc_url($url_new).'[/embed]';
    542596
    543597                    // Replace the original <div class="embed_youtube"> tag with this new <div>[embed]url[/embed]</div> arrangement
     
    576630                // Create a new paragraph, and insert the iframe shortcode
    577631                $embed_shortcode = $dom->createElement('p');
    578                 $embed_shortcode->nodeValue = '[restful_syndication_iframe src="'.esc_url($url).'" width="'.esc_attr($width).'" height="'.esc_attr($height).'"]';
     632                $embed_shortcode->textContent = '[restful_syndication_iframe src="'.esc_url($url).'" width="'.esc_attr($width).'" height="'.esc_attr($height).'"]';
    579633
    580634                // Replace the original <iframe> tag with this new <p>[restful_syndication_iframe src="url"]</p> arrangement
     
    609663                // Create a new paragraph, and insert the iframe shortcode
    610664                $embed_shortcode = $dom->createElement('p');
    611                 $embed_shortcode->nodeValue = '[restful_syndication_iframe src="'.esc_url($url).'" width="'.esc_attr($width).'" height="'.esc_attr($height).'"]';
     665                $embed_shortcode->textContent = '[restful_syndication_iframe src="'.esc_url($url).'" width="'.esc_attr($width).'" height="'.esc_attr($height).'"]';
    612666
    613667                // Replace the original <iframe> tag with this new <p>[restful_syndication_iframe src="url"]</p> arrangement
     
    677731                                if(is_array($tag)) {
    678732                                    $tags[] = $tag['term_id'];
     733                                }
     734                            }
     735                        }
     736                    }
     737                }
     738            }
     739        }
     740
     741        // Find local matching author taxonomy
     742        $author_terms = array();
     743
     744        if(isset($post['_links']['wp:term']) && taxonomy_exists('author')) {
     745            foreach($post['_links']['wp:term'] as $term_link) {
     746                if($term_link['taxonomy'] == 'author') {
     747                    $term_data_all = $this->rest_fetch($term_link['href'], true);
     748
     749                    foreach($term_data_all as $term_data) {
     750                        if(isset($term_data['name']) && !empty($term_data['name'])) {
     751                            $term = get_term_by('name', $term_data['name'], 'author');
     752
     753                            if($term !== false) {
     754                                // Term already exists
     755                                $author_terms[] = $term->term_id;
     756                            } else {
     757                                // Create the author taxonomy term
     758                                $term = wp_insert_term($term_data['name'], 'author');
     759
     760                                if(is_array($term)) {
     761                                    $author_terms[] = $term['term_id'];
    679762                                }
    680763                            }
     
    750833        ), false);
    751834
     835        if($post_id > 0 && count($author_terms) > 0) {
     836            // Set author taxonomy terms
     837            wp_set_object_terms($post_id, $author_terms, 'author');
     838        }
     839
    752840        if($post_id > 0 && isset($post['_links']['wp:featuredmedia'][0]['href'])) {
    753841            // Download and attach featured image
     
    757845
    758846            if(isset($attachment['media_details']['sizes']['full']['source_url'])) {
    759                 $featured_attachment_id = $this->ingest_image($attachment['media_details']['sizes']['full']['source_url'], $post_id);
     847                $featured_attachment_id = $this->ingest_image($attachment['media_details']['sizes']['full']['source_url'], $post_id, $attachment);
    760848                set_post_thumbnail($post_id, $featured_attachment_id);
    761849            } elseif(isset($attachment['source_url'])) {
    762                 $featured_attachment_id = $this->ingest_image($attachment['source_url'], $post_id);
     850                $featured_attachment_id = $this->ingest_image($attachment['source_url'], $post_id, $attachment);
    763851                set_post_thumbnail($post_id, $featured_attachment_id);
    764852            } else {
    765                 $this->log("Attachment full image not found: " . $api_url);
    766             }
     853                $this->log("Attachment full image not found: " . $api_url . " - " . print_r($attachment, true));
     854            }
     855        } else {
     856            $this->log("Featured Media Link not found. " . $post_id);
    767857        }
    768858
     
    809899        $supplied_url_info = parse_url($_POST['restful_push_url']);
    810900        $supplied_domain = $supplied_url_info['host'];
     901        $query_string =  $supplied_url_info['query'];
     902        parse_str($query_string, $query_string_parsed);
    811903
    812904        foreach($domains as $key => $domain) {
     
    818910        }
    819911
     912        if(isset($query_string_parsed['restful_push_auth_key'])) {
     913            $this->push_remote_auth_key = $query_string_parsed['restful_push_auth_key'];
     914            $this->push_remote_domain = $supplied_domain;
     915        }
     916
    820917        // Request data
    821918        $payload = $this->rest_fetch($_POST['restful_push_url'], true);
    822919
    823920        if(empty($payload) || $payload == null) {
    824             return array("error" => 'Failed to fetch post data from API', "errors" => $errors_logged);
     921            return array("error" => 'Failed to fetch post data from API', "errors" => $this->errors_logged);
    825922        }
    826923
     
    833930            if($_POST['post_status'] == 'future' && isset($_POST['schedule_date'])) {
    834931                $payload['date'] = wp_date('Y-m-d H:i:s', $this->wp_strtotime($_POST['schedule_date']));
     932                $payload['date_gmt'] = date('Y-m-d H:i:s', $this->wp_strtotime($_POST['schedule_date']));
    835933            }
    836934        }
    837935
    838936        // Process data
    839         $post_id = $this->syndicate_one($payload, true, $force_publish, true, $post_status, true);
    840 
    841         return array("post_id" => $post_id);
     937        $post_id = $this->syndicate_one($payload, true, false, true, $post_status, true);
     938
     939        return array(
     940            "post_id" => $post_id,
     941            "errors" => $this->errors_logged,
     942            'post_status' => $post_status,
     943            'post_date' => $payload['date'],
     944        );
    842945    }
    843946
     
    10341137    }
    10351138
    1036     private function ingest_image($url, $parent_post_id = null) {
     1139    private function ingest_image($url, $parent_post_id = null, $attachment_rest_data = null, $original_attachment_id = null, $post_rest_data = null) {
    10371140        // Download a file from a URL, and add it to the media library
    10381141
    10391142        require_once(ABSPATH . 'wp-admin/includes/image.php');
    1040 
    1041         $filename = basename($url);
    1042         $response = wp_remote_get($url,
    1043             array(
    1044                 "timeout" => 30,
    1045             )
    1046         );
    1047 
    1048         if(is_wp_error($response)) {
    1049             $this->log("HTTP request failed when downloading image. " . $response->get_error_message());
    1050             return;
    1051         }
    1052 
    1053         $image_data = wp_remote_retrieve_body($response);
    1054 
    1055         if(empty($image_data)) {
    1056             $this->log("Failed to download image " . $url);
    1057             return null;
    1058         }
    1059 
    1060         $upload_dir = wp_upload_dir();
    1061 
    1062         if(wp_mkdir_p($upload_dir['path'])) {
    1063             $file = $upload_dir['path'] . '/' . $filename;
    1064         } else {
    1065             $file = $upload_dir['basedir'] . '/' . $filename;
    1066         }
    10671143
    10681144        // Lookup existing file by meta field
     
    10781154        }
    10791155
     1156        $filename = basename($url);
     1157
     1158        // Check extension - uses get_allowed_mime_types() for mime type list
     1159        $check_filetype = wp_check_filetype($filename);
     1160
     1161        if($check_filetype['type'] == false) {
     1162            $this->log("Stopped download of image due to invalid file type: " . $url);
     1163            return null;
     1164        }
     1165
     1166        $response = wp_remote_get($url,
     1167            array(
     1168                "timeout" => 30,
     1169            )
     1170        );
     1171
     1172        if(is_wp_error($response)) {
     1173            $this->log("HTTP request failed when downloading image. " . $response->get_error_message());
     1174            return;
     1175        }
     1176
     1177        $image_data = wp_remote_retrieve_body($response);
     1178
     1179        if(empty($image_data)) {
     1180            $this->log("Failed to download image " . $url);
     1181            return null;
     1182        }
     1183
     1184        $upload_dir = wp_upload_dir();
     1185
     1186        if(wp_mkdir_p($upload_dir['path'])) {
     1187            $file = $upload_dir['path'] . '/' . $filename;
     1188        } else {
     1189            $file = $upload_dir['basedir'] . '/' . $filename;
     1190        }
     1191
    10801192        if(file_exists($file)) {
    10811193            // File already exists - get the attachment ID
     
    10931205            }
    10941206
    1095             $wp_filetype = wp_check_filetype($filename, null);
     1207            // Check if file is an image
     1208            $check_filetype_and_ext = wp_check_filetype_and_ext($file, $filename);
     1209
     1210            if($check_filetype_and_ext['type'] == false) {
     1211                $this->log("Downloaded file is not a valid image: " . $file . ". Deleting file from disk.");
     1212                unlink($file);
     1213                return null;
     1214            }
    10961215
    10971216            $attachment = array(
    1098                 'post_mime_type' => $wp_filetype['type'],
     1217                'post_mime_type' => $check_filetype_and_ext['type'],
    10991218                'post_title' => sanitize_file_name($filename),
    11001219                'post_content' => '',
     
    11091228            update_post_meta($attach_id, 'restfulsyndication_original_url', $url);
    11101229
     1230            // Fetch rest data for image
     1231            // We will want to save some metadata
     1232            if(empty($attachment_rest_data)) {
     1233                if(!empty($original_attachment_id) && !empty($post_rest_data)) {
     1234                    if(isset($post_rest_data['_links']['self'][0]['href'])) {
     1235                        $post_rest_url = $post_rest_data['_links']['self'][0]['href'];
     1236                        $attachment_rest_url = str_replace("wp/v2/posts/", "wp/v2/media/", $post_rest_url);
     1237                        $attachment_rest_url = substr($attachment_rest_url, 0, strpos($attachment_rest_url, "/media/") + 7) . $original_attachment_id;
     1238                        $attachment_rest_data = $this->rest_fetch($attachment_rest_url, true);
     1239                    }
     1240                }
     1241            }
     1242
     1243            if(isset($attachment_rest_data['alt_text'])) {
     1244                update_post_meta($attach_id, '_wp_attachment_image_alt', $attachment_rest_data['alt_text']);
     1245            }
     1246
     1247            if(isset($attachment_rest_data['title']['rendered'])) {
     1248                $update = array(
     1249                    'ID' => $attach_id,
     1250                    'post_title' => strip_tags($attachment_rest_data['title']['rendered']),
     1251                );
     1252                wp_update_post($update);
     1253            }
     1254
     1255            if(isset($attachment_rest_data['caption']['rendered'])) {
     1256                $update = array(
     1257                    'ID' => $attach_id,
     1258                    'post_excerpt' => strip_tags($attachment_rest_data['caption']['rendered']),
     1259                );
     1260                wp_update_post($update);
     1261            }
     1262
     1263            if(isset($attachment_rest_data['description']['rendered'])) {
     1264                $update = array(
     1265                    'ID' => $attach_id,
     1266                    'post_content' => strip_tags($attachment_rest_data['description']['rendered']),
     1267                );
     1268                wp_update_post($update);
     1269            }
     1270
     1271            // Image Credit fields
     1272            $fields = array(
     1273                'media_credit_text',
     1274                'media_credit_url',
     1275                'media_credit_supplied',
     1276                'supplied_notes',
     1277                'media_credit_original',
     1278                'media_credit_ai_generated',
     1279            );
     1280
     1281            foreach($fields as $field) {
     1282                if(isset($attachment_rest_data['image_credit'][$field])) {
     1283                    if($attachment_rest_data['image_credit'][$field] === true) {
     1284                        $attachment_rest_data['image_credit'][$field] = '1';
     1285                    } elseif($attachment_rest_data['image_credit'][$field] === false) {
     1286                        $attachment_rest_data['image_credit'][$field] = '0';
     1287                    }
     1288                    update_post_meta($attach_id, $field, sanitize_text_field($attachment_rest_data['image_credit'][$field]));
     1289                }
     1290            }
     1291
    11111292            if(!file_exists($file)) {
    11121293                $this->log("Image doesn't exist after processing " . $file);
     
    11151296
    11161297        }
    1117 
    1118        
    11191298
    11201299        return $attach_id;
     
    11651344    }
    11661345
     1346    private function get_image_id_from_classes($class_string) {
     1347        // If given a string of classes, return just the Image ID (if it exists)
     1348        if(preg_match('/wp-image-(\d+)/', $class_string, $matches)) {
     1349            return intval($matches[1]);
     1350        }
     1351        return null;
     1352    }
     1353
     1354    public function fields_image_credits_acf() {
     1355        if (!function_exists('acf_add_local_field_group')) {
     1356            return;
     1357        }
     1358
     1359        acf_add_local_field_group(array(
     1360            'key' => 'group_683a92f3ef691',
     1361            'title' => 'Media Source',
     1362            'fields' => array(
     1363                array(
     1364                    'key' => 'field_683a92f4ab28d',
     1365                    'label' => 'Media Credit',
     1366                    'name' => 'media_credit_text',
     1367                    'aria-label' => '',
     1368                    'type' => 'text',
     1369                    'instructions' => 'Store the name of the photographer or stock photo website here.',
     1370                    'required' => 0,
     1371                    'conditional_logic' => 0,
     1372                    'wrapper' => array(
     1373                        'width' => '',
     1374                        'class' => '',
     1375                        'id' => '',
     1376                    ),
     1377                    'relevanssi_exclude' => 1,
     1378                    'default_value' => '',
     1379                    'maxlength' => '',
     1380                    'allow_in_bindings' => 0,
     1381                    'placeholder' => '',
     1382                    'prepend' => '',
     1383                    'append' => '',
     1384                ),
     1385                array(
     1386                    'key' => 'field_683a9338ab28e',
     1387                    'label' => 'Media Credit URL',
     1388                    'name' => 'media_credit_url',
     1389                    'aria-label' => '',
     1390                    'type' => 'url',
     1391                    'instructions' => 'Paste the link to the original media source.',
     1392                    'required' => 0,
     1393                    'conditional_logic' => 0,
     1394                    'wrapper' => array(
     1395                        'width' => '',
     1396                        'class' => '',
     1397                        'id' => '',
     1398                    ),
     1399                    'relevanssi_exclude' => 1,
     1400                    'default_value' => '',
     1401                    'allow_in_bindings' => 0,
     1402                    'placeholder' => '',
     1403                ),
     1404                array(
     1405                    'key' => 'field_683d2a1832a6d',
     1406                    'label' => 'Supplied?',
     1407                    'name' => 'media_credit_supplied',
     1408                    'aria-label' => '',
     1409                    'type' => 'true_false',
     1410                    'instructions' => 'If this image has been supplied with permission to use it on your website, tick this box. This is sometimes the case with press releases. However, you should still ensure you have permission to use it from the original creator or photographer. If you downloaded this photo from a stock website, do not tick this box.',
     1411                    'required' => 0,
     1412                    'conditional_logic' => 0,
     1413                    'wrapper' => array(
     1414                        'width' => '',
     1415                        'class' => '',
     1416                        'id' => '',
     1417                    ),
     1418                    'relevanssi_exclude' => 1,
     1419                    'message' => '',
     1420                    'default_value' => 0,
     1421                    'allow_in_bindings' => 0,
     1422                    'ui' => 0,
     1423                    'ui_on_text' => '',
     1424                    'ui_off_text' => '',
     1425                ),
     1426                array(
     1427                    'key' => 'field_6853aa7b51fdd',
     1428                    'label' => 'Supplied Notes',
     1429                    'name' => 'supplied_notes',
     1430                    'aria-label' => '',
     1431                    'type' => 'textarea',
     1432                    'instructions' => 'Please note who who gave you permission to use this image. This is an internal field and won\'t be displayed to end-users of your website.',
     1433                    'required' => 0,
     1434                    'conditional_logic' => array(
     1435                        array(
     1436                            array(
     1437                                'field' => 'field_683d2a1832a6d',
     1438                                'operator' => '==',
     1439                                'value' => '1',
     1440                            ),
     1441                        ),
     1442                    ),
     1443                    'wrapper' => array(
     1444                        'width' => '',
     1445                        'class' => '',
     1446                        'id' => '',
     1447                    ),
     1448                    'relevanssi_exclude' => 0,
     1449                    'default_value' => '',
     1450                    'maxlength' => '',
     1451                    'allow_in_bindings' => 0,
     1452                    'rows' => '',
     1453                    'placeholder' => '',
     1454                    'new_lines' => '',
     1455                ),
     1456                array(
     1457                    'key' => 'field_683d2af832a6e',
     1458                    'label' => 'Original?',
     1459                    'name' => 'media_credit_original',
     1460                    'aria-label' => '',
     1461                    'type' => 'true_false',
     1462                    'instructions' => 'If you have created this image yourself, tick this box.',
     1463                    'required' => 0,
     1464                    'conditional_logic' => 0,
     1465                    'wrapper' => array(
     1466                        'width' => '',
     1467                        'class' => '',
     1468                        'id' => '',
     1469                    ),
     1470                    'relevanssi_exclude' => 1,
     1471                    'message' => '',
     1472                    'default_value' => 0,
     1473                    'allow_in_bindings' => 0,
     1474                    'ui' => 0,
     1475                    'ui_on_text' => '',
     1476                    'ui_off_text' => '',
     1477                ),
     1478                array(
     1479                    'key' => 'field_683d2b2732a6f',
     1480                    'label' => 'AI Generated?',
     1481                    'name' => 'media_credit_ai_generated',
     1482                    'aria-label' => '',
     1483                    'type' => 'true_false',
     1484                    'instructions' => 'If this image was created with Artificial Intelligence (AI) tools, tick this box. Usage of AI media creation tools is still a grey area, and caution should be exercised.',
     1485                    'required' => 0,
     1486                    'conditional_logic' => 0,
     1487                    'wrapper' => array(
     1488                        'width' => '',
     1489                        'class' => '',
     1490                        'id' => '',
     1491                    ),
     1492                    'relevanssi_exclude' => 1,
     1493                    'message' => '',
     1494                    'default_value' => 0,
     1495                    'allow_in_bindings' => 0,
     1496                    'ui' => 0,
     1497                    'ui_on_text' => '',
     1498                    'ui_off_text' => '',
     1499                ),
     1500            ),
     1501            'location' => array(
     1502                array(
     1503                    array(
     1504                        'param' => 'attachment',
     1505                        'operator' => '==',
     1506                        'value' => 'all',
     1507                    ),
     1508                ),
     1509            ),
     1510            'menu_order' => 0,
     1511            'position' => 'normal',
     1512            'style' => 'seamless',
     1513            'label_placement' => 'top',
     1514            'instruction_placement' => 'label',
     1515            'hide_on_screen' => '',
     1516            'active' => true,
     1517            'description' => '',
     1518            'show_in_rest' => 0,
     1519        ));
     1520    }
     1521
     1522    public function fields_image_credits_api_header($headers) {
     1523        // Header to disable embedding image credits in
     1524        $headers['X-RESTful-Syndication-Hide-Image-Credits'] = 'True';
     1525
     1526        return $headers;
     1527    }
     1528
    11671529    private function random_str($length, $keyspace = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()-=+`~') {
    11681530        // From https://stackoverflow.com/a/31284266/2888733
  • restful-syndication/trunk/readme.txt

    r3214629 r3384901  
    11=== RESTful Content Syndication ===
    22Contributors: anthonyeden
    3 Tags: syndication, rest, wp-rest
    4 Stable Tag: 1.5.0
     3Tags: syndication, rest, wp-rest, wp-json, json
     4Stable Tag: 1.6.0
    55Requires at least: 6.0.0
    6 Tested up to: 6.7.1
     6Tested up to: 6.8.3
    77Requires PHP: 7.4.0
    88License: GPLv2 or later
     
    5959
    6060== Changelog ==
     61
     62= 1.6.0 =
     63
     64* Image Ingest: Import meta fields where available (title, caption, alt text)
     65* Image Credits: Add option to enable these new media credit fields via the plugin. This option is only available if you have the Advanced Custom Fields plugin active on your site.
     66* Add support for Author taxonomy
     67* Content Push: Return additional fields upon successful push
     68* Content Push: GMT date should align with local date
     69* Content Push: Provide auth key back to source site for subsequent resource pulls
     70* Use \Dom\HTMLDocument where available (in PHP 8.4)
     71* Add new filter: restful_syndication_api_headers
     72* Security: Ensure only image files are downloaded and stored locally (thanks Wordfence/kr0d for responsibly disclosing)
    6173
    6274= 1.5.0 =
Note: See TracChangeset for help on using the changeset viewer.