PluginProbe
Parse.ly / 3.24.2
Parse.ly v3.24.2
3.24.2 3.24.1 3.24.0 3.23.7 3.23.6 3.23.5 3.23.4 3.23.3 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.17.0 3.18.0 3.18.1 3.19.0 3.19.1 3.19.2 3.19.3 3.2.0 3.2.1 3.20.0 3.20.1 3.20.2 All 106 releases
← All changes | src/class-parsely.php +177 -15 3.18.0 → 3.24.2 View file →
@@ -9,14 +9,14 @@
9 9 declare(strict_types=1);
10 10
11 11 namespace Parsely;
12 12
13 +use Parsely\Content_Helper\Suggestion_Defaults;
13 14 use Parsely\REST_API\REST_API_Controller;
14 15 use Parsely\Services\Content_API\Content_API_Service;
15 16 use Parsely\Services\Suggestions_API\Suggestions_API_Service;
16 17 use Parsely\UI\Metadata_Renderer;
17 18 use Parsely\UI\Settings_Page;
18 -use Parsely\Utils\Utils;
19 19 use WP_Post;
20 20
21 21 /**
22 22 * Holds most of the logic for the plugin.
@@ -31,8 +31,9 @@
31 31 * use_top_level_cats: bool,
32 32 * custom_taxonomy_section: string,
33 33 * cats_as_tags: bool,
34 34 * content_helper: Parsely_Options_Content_Helper,
35 + * headline_testing: Parsely_Options_Headline_Testing,
35 36 * track_authenticated_users: bool,
36 37 * lowercase_tags: bool,
37 38 * force_https_canonicals: bool,
38 39 * track_post_types: string[],
@@ -52,15 +53,28 @@
52 53 * ai_features_enabled: bool,
53 54 * smart_linking: Parsely_Options_Content_Helper_Feature,
54 55 * title_suggestions: Parsely_Options_Content_Helper_Feature,
55 56 * excerpt_suggestions: Parsely_Options_Content_Helper_Feature,
57 + * traffic_boost: Parsely_Options_Content_Helper_Feature,
56 58 * }
57 59 *
58 60 * @phpstan-type Parsely_Options_Content_Helper_Feature array{
59 61 * enabled: bool,
60 62 * allowed_user_roles: string[],
63 + * default_length?: int,
64 + * default_tone?: string,
65 + * default_persona?: string,
61 66 * }
62 67 *
68 + * @phpstan-type Parsely_Options_Headline_Testing array{
69 + * enabled: bool,
70 + * installation_method: string,
71 + * enable_flicker_control: bool,
72 + * enable_live_updates: bool,
73 + * live_update_timeout: int,
74 + * allow_after_content_load: bool,
75 + * }
76 + *
63 77 * @phpstan-type WP_HTTP_Request_Args array{
64 78 * method?: string,
65 79 * timeout?: float,
66 80 * blocking?: bool,
@@ -75,13 +89,15 @@
75 89 /**
76 90 * Declare our constants
77 91 */
78 92 public const VERSION = PARSELY_VERSION;
79 - public const MENU_SLUG = 'parsely'; // The page param passed to options-general.php.
93 + public const MENU_SLUG = 'parsely-settings'; // The page param passed to admin.php.
80 94 public const OPTIONS_KEY = 'parsely'; // The key used to store options in the WP database.
81 95 public const CAPABILITY = 'manage_options'; // The capability required to administer settings.
82 96 public const DASHBOARD_BASE_URL = 'https://dash.parsely.com';
83 97
98 + private const PARSELY_CANONICAL_URL_META_KEY = '_parsely_canonical_url';
99 +
84 100 /**
85 101 * The Content API service.
86 102 *
87 103 * @var ?Content_API_Service $content_api_service
@@ -122,14 +138,31 @@
122 138 ),
123 139 'title_suggestions' => array(
124 140 'enabled' => true,
125 141 'allowed_user_roles' => array( 'administrator' ),
142 + 'default_tone' => Suggestion_Defaults::DEFAULT_TONE,
143 + 'default_persona' => Suggestion_Defaults::DEFAULT_PERSONA,
126 144 ),
127 145 'excerpt_suggestions' => array(
128 146 'enabled' => true,
129 147 'allowed_user_roles' => array( 'administrator' ),
148 + 'default_length' => Suggestion_Defaults::DEFAULT_LENGTH,
149 + 'default_tone' => Suggestion_Defaults::DEFAULT_TONE,
150 + 'default_persona' => Suggestion_Defaults::DEFAULT_PERSONA,
130 151 ),
152 + 'traffic_boost' => array(
153 + 'enabled' => true,
154 + 'allowed_user_roles' => array( 'administrator' ),
155 + ),
131 156 ),
157 + 'headline_testing' => array(
158 + 'enabled' => false,
159 + 'installation_method' => 'one_line',
160 + 'enable_flicker_control' => false,
161 + 'enable_live_updates' => false,
162 + 'live_update_timeout' => 30000,
163 + 'allow_after_content_load' => false,
164 + ),
132 165 'track_authenticated_users' => false,
133 166 'lowercase_tags' => true,
134 167 'force_https_canonicals' => false,
135 168 'track_post_types' => array(),
@@ -347,10 +380,21 @@
347 380 */
348 381 public function get_tracker_url(): string {
349 382 if ( $this->site_id_is_set() ) {
350 383 $tracker_url = 'https://cdn.parsely.com/keys/' . $this->get_site_id() . '/p.js';
351 - return esc_url( $tracker_url );
384 +
385 + /**
386 + * Filters the URL of the Parse.ly tracker script.
387 + *
388 + * The filtered value gets sanitized with {@see esc_url_raw()}.
389 + *
390 + * @since 3.23.0
391 + *
392 + * @param string $tracker_url The URL of the tracker script.
393 + */
394 + return esc_url_raw( apply_filters( 'wp_parsely_tracker_url', $tracker_url ) );
352 395 }
396 +
353 397 return '';
354 398 }
355 399
356 400 /**
@@ -547,20 +591,42 @@
547 591 * @var Parsely_Options|null
548 592 */
549 593 $options = get_option( self::OPTIONS_KEY, null );
550 594
551 - // @phpstan-ignore isset.offset, booleanAnd.alwaysFalse
595 + // Existing plugin installation without full metadata option.
596 + /* @phpstan-ignore isset.offset, booleanAnd.alwaysFalse */
552 597 if ( is_array( $options ) && ! isset( $options['full_metadata_in_non_posts'] ) ) {
553 - // Existing plugin installation without full metadata option.
554 598 $this->set_default_full_metadata_in_non_posts();
555 599 }
556 600
557 - // @phpstan-ignore isset.offset, booleanAnd.alwaysFalse
601 + // Existing plugin installation without Content Intelligence options.
602 + /* @phpstan-ignore isset.offset, booleanAnd.alwaysFalse */
558 603 if ( is_array( $options ) && ! isset( $options['content_helper'] ) ) {
559 - // Existing plugin installation without Content Helper options.
560 604 $this->set_default_content_helper_settings_values();
561 605 }
562 606
607 + // Existing plugin installation that's missing a Content Intelligence
608 + // feature option.
609 + /* @phpstan-ignore isset.offset */
610 + if ( is_array( $options ) && isset( $options['content_helper'] ) ) {
611 + /** @var array<string,Parsely_Options_Content_Helper_Feature> $pch_options */
612 + $pch_options = $options['content_helper'];
613 +
614 + /** @var array<string,Parsely_Options_Content_Helper_Feature> $pch_options_defaults */
615 + $pch_options_defaults = $this->option_defaults['content_helper'];
616 +
617 + if ( count( $pch_options ) !== count( $pch_options_defaults ) ) {
618 + $new_keys = array_diff(
619 + array_keys( $pch_options_defaults ),
620 + array_keys( $pch_options )
621 + );
622 +
623 + foreach ( $new_keys as $key ) {
624 + $options['content_helper'][ $key ] = $pch_options_defaults[ $key ];
625 + }
626 + }
627 + }
628 +
563 629 // New plugin installation that hasn't saved its options yet.
564 630 if ( ! is_array( $options ) ) {
565 631 $this->set_default_track_as_values();
566 632 $this->set_default_full_metadata_in_non_posts();
@@ -656,9 +722,9 @@
656 722 }
657 723 }
658 724
659 725 /**
660 - * Sets the default values for Content Helper options.
726 + * Sets the default values for Content Intelligence options.
661 727 *
662 728 * Gives PCH access to all users having the edit_posts capability, to keep
663 729 * consistent behavior with plugin versions prior to 3.16.0.
664 730 *
@@ -679,9 +745,9 @@
679 745 * for context (Default null for current).
680 746 * @return string
681 747 */
682 748 public static function get_settings_url( ?int $_blog_id = null ): string {
683 - return get_admin_url( $_blog_id, 'options-general.php?page=' . self::MENU_SLUG );
749 + return get_admin_url( $_blog_id, 'admin.php?page=' . self::MENU_SLUG );
684 750 }
685 751
686 752 /**
687 753 * Returns the URL of the Parse.ly dashboard for a specific page. If a page
@@ -966,8 +1032,97 @@
966 1032 }
967 1033 }
968 1034
969 1035 /**
1036 + * Gets the Parse.ly canonical URL for a given post.
1037 + *
1038 + * @since 3.19.0
1039 + *
1040 + * @param WP_Post|int $post The post ID or post object.
1041 + * @return string The Parse.ly canonical URL.
1042 + */
1043 + public static function get_canonical_url_from_post( $post ): string {
1044 + $post_id = is_int( $post ) ? $post : $post->ID;
1045 + $canonical_url = get_post_meta( $post_id, self::PARSELY_CANONICAL_URL_META_KEY, true );
1046 +
1047 + if ( null !== $canonical_url && is_string( $canonical_url ) && '' !== $canonical_url ) {
1048 + return self::get_canonical_url( $canonical_url );
1049 + }
1050 +
1051 + $permalink = get_permalink( $post );
1052 +
1053 + if ( false === $permalink ) {
1054 + return __( 'no permalink', 'wp-parsely' );
1055 + }
1056 +
1057 + return self::get_canonical_url( $permalink );
1058 + }
1059 +
1060 + /**
1061 + * Returns the canonical version of the passed URL.
1062 + *
1063 + * In this context, the canonical URL is the URL containing the Site ID as
1064 + * its domain. If the Site ID differs from the real domain, the
1065 + * `wp_parsely_canonical_url_domain` filter can be used to set it.
1066 + *
1067 + * @since 3.19.0
1068 + * @since 3.20.4 Made the domain overridable.
1069 + *
1070 + * @param string $url The URL to get the canonical URL for.
1071 + * @return string The canonical URL.
1072 + */
1073 + public static function get_canonical_url( string $url ): string {
1074 + $canonical_url_domain = apply_filters(
1075 + 'wp_parsely_canonical_url_domain',
1076 + null
1077 + );
1078 +
1079 + // Handle domain override.
1080 + if ( is_string( $canonical_url_domain ) ) {
1081 + // Get the canonical URL domain without protocol, trailing slashes
1082 + // or accidental whitespace.
1083 + $canonical_url_domain = rtrim( trim( $canonical_url_domain ), '/' );
1084 + $canonical_url_domain = preg_replace( '#^https?://#', '', $canonical_url_domain );
1085 +
1086 + if ( is_string( $canonical_url_domain ) && '' !== $canonical_url_domain ) {
1087 + $url_domain = (string) wp_parse_url( $url, PHP_URL_HOST );
1088 + return str_replace( $url_domain, $canonical_url_domain, $url );
1089 + }
1090 + }
1091 +
1092 + $site_id = \Parsely\get_parsely()->get_site_id();
1093 + $home_url_host = (string) wp_parse_url( home_url(), PHP_URL_HOST );
1094 +
1095 + if ( $home_url_host === $site_id ) {
1096 + // URL does not need to be modified.
1097 + return $url;
1098 + }
1099 +
1100 + // Return the URL with the Site ID as the domain.
1101 + return str_replace( $home_url_host, $site_id, $url );
1102 + }
1103 +
1104 + /**
1105 + * Sets the Parse.ly canonical URL for a post.
1106 + *
1107 + * @since 3.19.0
1108 + *
1109 + * @param WP_Post|int $post The post object or post ID.
1110 + * @param string $url The canonical URL.
1111 + * @return bool True if the canonical URL was set, false otherwise.
1112 + */
1113 + public static function set_canonical_url( $post, string $url ): bool {
1114 + $post_id = is_int( $post ) ? $post : $post->ID;
1115 + $canonical_url = self::get_canonical_url( $url );
1116 +
1117 + return false !== update_post_meta(
1118 + $post_id,
1119 + self::PARSELY_CANONICAL_URL_META_KEY,
1120 + sanitize_url( $canonical_url, array( 'http', 'https' ) )
1121 + );
1122 + }
1123 +
1124 + /**
970 1125 * Sanitizes the value of the passed managed option.
971 1126 *
972 1127 * @since 3.9.0
973 1128 * @access private
@@ -1046,8 +1201,9 @@
1046 1201 *
1047 1202 * This is needed for environments, such as wp-now, that block remote requests.
1048 1203 *
1049 1204 * @since 3.13.0
1205 + * @since 3.24.2 Matches the Parse.ly hosts exactly, over HTTPS.
1050 1206 * @access private
1051 1207 */
1052 1208 private function allow_parsely_remote_requests(): void {
1053 1209 $allowed_urls = array(
@@ -1057,15 +1213,21 @@
1057 1213 );
1058 1214
1059 1215 add_filter(
1060 1216 'http_request_host_is_external',
1061 - function ( $external, $host, $url ) use ( $allowed_urls ) {
1062 - // Check if the URL matches any URLs on the allowed list.
1063 - foreach ( $allowed_urls as $allowed_url ) {
1064 - if ( Utils::str_starts_with( $url, $allowed_url ) ) {
1065 - return true;
1066 - }
1217 + function ( bool $external, string $host, string $url ) use ( $allowed_urls ) {
1218 + $scheme = strtolower( (string) wp_parse_url( $url, PHP_URL_SCHEME ) );
1219 + $allowed_hosts = array_map(
1220 + static function ( string $allowed_url ): string {
1221 + return (string) wp_parse_url( $allowed_url, PHP_URL_HOST );
1222 + },
1223 + $allowed_urls
1224 + );
1225 +
1226 + if ( 'https' === $scheme && in_array( strtolower( $host ), $allowed_hosts, true ) ) {
1227 + return true;
1067 1228 }
1229 +
1068 1230 return $external;
1069 1231 },
1070 1232 10,
1071 1233 3