PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.0.1
Jetpack – WP Security, Backup, Speed, & Growth v16.0.1
16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / modules / seo-tools / class-jetpack-seo-titles.php
jetpack / modules / seo-tools Last commit date
class-jetpack-seo-admin-columns.php 3 weeks ago class-jetpack-seo-posts.php 3 weeks ago class-jetpack-seo-titles.php 3 years ago class-jetpack-seo-utils.php 7 months ago class-jetpack-seo.php 1 month ago
class-jetpack-seo-titles.php
351 lines
1 <?php
2 /**
3 * Class containing utility static methods for managing SEO custom title formats.
4 *
5 * @package automattic/jetpack
6 */
7
8 /*
9 * Each title format is an array of arrays containing two values:
10 * - type
11 * - value
12 *
13 * Possible values for type are: 'token' and 'string'.
14 * Possible values for 'value' are: any string in case that 'type' is set
15 * to 'string', or allowed token values for page type in case that 'type'
16 * is set to 'token'.
17 *
18 * Examples of valid formats:
19 *
20 * [
21 * 'front_page' => [
22 * [ 'type' => 'string', 'value' => 'Front page title and site name:'],
23 * [ 'type' => 'token', 'value' => 'site_name']
24 * ],
25 * 'posts' => [
26 * [ 'type' => 'token', 'value' => 'site_name' ],
27 * [ 'type' => 'string', 'value' => ' | ' ],
28 * [ 'type' => 'token', 'value' => 'post_title' ]
29 * ],
30 * 'pages' => [],
31 * 'groups' => [],
32 * 'archives' => []
33 * ]
34 * Custom title for given page type is created by concatenating all of the array 'value' parts.
35 * Tokens are replaced with their corresponding values for current site.
36 * Empty array signals that we are not overriding the default title for particular page type.
37 */
38
39 /**
40 * Class containing utility static methods for managing SEO custom title formats.
41 */
42 class Jetpack_SEO_Titles {
43 /**
44 * Site option name used to store custom title formats.
45 */
46 const TITLE_FORMATS_OPTION = 'advanced_seo_title_formats';
47
48 /**
49 * Retrieves custom title formats from site option.
50 *
51 * @return array Array of custom title formats, or empty array.
52 */
53 public static function get_custom_title_formats() {
54 if ( Jetpack_SEO_Utils::is_enabled_jetpack_seo() ) {
55 return get_option( self::TITLE_FORMATS_OPTION, array() );
56 }
57
58 return array();
59 }
60
61 /**
62 * Returns tokens that are currently supported for each page type.
63 *
64 * @return array Array of allowed token strings.
65 */
66 public static function get_allowed_tokens() {
67 return array(
68 'front_page' => array( 'site_name', 'tagline' ),
69 'posts' => array( 'site_name', 'tagline', 'post_title' ),
70 'pages' => array( 'site_name', 'tagline', 'page_title' ),
71 'groups' => array( 'site_name', 'tagline', 'group_title' ),
72 'archives' => array( 'site_name', 'tagline', 'date', 'archive_title' ),
73 );
74 }
75
76 /**
77 * Used to modify the default title with custom SEO title.
78 *
79 * @param string $default_title Default title for current page.
80 *
81 * @return string A custom per-post title, custom title structure with replaced tokens, or default title.
82 */
83 public static function get_custom_title( $default_title = '' ) {
84 // Don't filter title for unsupported themes.
85 if ( self::is_conflicted_theme() ) {
86 return $default_title;
87 }
88
89 $page_type = self::get_page_type();
90
91 // Keep default title if invalid page type is supplied.
92 if ( empty( $page_type ) ) {
93 return $default_title;
94 }
95
96 if ( ! Jetpack_SEO_Utils::is_enabled_jetpack_seo() ) {
97 return $default_title;
98 }
99
100 // If it's a singular -- page or post -- check for a meta title override.
101 if ( 'pages' === $page_type || 'posts' === $page_type ) {
102 $post = get_post();
103 if ( $post instanceof WP_Post ) {
104 $custom_title = get_post_meta( $post->ID, Jetpack_SEO_Posts::HTML_TITLE_META_KEY, true );
105 if ( ! empty( trim( $custom_title ) ) ) {
106 return esc_html( $custom_title );
107 }
108 }
109 }
110
111 $title_formats = self::get_custom_title_formats();
112
113 // Keep default title if user has not defined custom title for this page type.
114 if ( empty( $title_formats[ $page_type ] ) ) {
115 return $default_title;
116 }
117
118 $custom_title = '';
119 $format_array = $title_formats[ $page_type ];
120
121 foreach ( $format_array as $item ) {
122 if ( 'token' === $item['type'] ) {
123 $custom_title .= self::get_token_value( $item['value'] );
124 } else {
125 $custom_title .= $item['value'];
126 }
127 }
128
129 return esc_html( $custom_title );
130 }
131
132 /**
133 * Returns string value for given token.
134 *
135 * @param string $token_name The token name value that should be replaced.
136 *
137 * @return string Token replacement for current site, or empty string for unknown token name.
138 */
139 public static function get_token_value( $token_name ) {
140
141 switch ( $token_name ) {
142 case 'site_name':
143 return get_bloginfo( 'name' );
144
145 case 'tagline':
146 return get_bloginfo( 'description' );
147
148 case 'post_title':
149 case 'page_title':
150 return the_title_attribute( array( 'echo' => false ) );
151
152 case 'group_title':
153 return single_tag_title( '', false );
154
155 case 'date':
156 case 'archive_title':
157 return self::get_archive_title();
158
159 default:
160 return '';
161 }
162 }
163
164 /**
165 * Returns page type for current page. We need this helper in order to determine what
166 * user defined title format should be used for custom title.
167 *
168 * @return string|bool Type of current page or false if unsupported.
169 */
170 public static function get_page_type() {
171
172 if ( is_front_page() ) {
173 return 'front_page';
174 }
175
176 if ( is_category() || is_tag() || is_tax() ) {
177 return 'groups';
178 }
179
180 if ( is_archive() && ! is_author() ) {
181 return 'archives';
182 }
183
184 if ( is_page() ) {
185 return 'pages';
186 }
187
188 if ( is_singular() ) {
189 return 'posts';
190 }
191
192 return false;
193 }
194
195 /**
196 * Returns the value that should be used as a replacement for the `date` or `archive_title` tokens.
197 * For date-based archives, a date is returned. Otherwise the `post_type_archive_title` is returned.
198 *
199 * The `archive_title` token was added after the `date` token to provide a more generic option
200 * that would work for non date-based archives.
201 *
202 * @return string Token replaced string.
203 */
204 public static function get_archive_title() {
205 // If archive year, month, and day are specified.
206 if ( is_day() ) {
207 return get_the_date();
208 }
209
210 // If archive year, and month are specified.
211 if ( is_month() ) {
212 return trim( single_month_title( ' ', false ) );
213 }
214
215 // Only archive year is specified.
216 if ( is_year() ) {
217 return get_query_var( 'year' );
218 }
219
220 // Not a date based archive.
221 // An example would be "Projects" for Jetpack's Portoflio CPT.
222 return post_type_archive_title( '', false );
223 }
224
225 /**
226 * Checks if current theme is defining custom title that won't work nicely
227 * with our custom SEO title override.
228 *
229 * @return bool True if current theme sets custom title, false otherwise.
230 */
231 public static function is_conflicted_theme() {
232 /**
233 * Can be used to specify a list of themes that use their own custom title format.
234 *
235 * If current site is using one of the themes listed as conflicting,
236 * Jetpack SEO custom title formats will be disabled.
237 *
238 * @module seo-tools
239 *
240 * @since 4.4.0
241 *
242 * @param array List of conflicted theme names. Defaults to empty array.
243 */
244 $conflicted_themes = apply_filters( 'jetpack_seo_custom_title_conflicted_themes', array() );
245
246 return isset( $conflicted_themes[ get_option( 'template' ) ] );
247 }
248
249 /**
250 * Checks if a given format conforms to predefined SEO title templates.
251 *
252 * Every format type and token must be specifically allowed.
253 *
254 * @see get_allowed_tokens()
255 *
256 * @param array $title_formats Template of SEO title to check.
257 *
258 * @return bool True if the formats are valid, false otherwise.
259 */
260 public static function are_valid_title_formats( $title_formats ) {
261 $allowed_tokens = self::get_allowed_tokens();
262
263 if ( ! is_array( $title_formats ) ) {
264 return false;
265 }
266
267 foreach ( $title_formats as $format_type => $format_array ) {
268 if ( ! array_key_exists( $format_type, $allowed_tokens ) ) {
269 return false;
270 }
271
272 if ( '' === $format_array ) {
273 continue;
274 }
275
276 if ( ! is_array( $format_array ) ) {
277 return false;
278 }
279
280 foreach ( $format_array as $item ) {
281 if ( empty( $item['type'] ) || empty( $item['value'] ) ) {
282 return false;
283 }
284
285 if ( 'token' === $item['type'] ) {
286 if ( ! in_array( $item['value'], $allowed_tokens[ $format_type ], true ) ) {
287 return false;
288 }
289 }
290 }
291 }
292
293 return true;
294 }
295
296 /**
297 * Sanitizes the arbitrary user input strings for custom SEO titles.
298 *
299 * @param array $title_formats Array of custom title formats.
300 *
301 * @return array The sanitized array.
302 */
303 public static function sanitize_title_formats( $title_formats ) {
304 foreach ( $title_formats as &$format_array ) {
305 foreach ( $format_array as &$item ) {
306 if ( 'string' === $item['type'] ) {
307 // From `wp_strip_all_tags`, but omitting the `trim` portion since we want spacing preserved.
308 $item['value'] = preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $item['value'] );
309 $item['value'] = strip_tags( $item['value'] ); // phpcs:ignore WordPress.WP.AlternativeFunctions.strip_tags_strip_tags
310 $item['value'] = preg_replace( '/[\r\n\t ]+/', ' ', $item['value'] );
311 }
312 }
313 }
314 unset( $format_array );
315 unset( $item );
316
317 return $title_formats;
318 }
319
320 /**
321 * Combines the previous values of title formats, stored as array in site options,
322 * with the new values that are provided.
323 *
324 * @param array $new_formats Array containing new title formats.
325 *
326 * @return array $result Array of updated title formats, or empty array if no update was performed.
327 */
328 public static function update_title_formats( $new_formats ) {
329 $new_formats = self::sanitize_title_formats( $new_formats );
330
331 // Empty array signals that custom title shouldn't be used.
332 $empty_formats = array(
333 'front_page' => array(),
334 'posts' => array(),
335 'pages' => array(),
336 'groups' => array(),
337 'archives' => array(),
338 );
339
340 $previous_formats = self::get_custom_title_formats();
341
342 $result = array_merge( $empty_formats, $previous_formats, $new_formats );
343
344 if ( update_option( self::TITLE_FORMATS_OPTION, $result ) ) {
345 return $result;
346 }
347
348 return array();
349 }
350 }
351