PluginProbe
Advanced Ads – Ad Manager & AdSense / 1.40.1
Advanced Ads – Ad Manager & AdSense v1.40.1
2.0.26 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 All 348 releases
advanced-ads / modules / ad-blocker / admin / admin.php
admin.php
555 lines 19.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Ad blocker admin functionality.
4 */
5 class Advanced_Ads_Ad_Blocker_Admin {
6 /**
7 * Singleton instance of the plugin
8 *
9 * @var Advanced_Ads_Ad_Blocker_Admin
10 */
11 protected static $instance;
12
13 /**
14 * Module options
15 *
16 * @var array (if loaded)
17 */
18 protected $options;
19
20 /**
21 * Pattern to search assets using preg_match. The string ends with .css/.js/.png/.gif
22 *
23 * @var string
24 */
25 protected $search_file_pattern = '/(css|js|png|gif)$/';
26
27 /**
28 * Pattern to exclide directories from search. The string does not contain '/vendor/' or '/lib/' or '/admin/' or /node_modules/
29 *
30 * @var string
31 */
32 protected $exclude_dir_pattern = '/(\/vendor\/|\/lib\/|\/admin\/|\/node_modules\/)/';
33
34 /**
35 * Array, containing path information on the currently configured uploads directory
36 *
37 * @var array
38 */
39 protected $upload_dir;
40
41 /**
42 * Error messages for user
43 *
44 * @var WP_Error
45 */
46 protected $error_messages;
47
48 /**
49 * Initialize the module
50 *
51 */
52 private function __construct() {
53 // add module settings to Advanced Ads settings page
54 add_action( 'advanced-ads-settings-init', [ $this, 'settings_init' ], 9, 1 );
55
56 $is_main_site = is_main_site( get_current_blog_id() );
57 if ( ! $is_main_site ) {
58 return;
59 }
60
61 // Get the most recent options values
62 $this->options = Advanced_Ads_Ad_Blocker::get_instance()->options();
63 $this->upload_dir = $this->options['upload_dir'];
64
65 add_action( 'admin_init', [ $this, 'process_auto_update' ] );
66
67 $this->error_messages = new WP_Error();
68 }
69
70 /**
71 * Return an instance of Advanced_Ads_Ad_Blocker
72 *
73 * @return Advanced_Ads_Ad_Blocker_Admin
74 */
75 public static function get_instance() {
76 // If the single instance hasn't been set, set it now.
77 if (null === self::$instance)
78 {
79 self::$instance = new self;
80 }
81
82 return self::$instance;
83 }
84
85 /**
86 * Add settings to settings page.
87 *
88 * @param string $hook settings page hook
89 */
90 public function settings_init( $hook ) {
91 add_settings_field(
92 'use-adblocker',
93 __( 'Ad blocker fix', 'advanced-ads' ),
94 [ $this, 'render_settings_use_adblocker' ],
95 $hook,
96 'advanced_ads_adblocker_setting_section'
97 );
98 }
99
100 /**
101 * Render setting to enable/disable adblocker.
102 */
103 public function render_settings_use_adblocker() {
104 $is_main_site = is_main_site( get_current_blog_id() );
105 $checked = ! empty( Advanced_Ads::get_instance()->options()['use-adblocker'] );
106
107 include ADVADS_AB_BASE_PATH . 'admin/views/setting-use-adblocker.php';
108
109 // if this is a sub site in a network, don't run the rebuild form code.
110 if ( ! $is_main_site ) {
111 return;
112 }
113
114 // add the rebuild form directly after the settings
115 ?>
116 <div id="advads-adblocker-wrapper" <?php echo( $checked ? '' : 'style="display: none;"' ); ?>>
117 <?php
118 $button_disabled = true;
119 $upload_dir = $this->upload_dir;
120 $options = $this->options;
121
122 include ADVADS_AB_BASE_PATH . 'admin/views/rebuild_form.php';
123 ?>
124 </div>
125 <?php
126 }
127
128 /**
129 * Render the ad-blocker rebuild assets form
130 *
131 */
132 public function add_asset_rebuild_form() {
133 global $wp_filesystem;
134 $success = false;
135 $message = '';
136
137 $fs_connect = Advanced_Ads_Filesystem::get_instance()->fs_connect( $this->upload_dir['basedir'] );
138
139 if ( $fs_connect === false || is_wp_error( $fs_connect ) ) {
140 $message = __( 'Unable to connect to the filesystem. Please confirm your credentials.', 'advanced-ads' );
141
142 if ( $wp_filesystem instanceof WP_Filesystem_Base && is_wp_error( $wp_filesystem->errors ) && $wp_filesystem->errors->get_error_code() ) {
143 $message = esc_html( $wp_filesystem->errors->get_error_message() );
144 }
145 if ( is_wp_error( $fs_connect ) && $fs_connect->get_error_code() ) {
146 $message = esc_html( $fs_connect->get_error_message() );
147 }
148 } else {
149 $output = $this->process_form();
150 if ( is_wp_error( $output ) ) {
151 $message = $output->get_error_message();
152 } else {
153 $success = true;
154 $message = __( 'The asset folder was rebuilt successfully', 'advanced-ads' );
155 }
156 }
157
158 $upload_dir = $this->upload_dir;
159 $button_disabled = false;
160 $options = Advanced_Ads_Ad_Blocker::get_instance()->options( true );
161
162 include ADVADS_AB_BASE_PATH . 'admin/views/rebuild_form.php';
163 }
164
165 /**
166 * Perform processing of the rebuild_form, sent by user
167 *
168 * @return true|WP_Error true on success, WP_Error in case of error
169 **/
170 private function process_form() {
171 // at this point we do not need ftp/ssh credentials anymore
172 $form_post_fields = array_intersect_key( $_POST, [ 'advads_ab_assign_new_folder' => false ] );
173
174 $this->create_dummy_plugin( $form_post_fields );
175
176 if ( $error_messages = $this->error_messages->get_error_messages() ) {
177 foreach ( $error_messages as $error_message ) {
178 Advanced_Ads::log( __METHOD__ . ': ' . $error_message );
179 }
180
181 return $this->error_messages;
182 }
183
184 return true;
185 }
186
187 /**
188 * Creates dummy plugin and return new options, that need to be stored in database.
189 *
190 * @param array $form_post_fields options, POST data sent by user.
191 * @return array $new_options - options, that need to be stored in database.
192 */
193 public function create_dummy_plugin( $form_post_fields = [] ) {
194 global $wp_filesystem;
195
196 $need_assign_new_name = isset( $form_post_fields['advads_ab_assign_new_folder'] );
197
198 if ( ! $this->upload_dir ) {
199 $message = __( 'There is no writable upload folder', 'advanced-ads' );
200 $this->error_messages->add( 'create_dummy_1', $message);
201 return false;
202 }
203
204 $new_options = [
205 'lookup_table' => isset( $this->options['lookup_table'] ) ? $this->options['lookup_table'] : [],
206 ];
207 $new_options_error = $new_options;
208 // $new_options_error does not have the 'module_can_work' key - ad-blocker script will be inactive and the asset folder will be rebuilt next time
209 $new_options['module_can_work'] = true;
210
211 $existing_files = @scandir( $this->upload_dir['basedir'] );
212 if ( $existing_files ) {
213 $existing_files = array_diff( $existing_files, [ '..', '.' ] );
214 } else {
215 $existing_files = [];
216 }
217
218 if ( ! empty( $this->options['folder_name'] ) ) {
219 $new_options['folder_name'] = $new_options_error['folder_name'] = $this->options['folder_name'];
220
221 $old_folder_normalized = Advanced_Ads_Filesystem::get_instance()->normalize_path( trailingslashit( $this->upload_dir['basedir'] ) ) . $this->options['folder_name'];
222
223 if ( $wp_filesystem->exists( $old_folder_normalized ) ) {
224
225 if ( $need_assign_new_name ) {
226 $existing_files[] = (string) $new_options['folder_name'];
227 $new_folder_name = $this->generate_unique_name( $existing_files );
228 $new_folder_normalized = Advanced_Ads_Filesystem::get_instance()->normalize_path( trailingslashit( $this->upload_dir['basedir'] ) ) . $new_folder_name;
229
230 if ( ! $wp_filesystem->move( $old_folder_normalized, $new_folder_normalized ) ) {
231 $message = sprintf( __( 'Unable to rename "%s" directory', 'advanced-ads' ), $old_folder_normalized );
232 $this->error_messages->add( 'create_dummy_2', $message);
233 return false;
234 }
235 $new_options['folder_name'] = $new_options_error['folder_name'] = $new_folder_name;
236
237 }
238
239 $is_rebuild_needed = count( $this->get_assets() );
240
241 // we have an error while the method is being executed
242 Advanced_Ads_Ad_Blocker::get_instance()->update_options( $new_options_error );
243
244 if ( $is_rebuild_needed ) {
245 $lookup_table = $this->copy_assets( $new_options['folder_name'], $need_assign_new_name );
246 if ( ! $lookup_table ) {
247 $message = sprintf( __( 'Unable to copy assets to the "%s" directory', 'advanced-ads' ), $new_options['folder_name'] );
248 $this->error_messages->add( 'create_dummy_3', $message);
249 return false;
250 }
251 $new_options['lookup_table'] = $lookup_table;
252 }
253
254 } else {
255 // we have an error while the method is being executed
256 Advanced_Ads_Ad_Blocker::get_instance()->update_options( $new_options_error );
257 // old folder does not exist, let's create it
258 $lookup_table = $this->copy_assets( $new_options['folder_name'] );
259 if ( ! $lookup_table ) {
260 $message = sprintf( __( 'Unable to copy assets to the "%s" directory', 'advanced-ads' ), $new_options['folder_name'] );
261 $this->error_messages->add( 'create_dummy_4', $message);
262 return false;
263 }
264 $new_options['lookup_table'] = $lookup_table;
265 }
266 } else {
267 // It seems this is the first time this plugin was ran, let's create everything we need in order to
268 // have this plugin function normally.
269 $new_folder_name = $this->generate_unique_name( $existing_files );
270 // Create a unique folder name
271 $new_options['folder_name'] = $new_options_error['folder_name'] = $new_folder_name;
272 // we have an error while the method is being executed
273 Advanced_Ads_Ad_Blocker::get_instance()->update_options( $new_options_error );
274 // Copy the assets
275 $lookup_table = $this->copy_assets( $new_options['folder_name'] );
276 if ( ! $lookup_table ) {
277 $message = sprintf( __( 'Unable to copy assets to the "%s" directory', 'advanced-ads' ), $new_options['folder_name'] );
278 $this->error_messages->add( 'create_dummy_5', $message);
279 return false;
280 }
281 $new_options['lookup_table'] = $lookup_table;
282 }
283 // successful result, save options and rewrite previous error options
284 Advanced_Ads_Ad_Blocker::get_instance()->update_options( $new_options );
285 Advanced_Ads_Ad_Health_Notices::get_instance()->remove( 'assets_expired' );
286 }
287
288 /**
289 * Copy all assets (JS/CSS) to the magic directory.
290 *
291 * @param string $folder_name Destination folder.
292 * @param bool $need_assign_new_name True if we need to assign new random names to assets.
293 * @return bool/array Bool false on failure, array lookup table on success.
294 */
295 public function copy_assets( $folder_name, $need_assign_new_name = false ) {
296 global $wp_filesystem;
297
298 // Are we completely rebuilding the assets folder?
299 $asset_path = trailingslashit( $this->upload_dir['basedir'] ) . $folder_name ;
300 $asset_path_normalized = Advanced_Ads_Filesystem::get_instance()->normalize_path( trailingslashit( $this->upload_dir['basedir'] ) ) . $folder_name;
301
302 // already saved associations (original name => replaced name)
303 $rand_asset_names = [];
304
305 if ( $need_assign_new_name ) {
306 // Check if there is a previous asset folder
307 if ( $wp_filesystem->exists( $asset_path_normalized ) ) {
308 // Remove the old directory and its contents
309 if ( ! $wp_filesystem->rmdir( trailingslashit( $asset_path_normalized ), true ) ) {
310 $message = sprintf( __( 'We do not have direct write access to the "%s" directory', 'advanced-ads' ), $asset_path_normalized );
311 $this->error_messages->add( 'copy_assets_1', $message);
312 return false;
313 }
314 }
315 } elseif ( isset( $this->options['lookup_table'] ) ) {
316 foreach ( $this->options['lookup_table'] as $orig_path => $replaced_info ) {
317 $replaced_path = is_array( $replaced_info ) ? $replaced_info['path'] : $replaced_info;
318
319 $orig_path_components = preg_split('/\//', $orig_path, -1, PREG_SPLIT_NO_EMPTY);
320 $replaced_path_components = preg_split('/\//', $replaced_path, -1, PREG_SPLIT_NO_EMPTY);
321
322 // (css, style.css) => (1, 2.css)
323 foreach ( $orig_path_components as $k=> $orig_path_part ) {
324 $rand_asset_names[ $orig_path_part] = (string) $replaced_path_components[$k];
325 }
326 }
327 }
328
329
330 // Lookup_table contains associations between the original path of the asset and it path within our magic folder.
331 // I.e: [advanced-ads-layer/admin/assets/css/admin.css] => array( path => /12/34/56/78/1347107783.css, mtime => 99 ).
332 $assets = $this->get_assets();
333 if ( $need_assign_new_name ) {
334 $lookup_table = [];
335 } else {
336 $lookup_table = isset( $this->options['lookup_table'] ) ? $this->options['lookup_table'] : [];
337 }
338
339 /* Do not rename assets and folders. If, for example, some library uses in file.css something like this:
340 'background: url(/img/image.png)', you should add 'img') to this array */
341 $not_rename_assets = [ 'public', 'assets', 'js', 'css', 'fancybox', 'advanced.js', 'jquery.fancybox-1.3.4.css' ];
342
343 // Loop through all the found assets
344 foreach ( $assets as $file => $filemtime ) {
345 if ( ! file_exists( $file ) ) {
346 continue;
347 }
348
349 $first_cleanup = str_replace( WP_PLUGIN_DIR , '', $file );
350 $first_cleanup_dir = dirname( $first_cleanup );
351 $first_cleanup_filename = basename( $first_cleanup );
352 $first_cleanup_file_extension = pathinfo( $first_cleanup, PATHINFO_EXTENSION );
353 $path_components = preg_split('/\//', $first_cleanup_dir, -1, PREG_SPLIT_NO_EMPTY);
354 $path_components_new = [];
355
356 // Interate over directories.
357 foreach ( $path_components as $k => $dir ) {
358 if ( in_array( $dir, $not_rename_assets ) ) {
359 $path_components_new[ $k ] = $dir;
360 } elseif ( array_key_exists( $dir, $rand_asset_names ) ) {
361 $path_components_new[ $k ] = $rand_asset_names[ $dir ];
362 } else {
363 $new_rand_folder_name = $this->generate_unique_name( array_values( $rand_asset_names ) );
364 $path_components_new[ $k ] = $new_rand_folder_name;
365 $rand_asset_names[ $dir ] = (string) $new_rand_folder_name;
366 }
367 }
368
369 $new_dir_full = trailingslashit( $asset_path ) . trailingslashit( implode( '/', $path_components_new ) );
370 $new_dir_full_normalized = trailingslashit( $asset_path_normalized ) . trailingslashit( implode( '/', $path_components_new ) );
371 $new_dir = trailingslashit( implode( '/', $path_components_new ) );
372
373
374
375
376 if ( ! in_array( $first_cleanup_filename, $not_rename_assets ) && ( $first_cleanup_file_extension == 'js' || $first_cleanup_file_extension == 'css' ) ) {
377 if ( array_key_exists( $first_cleanup_filename, $rand_asset_names ) ) {
378 $new_abs_file = $new_dir_full_normalized . $rand_asset_names[$first_cleanup_filename];
379 $new_rel_file = $new_dir . $rand_asset_names[$first_cleanup_filename];
380 } else {
381 $new_filename = $this->generate_unique_name( array_values( $rand_asset_names ), $first_cleanup_file_extension );
382 $rand_asset_names[$first_cleanup_filename] = (string) $new_filename;
383 $new_abs_file = $new_dir_full_normalized . $new_filename;
384 $new_rel_file = $new_dir . $new_filename;
385 }
386 } else {
387 $new_abs_file = $new_dir_full_normalized . $first_cleanup_filename;
388 $new_rel_file = $new_dir . $first_cleanup_filename;
389 }
390
391
392 if ( ! file_exists( $new_dir_full_normalized ) ) {
393 // Create the path if it doesn't exist (prevents the copy() function from failing)
394 if ( ! Advanced_Ads_Filesystem::get_instance()->mkdir_p( $new_dir_full_normalized ) ) {
395 $message = sprintf( __( 'We do not have direct write access to the "%s" directory', 'advanced-ads' ), $this->upload_dir['basedir'] );
396 $this->error_messages->add( 'copy_assets_4', $message);
397 return false;
398 }
399 }
400
401
402 $file_normalized = Advanced_Ads_Filesystem::get_instance()->normalize_path( trailingslashit( dirname( $file ) ) ) . basename( $file );
403
404 // Copy the file to our new magic directory,
405 if ( ! $wp_filesystem->copy( $file_normalized, $new_abs_file, true, FS_CHMOD_FILE ) ) {
406 $message = sprintf( __( 'Unable to copy files to %s', 'advanced-ads' ), $asset_path_normalized );
407 $this->error_messages->add( 'copy_assets_5', $message);
408 return false;
409 }
410
411 $lookup_table[ $first_cleanup ] = [
412 'path' => $new_rel_file,
413 'mtime' => $filemtime,
414 ];
415 }
416
417 return $lookup_table;
418 }
419
420 /**
421 * This function recursively searches for assets
422 *
423 * @param string $dir The directory to search in.
424 * @return Array with pairs: abs_filename => mtime.
425 */
426 public function recursive_search_assets( $dir ) {
427 $assets = [];
428
429 $tree = glob( rtrim( $dir, '/' ) . '/*' );
430 if ( is_array( $tree ) ) {
431 foreach( $tree as $file ) {
432 if ( is_dir( $file ) && ! preg_match( $this->exclude_dir_pattern, $file ) ) {
433 $assets = array_merge( $assets, $this->recursive_search_assets( $file ) );
434 }
435 elseif ( is_file( $file ) && preg_match( $this->search_file_pattern, $file ) ) {
436 $assets[ $file ] = @filemtime( $file );
437 }
438 }
439 }
440
441 return $assets;
442 }
443
444 /**
445 * Returns new or modified assets and their mtimes.
446 *
447 * @return array
448 */
449 public function get_assets() {
450 $new_files_info = $this->recursive_search_assets( trailingslashit( WP_PLUGIN_DIR ) . 'advanced-ads*' );
451
452 if ( ! isset( $this->options['lookup_table'] ) || ! isset( $this->upload_dir['basedir'] ) || ! isset( $this->options['folder_name'] ) ) {
453 return $new_files_info;
454 }
455
456 $asset_path = trailingslashit( trailingslashit( $this->upload_dir['basedir'] ) . $this->options['folder_name'] ) ;
457 $new_files = [];
458
459 foreach ( $new_files_info as $abs_file => $mtime ) {
460 $rel_file = str_replace( WP_PLUGIN_DIR , '', $abs_file );
461
462 if ( ! isset( $this->options['lookup_table'][ $rel_file ]['mtime'] ) ||
463 $this->options['lookup_table'][ $rel_file ]['mtime'] !== $mtime ||
464 ! file_exists( $asset_path . $this->options['lookup_table'][$rel_file]['path'] )
465 ) {
466 $new_files[ $abs_file ] = $mtime;
467 }
468 }
469
470 return $new_files;
471 }
472
473 /**
474 * Automatically updates assets
475 *
476 */
477 public function process_auto_update() {
478 $advads_options = Advanced_Ads::get_instance()->options();
479 if ( ! isset( $advads_options['use-adblocker'] )
480 || ! $this->upload_dir
481 ) { return; }
482
483 //if module is working without errors and there are new assets
484 if ( ! empty( $this->options['module_can_work'] ) && count( $this->get_assets() ) ) {
485 $fs_connect = Advanced_Ads_Filesystem::get_instance()->fs_connect( $this->upload_dir['basedir'] );
486
487 if ( false === $fs_connect || is_wp_error( $fs_connect ) ) {
488 // we can not update assets automatically. The user should visit the setting page and update assets manually
489 // disable module and show notice
490 unset( $this->options['module_can_work'] );
491 Advanced_Ads_Ad_Blocker::get_instance()->update_options( $this->options );
492 return;
493 }
494
495 $this->create_dummy_plugin();
496
497 // write errors to the log
498 if ( $error_messages = $this->error_messages->get_error_messages() ) {
499 foreach ( $error_messages as $error_message ) {
500 Advanced_Ads::log( __METHOD__ . ': ' . $error_message );
501 }
502 }
503 }
504
505 }
506
507 /**
508 * Generate unique name
509 *
510 * @param array $haystack array to check, that the returned string does not exist in this array
511 * @param string $extension Extension to append to the name.
512 * @return string unique name
513 */
514 function generate_unique_name( $haystack = false, $extension = '' ) {
515 $extension = $extension ? '.' . $extension : '';
516 if ( $haystack ) {
517 $i = 0;
518
519 do {
520 $rand = (string) mt_rand( 1, 999 );
521 if ( ++$i < 100 ) {
522 $needle = (string) $rand . $extension;
523 } else {
524 $needle = (string) $rand . '_' . $i . $extension;
525 }
526
527 } while( in_array( $needle, $haystack ) );
528
529 return $needle;
530 }
531
532 $needle = (string) mt_rand( 1, 999 ) . $extension;
533 return $needle;
534 }
535
536 /**
537 * Clear assets (on uninstall)
538 */
539 function clear_assets() {
540 $advads_options = Advanced_Ads::get_instance()->options();
541
542 if ( ! empty( $advads_options['use-adblocker'] )
543 && ! empty( $this->options['folder_name'] )
544 && ! empty( $this->options['module_can_work'] )
545 && $this->upload_dir
546 && class_exists( 'WP_Filesystem_Direct', false )
547 ) {
548 $wp_filesystem = new WP_Filesystem_Direct( new StdClass() );
549 $path = trailingslashit( $this->upload_dir['basedir'] ) . trailingslashit( $this->options['folder_name'] );
550 $wp_filesystem->rmdir( $path, true );
551 }
552 }
553
554 }
555