PluginProbe ʕ •ᴥ•ʔ
Advanced Ads – Ad Manager & AdSense / 2.0.25
Advanced Ads – Ad Manager & AdSense v2.0.25
2.0.25 2.0.24 2.0.23 2.0.22 2.0.21 1.38.0 1.39.0 1.39.1 1.39.2 1.39.3 1.39.4 1.4.0 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.40.0 1.40.1 1.40.2 1.41.0 1.42.0 1.42.1 1.42.2 1.43.0 1.44.0 1.44.1 1.45.0 1.45.1 1.46.0 1.47.0 1.47.1 1.47.2 1.47.3 1.47.4 1.47.5 1.48.0 1.48.1 1.49.0 1.5.0 1.5.0.1 1.5.1 1.5.2 1.5.2.1 1.5.4 1.5.4.1 1.5.5 1.50.0 1.51.0 1.51.1 1.51.2 1.51.3 1.52.0 1.52.1 1.52.2 1.52.3 1.52.4 1.53.0 1.53.1 1.53.2 1.54.0 1.54.1 1.55.0 1.56.0 1.56.1 1.56.2 1.56.3 1.56.4 1.6 1.6.1 1.6.10 1.6.10.1 1.6.10.2 1.6.11 1.6.11.1 1.6.12 1.6.13 1.6.14 1.6.15 1.6.16 1.6.17 1.6.17.1 1.6.17.2 1.6.2 1.6.2.1 1.6.3 1.6.4 1.6.4.1 1.6.5 1.6.6 1.6.6.1 1.6.7 1.6.7.1 1.6.8 1.6.8.1 1.6.8.2 1.6.8.3 1.6.9 1.6.9.1 1.6.9.2 1.6.9.3 1.6.9.4 1.7 1.7.0.1 1.7.0.2 1.7.0.3 1.7.1 1.7.1.1 1.7.1.2 1.7.1.3 1.7.1.4 1.7.1.5 1.7.10 trunk 1.7.11 1.0.1 1.7.12 1.0.2 1.7.13 1.0.3 1.7.14 1.1.0 1.7.15 1.1.1 1.7.16 1.1.2 1.7.17 1.1.3 1.7.18 1.10 1.7.19 1.10.1 1.7.2 1.10.10 1.7.2.1 1.10.11 1.7.20 1.10.12 1.7.21 1.10.2 1.7.22 1.10.3 1.7.23 1.10.4 1.7.24 1.10.5 1.7.25 1.10.6 1.7.3 1.10.7 1.7.4 1.10.8 1.7.4.1 1.10.9 1.7.4.2 1.11 1.7.4.3 1.11.1 1.7.4.4 1.11.2 1.7.4.5 1.12 1.7.5 1.13 1.7.5.1 1.13.1 1.7.6 1.13.2 1.7.7 1.13.3 1.7.8 1.13.4 1.7.9 1.13.5 1.7.9.1 1.13.6 1.7.9.2 1.13.7 1.7.9.3 1.13.8 1.8 1.14 1.8.1 1.14.1 1.8.10 1.14.10 1.8.11 1.14.11 1.8.12 1.14.2 1.8.13 1.14.3 1.8.14 1.14.4 1.8.15 1.14.5 1.8.16 1.14.6 1.8.17 1.14.7 1.8.18 1.14.8 1.8.19 1.14.9 1.8.2 1.15 1.8.20 1.16 1.8.21 1.16.1 1.8.22 1.17 1.8.23 1.17.1 1.8.24 1.17.10 1.8.25 1.17.10-rc.1 1.8.26 1.17.11 1.8.27 1.17.12 1.8.28 1.17.12-rc.1 1.8.29 1.17.2 1.8.3 1.17.3 1.8.30 1.17.4 1.8.4 1.17.5 1.8.5 1.17.6 1.8.6 1.17.7 1.8.7 1.17.8 1.8.8 1.17.9 1.8.9 1.17.9-beta.1 1.9 1.18.0 2.0.0 1.19.0 2.0.1 1.19.1 2.0.10 1.2 2.0.11 1.2.1 2.0.12 1.2.2 2.0.13 1.2.3 2.0.14 1.2.4 2.0.15 1.2.5 2.0.16 1.2.6 2.0.17 1.2.7 2.0.18 1.20.0 2.0.19 1.20.0-rc.1 2.0.2 1.20.0-rc.2 2.0.20 1.20.1 2.0.3 1.20.2 2.0.4 1.20.3 2.0.5 1.21.0 2.0.6 1.21.1 2.0.7 1.22.0 2.0.8 1.22.1 2.0.9 1.22.2 1.23.0 1.23.1 1.23.2 1.24.0 1.24.1 1.24.2 1.25.0 1.25.1 1.26.0 1.27.0 1.28.0 1.29.0 1.29.1 1.3 1.3.1 1.3.10 1.3.11 1.3.12 1.3.13 1.3.14 1.3.15 1.3.16 1.3.17 1.3.18 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.3.7 1.3.8 1.3.9 1.30.0 1.30.1 1.30.2 1.30.2-rc.1 1.30.3 1.30.4 1.30.4-rc.1 1.30.5 1.31.0 1.31.1 1.32.0 1.32.0-rc.1 1.33.0 1.33.1 1.33.2 1.34.0 1.35.0 1.35.1 1.36.0 1.36.1 1.36.2 1.36.3 1.37.0 1.37.1 1.37.2
advanced-ads / modules / ads-txt / admin / class-advanced-ads-ads-txt-admin.php
advanced-ads / modules / ads-txt / admin Last commit date
views 2 weeks ago class-advanced-ads-ads-txt-admin.php 2 weeks ago
class-advanced-ads-ads-txt-admin.php
527 lines
1 <?php // phpcs:ignoreFile
2
3 use AdvancedAds\Framework\Utilities\Params;
4 use AdvancedAds\Utilities\Conditional;
5
6 /**
7 * User interface for managing the 'ads.txt' file.
8 */
9 class Advanced_Ads_Ads_Txt_Admin {
10 /**
11 * Ads.txt data management class
12 *
13 * @var Advanced_Ads_Ads_Txt_Strategy
14 */
15 private $strategy;
16
17 /**
18 * Ads.txt frontend logic class
19 *
20 * @var Advanced_Ads_Ads_Txt_Public
21 */
22 private $public;
23
24 /**
25 * AdSense network ID.
26 */
27 const adsense = 'adsense';
28
29 const ACTION = 'wp_ajax_advads-ads-txt';
30
31 /**
32 * Whether the notices should be updated via AJAX because no cached data exists.
33 *
34 * @var bool
35 */
36 private $notices_are_stale = false;
37
38 /**
39 * Constructor
40 *
41 * @param Advanced_Ads_Ads_Txt_Strategy $strategy Ads.txt data management class.
42 * @param Advanced_Ads_Ads_Txt_Public $public Ads.txt frontend logic class.
43 */
44 public function __construct( Advanced_Ads_Ads_Txt_Strategy $strategy, Advanced_Ads_Ads_Txt_Public $public ) {
45 $this->strategy = $strategy;
46 $this->public = $public;
47
48 add_filter( 'advanced-ads-sanitize-settings', [ $this, 'toggle' ], 10, 1 );
49 add_action( 'pre_update_option_advanced-ads-adsense', [ $this, 'update_adsense_option' ], 10, 2 );
50 add_action( 'advanced-ads-settings-init', [ $this, 'add_settings' ] );
51 add_action( self::ACTION, [ $this, 'ajax_refresh_notices' ] );
52 }
53
54
55 /**
56 * Toggle ads.txt and add additional content.
57 *
58 * @param array $options Options.
59 * @return array $options Options.
60 */
61 public function toggle( $options ) {
62 // phpcs:disable WordPress.Security.NonceVerification.Missing
63 $create = ! empty( Params::post( 'advads-ads-txt-create' ) );
64 $all_network = ! empty( Params::post( 'advads-ads-txt-all-network' ) );
65 $additional_content = trim( wp_unslash( Params::post( 'advads-ads-txt-additional-content', '' ) ) );
66 // phpcs:enable
67
68 $this->strategy->toggle( $create, $all_network, $additional_content );
69
70 if ( ! $this->is_adsense_disabled() ) {
71 $content = $this->get_adsense_blog_data();
72 $this->strategy->add_network_data( self::adsense, $content );
73 }
74
75 $r = $this->strategy->save_options();
76
77 if ( is_wp_error( $r ) ) {
78 add_settings_error(
79 'advanced-ads-adsense',
80 'adsense-ads-txt-created',
81 $r->get_error_message(),
82 'error'
83 );
84 }
85
86 return $options;
87 }
88
89 /**
90 * Update the 'ads.txt' file every time the AdSense settings are saved.
91 * The reason for not using `update_option_*` filter is that the function
92 * should also get called for newly added AdSense options.
93 *
94 * @param array $new New options.
95 * @param array $prev Previous options.
96 * @return array $new New options.
97 */
98 public function update_adsense_option( $new, $prev ) {
99 if ( $new === $prev ) {
100 return $new;
101 }
102
103 if ( ! $this->is_adsense_disabled() ) {
104 $content = $this->get_adsense_blog_data( $new );
105 $this->strategy->add_network_data( self::adsense, $content );
106 $r = $this->strategy->save_options();
107
108 if ( is_wp_error( $r ) ) {
109 add_settings_error(
110 'advanced-ads-adsense',
111 'adsense-ads-txt-created',
112 $r->get_error_message(),
113 'error'
114 );
115 }
116 }
117
118 return $new;
119 }
120
121 /**
122 * Add setting fields.
123 *
124 * @param string $hook The slug-name of the settings page.
125 */
126 public function add_settings( $hook ) {
127 $adsense_data = Advanced_Ads_AdSense_Data::get_instance();
128 $adsense_id = $adsense_data->get_adsense_id();
129
130 add_settings_section(
131 'advanced_ads_ads_txt_setting_section',
132 'ads.txt',
133 [ $this, 'render_ads_txt_section_callback' ],
134 $hook
135 );
136
137 add_settings_field(
138 'adsense-ads-txt-enable',
139 '',
140 [ $this, 'render_setting_toggle' ],
141 $hook,
142 'advanced_ads_ads_txt_setting_section'
143 );
144
145 add_settings_field(
146 'adsense-ads-txt-content',
147 '',
148 [ $this, 'render_setting_additional_content' ],
149 $hook,
150 'advanced_ads_ads_txt_setting_section'
151 );
152 }
153
154 public function render_ads_txt_section_callback() {}
155
156 /**
157 * Render toggle settings.
158 */
159 public function render_setting_toggle() {
160 global $current_blog;
161 $domain = isset( $current_blog->domain ) ? $current_blog->domain : '';
162
163 $can_process_all_network = $this->can_process_all_network();
164 $is_all_network = $this->strategy->is_all_network();
165
166 $is_enabled = $this->strategy->is_enabled();
167 include dirname( __FILE__ ) . '/views/setting-create.php';
168 }
169
170 /**
171 * Render additional content settings.
172 */
173 public function render_setting_additional_content() {
174 $content = $this->strategy->get_additional_content();
175 $notices = $this->get_notices();
176 $notices = $this->get_notices_markup( $notices );
177 $link = home_url( '/' ) . 'ads.txt';
178 $adsense_line = $this->get_adsense_blog_data();
179
180 $adsense_disabled = $this->is_adsense_disabled();
181
182 include dirname( __FILE__ ) . '/views/setting-additional-content.php';
183 }
184
185 /**
186 * Check if other sites of the network can be processed by the user.
187 *
188 * @return bool
189 */
190 private function can_process_all_network() {
191 return ! Advanced_Ads_Ads_Txt_Utils::is_subdir()
192 && is_super_admin()
193 && is_multisite();
194 }
195
196 /**
197 * Get notices.
198 *
199 * @return array Array of notices.
200 */
201 public function get_notices() {
202 $url = home_url( '/' );
203 $parsed_url = wp_parse_url( $url );
204 $notices = [];
205
206 if ( ! isset( $parsed_url['scheme'] ) || ! isset ( $parsed_url['host'] ) ) {
207 return $notices;
208 }
209
210 $link = sprintf( '<a href="%1$s" target="_blank">%1$s</a>', esc_url( $url . 'ads.txt' ) );
211 $button = ' <button type="button" class="advads-ads-txt-action button" style="vertical-align: middle;" id="%s">%s</button>';
212
213 if ( ! $this->strategy->is_enabled() ) {
214 return $notices;
215 }
216
217 if ( Advanced_Ads_Ads_Txt_Utils::is_subdir() ) {
218 $notices[] = [
219 'advads-error-message',
220 sprintf(
221 /* translators: %s homepage link */
222 esc_html__( 'The ads.txt file cannot be placed because the URL contains a subdirectory. You need to make the file available at %s', 'advanced-ads' ),
223 sprintf( '<a href="%1$s" target="_blank">%1$s</a>', esc_url( $parsed_url['scheme'] . '://' . $parsed_url['host'] ) )
224 ),
225 ];
226 } else {
227 if ( null === ( $file = $this->get_notice( 'get_file_info', $url ) ) ) {
228 $this->notices_are_stale = true;
229 return $notices;
230 }
231
232 if ( ! is_wp_error( $file ) ) {
233 if ( $file['exists'] ) {
234 $notices[] = [
235 '',
236 sprintf(
237 /* translators: %s link of ads.txt */
238 esc_html__( 'The file is available on %s.', 'advanced-ads' ),
239 $link
240 ),
241 ];
242 } else {
243 $notices[] = [ '', esc_html__( 'The file was not created.', 'advanced-ads' ) ];
244 }
245
246 if ( $file['is_third_party'] ) {
247 /* translators: %s link */
248 $message = sprintf( esc_html__( 'A third-party file exists: %s', 'advanced-ads' ), $link );
249
250 if ( $this->can_edit_real_file() ) {
251 $message .= sprintf( $button, 'advads-ads-txt-remove-real', __( 'Import & Replace', 'advanced-ads' ) );
252 $message .= '<p class="description">'
253 . __( 'Move the content of the existing ads.txt file into Advanced Ads and remove it.', 'advanced-ads' )
254 . '</p>';
255 }
256 $notices['is_third_party'] = [ 'advads-error-message', $message ];
257 }
258 } else {
259 $notices[] = [
260 'advads-error-message',
261 sprintf(
262 /* translators: %s is replaced with an error message. */
263 esc_html__( 'An error occured: %s.', 'advanced-ads' ),
264 esc_html( $file->get_error_message() )
265 ),
266 ];
267 }
268
269 $need_file_on_root_domain = $this->get_notice( 'need_file_on_root_domain', $url );
270 if ( null === $need_file_on_root_domain ) {
271 $this->notices_are_stale = true;
272 return $notices;
273 }
274
275 if ( $need_file_on_root_domain ) {
276 $notices[] = [
277 'advads-ads-txt-nfor',
278 sprintf(
279 /* translators: %s the line that may need to be added manually */
280 esc_html__( 'If your site is located on a subdomain, you need to add the following line to the ads.txt file of the root domain: %s', 'advanced-ads' ),
281 // Without http://.
282 '<code>subdomain=' . esc_html( $parsed_url['host'] ) . '</code>'
283 ),
284 ];
285 }
286 }
287
288 return $notices;
289 }
290
291 /**
292 * Get HTML markup of the notices.
293 *
294 * @param array $notices Notices.
295 * @return string $r HTML markup.
296 */
297 private function get_notices_markup( $notices ) {
298 if ( $this->notices_are_stale ) {
299 // Do not print `ul` to fetch notices via AJAX.
300 return '';
301 }
302
303 $r = '<ul id="advads-ads-txt-notices">';
304 foreach ( $notices as $notice ) {
305 $r .= sprintf( '<li class="%s">%s</li>', $notice[0], $notice[1] );
306 }
307 $r .= '</ul>';
308 return $r;
309 }
310
311 /**
312 * Check if the `ads.txt` file is displayed to visitors.
313 *
314 * @return bool True if displayed, False otherwise.
315 */
316 public static function is_displayed() {
317 $url = home_url( '/' );
318
319 $file = self::get_notice( 'get_file_info', $url );
320 return is_array( $file ) && ! empty( $file['exists'] );
321 }
322
323 /**
324 * Get a notice.
325 *
326 * @return null/bool Boolean on success or null if no cached data exists.
327 * In the latter case, this function should be called using AJAX
328 * to get fresh data.
329 */
330 public static function get_notice( $func, $url ) {
331 if ( ! method_exists( 'Advanced_Ads_Ads_Txt_Utils', $func ) ) {
332 return false;
333 }
334
335 $url = $url ? $url : home_url( '/' );
336 $key = self::get_transient_key();
337 $transient = get_transient( $key );
338
339 if ( ! wp_doing_ajax() || ! doing_action( self::ACTION ) ) {
340 return isset( $transient[ $func ] ) ? $transient[ $func ] : null;
341 }
342
343 $r = call_user_func( [ 'Advanced_Ads_Ads_Txt_Utils', $func ], $url );
344
345 $transient = is_array( $transient ) ? $transient : [];
346 $transient[ $func ] = $r;
347 set_transient( $key, $transient, WEEK_IN_SECONDS );
348 return $r;
349 }
350
351 /**
352 * Get Adsense data.
353 *
354 * @param array $new New data.
355 *
356 * @return string
357 */
358 public function get_adsense_blog_data( $new = null ) {
359 if ( null === $new ) {
360 $new = Advanced_Ads_AdSense_Data::get_instance()->get_options();
361 }
362
363 $adsense_id = ! empty( $new['adsense-id'] ) ? trim( $new['adsense-id'] ) : '';
364 if ( ! $adsense_id ) {
365 return '';
366 }
367
368 $data = [
369 'domain' => 'google.com',
370 'account_id' => $adsense_id,
371 'account_type' => 'DIRECT',
372 'certification_authority' => 'f08c47fec0942fa0',
373 ];
374 $result = implode( ', ', $data );
375
376 return $result;
377 }
378
379 /**
380 * Check if the AdSense ads.txt record is disabled.
381 *
382 * @return bool
383 */
384 private function is_adsense_disabled() {
385 $opts = $this->strategy->get_options();
386 return ! empty( $opts['networks'][ self::adsense ]['disabled'] );
387 }
388
389 /**
390 * Handle AJAX requests for ads.txt notices and actions.
391 */
392 public function ajax_refresh_notices() {
393
394 check_ajax_referer( 'advanced-ads-admin-ajax-nonce', 'nonce' );
395
396 if ( ! Conditional::user_can( 'advanced_ads_manage_options' ) ) {
397 return;
398 }
399
400 $response = [];
401 $action_notices = [];
402
403 $request_type = Params::request( 'type' );
404 if ( $request_type ) {
405 if ( 'remove_real_file' === $request_type ) {
406 $remove = $this->remove_real_file();
407 if ( is_wp_error( $remove ) ) {
408 $action_notices[] = [ 'advads-ads-txt-updated advads-notice-inline advads-error', $remove->get_error_message() ];
409 } else {
410 $action_notices[] = [
411 'advads-ads-txt-updated',
412 __( 'The ads.txt is now managed with Advanced Ads.', 'advanced-ads' )
413 ];
414 $options = $this->strategy->get_options();
415 $response['additional_content'] = esc_textarea( $options['custom'] );
416 }
417 }
418
419 if ( 'create_real_file' === $request_type ) {
420 $action_notices[] = $this->create_real_file();
421 }
422
423 if ( 'toggle_adsense' === $request_type ) {
424 $disabled = $this->is_adsense_disabled();
425
426 if ( $disabled ) {
427 // Re-enable: restore the record and clear the disabled flag.
428 $content = $this->get_adsense_blog_data();
429 $this->strategy->add_network_data( self::adsense, $content );
430 $this->strategy->set_network_disabled( self::adsense, false );
431 $response['adsense_line'] = $content;
432 } else {
433 // Disable: clear the record and set the disabled flag.
434 $this->strategy->add_network_data( self::adsense, '' );
435 $this->strategy->set_network_disabled( self::adsense, true );
436 }
437
438 $this->strategy->save_options();
439 $response['adsense_disabled'] = ! $disabled;
440 }
441 }
442
443 $notices = $this->get_notices();
444 $notices = array_merge( $notices, $action_notices );
445 $response['notices'] = $this->get_notices_markup( $notices );
446
447 wp_send_json( $response );
448 }
449
450 /**
451 * Connect to the filesystem.
452 */
453 private function fs_connect() {
454 global $wp_filesystem;
455 $fs_connect = Advanced_Ads_Filesystem::get_instance()->fs_connect( [ ABSPATH ] );
456
457 if ( false === $fs_connect || is_wp_error( $fs_connect ) ) {
458 $message = __( 'Unable to connect to the filesystem. Please confirm your credentials.', 'advanced-ads' );
459
460 if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->get_error_code() ) {
461 $message = esc_html( $wp_filesystem->errors->get_error_message() );
462 }
463 if ( is_wp_error( $fs_connect ) && $fs_connect->get_error_code() ) {
464 $message = esc_html( $fs_connect->get_error_message() );
465 }
466 return new WP_Error( 'can_not_connect', $message );
467 }
468 return true;
469 }
470
471 /**
472 * Remove existing real ads.txt file.
473 */
474 public function remove_real_file() {
475 global $wp_filesystem;
476
477 if ( ! $this->can_edit_real_file() ) {
478 return new WP_Error( 'not_main_site', __( 'Not the main blog', 'advanced-ads' ) );
479 }
480
481 $fs_connect = $this->fs_connect();
482 if ( is_wp_error( $fs_connect ) ) {
483 return $fs_connect;
484 }
485
486 $abspath = trailingslashit( $wp_filesystem->abspath() );
487 $file = $abspath . 'ads.txt';
488 if ( $wp_filesystem->exists( $file ) && $wp_filesystem->is_file( $file ) ) {
489 $data = $wp_filesystem->get_contents( $file );
490
491 $tp_file = new Advanced_Ads_Ads_Txt_Real_File();
492 $tp_file->parse_file( $data );
493
494 $aa_data = $this->public->get_frontend_output();
495 $aa_file = new Advanced_Ads_Ads_Txt_Real_File();
496 $aa_file->parse_file( $aa_data );
497
498 $tp_file->subtract( $aa_file );
499 $output = $tp_file->output();
500 $this->strategy->set_additional_content( $output );
501 $this->strategy->save_options();
502
503 if ( $wp_filesystem->delete( $file ) ) {
504 return true;
505 } else {
506 return new WP_Error( 'could_not_delete', __( 'Could not delete the existing ads.txt file', 'advanced-ads' ) );
507 }
508 } else {
509 return new WP_Error( 'not_found', __( 'Could not find the existing ads.txt file', 'advanced-ads' ) );
510 }
511 }
512
513 /**
514 * Check if the user is allowed to edit real file.
515 */
516 private function can_edit_real_file() {
517 return is_super_admin();
518 }
519
520 /**
521 * Get transient key.
522 */
523 public static function get_transient_key() {
524 return 'advanced_ads_ads_txt_ctp' . home_url( '/' );
525 }
526 }
527