PluginProbe
Yoast SEO – Advanced SEO with real-time guidance and built-in AI / trunk
Yoast SEO – Advanced SEO with real-time guidance and built-in AI vtrunk
28.5 28.4 28.3 28.2 28.1 28.0 27.9 27.8 27.7 27.6 27.5 trunk 18.0 18.1 18.2 18.3 18.4 18.4.1 18.5 18.5.1 18.6 18.7 18.8 18.9 19.0 All 129 releases
wordpress-seo / inc / options / class-wpseo-option-social.php

class-wpseo-option-social.php in Yoast SEO – Advanced SEO with real-time guidance and built-in AI trunk, at inc/options/class-wpseo-option-social.php

323 lines 9.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * WPSEO plugin file.
4 *
5 * @package WPSEO\Internals\Options
6 */
7
8 /**
9 * Option: wpseo_social.
10 */
11 class WPSEO_Option_Social extends WPSEO_Option {
12
13 /**
14 * Option name.
15 *
16 * @var string
17 */
18 public $option_name = 'wpseo_social';
19
20 /**
21 * Array of defaults for the option.
22 *
23 * Shouldn't be requested directly, use $this->get_defaults();
24 *
25 * @var array
26 */
27 protected $defaults = [
28 // Form fields.
29 'facebook_site' => '', // Text field.
30 'instagram_url' => '',
31 'linkedin_url' => '',
32 'myspace_url' => '',
33 'og_default_image' => '', // Text field.
34 'og_default_image_id' => '',
35 'og_frontpage_title' => '', // Text field.
36 'og_frontpage_desc' => '', // Text field.
37 'og_frontpage_image' => '', // Text field.
38 'og_frontpage_image_id' => '',
39 'opengraph' => true,
40 'pinterest_url' => '',
41 'pinterestverify' => '',
42 'twitter' => true,
43 'twitter_site' => '', // Text field.
44 'twitter_card_type' => 'summary_large_image',
45 'youtube_url' => '',
46 'wikipedia_url' => '',
47 'other_social_urls' => [],
48 'mastodon_url' => '',
49 ];
50
51 /**
52 * Array of sub-options which should not be overloaded with multi-site defaults.
53 *
54 * @var array
55 */
56 public $ms_exclude = [
57 /* Privacy. */
58 'pinterestverify',
59 ];
60
61 /**
62 * Array of allowed twitter card types.
63 *
64 * While we only have the options summary and summary_large_image in the
65 * interface now, we might change that at some point.
66 *
67 * {@internal Uncomment any of these to allow them in validation *and* automatically
68 * add them as a choice in the options page.}}
69 *
70 * @var array
71 */
72 public static $twitter_card_types = [
73 'summary_large_image' => '',
74 // 'summary' => '',
75 // 'photo' => '',
76 // 'gallery' => '',
77 // 'app' => '',
78 // 'player' => '',
79 // 'product' => '',
80 ];
81
82 /**
83 * Add the actions and filters for the option.
84 */
85 protected function __construct() {
86 parent::__construct();
87
88 add_filter( 'admin_title', [ 'Yoast_Input_Validation', 'add_yoast_admin_document_title_errors' ] );
89 }
90
91 /**
92 * Get the singleton instance of this class.
93 *
94 * @return object
95 */
96 public static function get_instance() {
97 if ( ! ( self::$instance instanceof self ) ) {
98 self::$instance = new self();
99 }
100
101 return self::$instance;
102 }
103
104 /**
105 * Translate/set strings used in the option defaults.
106 *
107 * @return void
108 */
109 public function translate_defaults() {
110 self::$twitter_card_types['summary_large_image'] = 'Summary with large image';
111 }
112
113 /**
114 * Validate the option.
115 *
116 * @param array $dirty New value for the option.
117 * @param array $clean Clean value for the option, normally the defaults.
118 * @param array $old Old value of the option.
119 *
120 * @return array Validated clean value for the option to be saved to the database.
121 */
122 protected function validate_option( $dirty, $clean, $old ) {
123
124 foreach ( $clean as $key => $value ) {
125 switch ( $key ) {
126 /* Text fields. */
127 case 'og_frontpage_desc':
128 case 'og_frontpage_title':
129 if ( isset( $dirty[ $key ] ) && $dirty[ $key ] !== '' ) {
130 $clean[ $key ] = WPSEO_Utils::sanitize_text_field( $dirty[ $key ] );
131 }
132 break;
133
134 case 'og_default_image_id':
135 case 'og_frontpage_image_id':
136 if ( isset( $dirty[ $key ] ) ) {
137 $clean[ $key ] = (int) $dirty[ $key ];
138
139 if ( $dirty[ $key ] === '' ) {
140 $clean[ $key ] = $dirty[ $key ];
141 }
142 }
143 break;
144
145 /* URL text fields - no ftp allowed. */
146 case 'facebook_site':
147 case 'instagram_url':
148 case 'linkedin_url':
149 case 'myspace_url':
150 case 'pinterest_url':
151 case 'og_default_image':
152 case 'og_frontpage_image':
153 case 'youtube_url':
154 case 'wikipedia_url':
155 case 'mastodon_url':
156 $this->validate_url( $key, $dirty, $old, $clean );
157 break;
158
159 case 'pinterestverify':
160 $this->validate_verification_string( $key, $dirty, $old, $clean );
161 break;
162
163 /* Twitter user name. */
164 case 'twitter_site':
165 if ( isset( $dirty[ $key ] ) && $dirty[ $key ] !== '' ) {
166 $twitter_id = $this->validate_twitter_id( $dirty[ $key ] );
167
168 if ( $twitter_id ) {
169 $clean[ $key ] = $twitter_id;
170 }
171 elseif ( isset( $old[ $key ] ) && $old[ $key ] !== '' ) {
172 $twitter_id = sanitize_text_field( ltrim( $old[ $key ], '@' ) );
173 if ( preg_match( '`^[A-Za-z0-9_]{1,25}$`', $twitter_id ) ) {
174 $clean[ $key ] = $twitter_id;
175 }
176 }
177 unset( $twitter_id );
178
179 Yoast_Input_Validation::add_dirty_value_to_settings_errors( $key, $dirty[ $key ] );
180 }
181 break;
182
183 case 'twitter_card_type':
184 if ( isset( $dirty[ $key ], self::$twitter_card_types[ $dirty[ $key ] ] ) && $dirty[ $key ] !== '' ) {
185 $clean[ $key ] = $dirty[ $key ];
186 }
187 break;
188
189 /* Boolean fields. */
190 case 'opengraph':
191 case 'twitter':
192 $clean[ $key ] = ( isset( $dirty[ $key ] ) ? WPSEO_Utils::validate_bool( $dirty[ $key ] ) : false );
193 break;
194
195 /* Array fields. */
196 case 'other_social_urls':
197 if ( isset( $dirty[ $key ] ) ) {
198 $items = $dirty[ $key ];
199 if ( ! is_array( $items ) ) {
200 $items = json_decode( $dirty[ $key ], true );
201 }
202
203 if ( is_array( $items ) ) {
204 foreach ( $items as $item_key => $item ) {
205 $validated_url = $this->validate_social_url( $item );
206
207 if ( $validated_url === false ) {
208 // Restore the previous URL values, if any.
209 $old_urls = ( isset( $old[ $key ] ) ) ? $old[ $key ] : [];
210 foreach ( $old_urls as $old_item_key => $old_url ) {
211 if ( $old_url !== '' ) {
212 $url = WPSEO_Utils::sanitize_url( $old_url );
213 if ( $url !== '' ) {
214 $clean[ $key ][ $old_item_key ] = $url;
215 }
216 }
217 }
218 break;
219 }
220
221 // The URL format is valid, let's sanitize it.
222 $url = WPSEO_Utils::sanitize_url( $validated_url );
223 if ( $url !== '' ) {
224 $clean[ $key ][ $item_key ] = $url;
225 }
226 }
227 }
228 }
229
230 break;
231 }
232 }
233
234 return $clean;
235 }
236
237 /**
238 * Validates a social URL.
239 *
240 * @param string $url The url to be validated.
241 *
242 * @return string|false The validated URL or false if the URL is not valid.
243 */
244 public function validate_social_url( $url ) {
245 $validated_url = filter_var( WPSEO_Utils::sanitize_url( trim( $url ) ), FILTER_VALIDATE_URL );
246
247 return $validated_url;
248 }
249
250 /**
251 * Validates a twitter id.
252 *
253 * @param string $twitter_id The twitter id to be validated.
254 * @param bool $strip_at_sign Whether or not to strip the `@` sign.
255 *
256 * @return string|false The validated twitter id or false if it is not valid.
257 */
258 public function validate_twitter_id( $twitter_id, $strip_at_sign = true ) {
259 $twitter_id = ( $strip_at_sign ) ? sanitize_text_field( ltrim( $twitter_id, '@' ) ) : sanitize_text_field( $twitter_id );
260
261 /*
262 * From the Twitter documentation about twitter screen names:
263 * Typically a maximum of 15 characters long, but some historical accounts
264 * may exist with longer names.
265 * A username can only contain alphanumeric characters (letters A-Z, numbers 0-9)
266 * with the exception of underscores.
267 *
268 * @link https://support.twitter.com/articles/101299-why-can-t-i-register-certain-usernames
269 */
270 if ( preg_match( '`^[A-Za-z0-9_]{1,25}$`', $twitter_id ) ) {
271 return $twitter_id;
272 }
273
274 if ( preg_match( '`^http(?:s)?://(?:www\.)?(?:twitter|x)\.com/(?P<handle>[A-Za-z0-9_]{1,25})/?$`', $twitter_id, $matches ) ) {
275 return $matches['handle'];
276 }
277
278 return false;
279 }
280
281 /**
282 * Clean a given option value.
283 *
284 * @param array $option_value Old (not merged with defaults or filtered) option value to
285 * clean according to the rules for this option.
286 * @param string|null $current_version Optional. Version from which to upgrade, if not set,
287 * version specific upgrades will be disregarded.
288 * @param array|null $all_old_option_values Optional. Only used when importing old options to have
289 * access to the real old values, in contrast to the saved ones.
290 *
291 * @return array Cleaned option.
292 */
293 protected function clean_option( $option_value, $current_version = null, $all_old_option_values = null ) {
294
295 /* Move options from very old option to this one. */
296 $old_option = null;
297 if ( isset( $all_old_option_values ) ) {
298 // Ok, we have an import.
299 if ( isset( $all_old_option_values['wpseo_indexation'] ) && is_array( $all_old_option_values['wpseo_indexation'] ) && $all_old_option_values['wpseo_indexation'] !== [] ) {
300 $old_option = $all_old_option_values['wpseo_indexation'];
301 }
302 }
303 else {
304 $old_option = get_option( 'wpseo_indexation' );
305 }
306
307 if ( is_array( $old_option ) && $old_option !== [] ) {
308 $move = [
309 'opengraph',
310 ];
311 foreach ( $move as $key ) {
312 if ( isset( $old_option[ $key ] ) && ! isset( $option_value[ $key ] ) ) {
313 $option_value[ $key ] = $old_option[ $key ];
314 }
315 }
316 unset( $move, $key );
317 }
318 unset( $old_option );
319
320 return $option_value;
321 }
322 }
323