All in One SEO Pack - Version 2.7.2

Version Description

Download this release

Release Info

Developer hallsofmontezuma
Plugin Icon 128x128 All in One SEO Pack
Version 2.7.2
Comparing to
See all releases

Code changes from version 2.6.1 to 2.7.2

admin/aioseop_module_class.php CHANGED
@@ -2306,7 +2306,7 @@ if ( ! class_exists( 'All_in_One_SEO_Pack_Module' ) ) {
2306
  } else {
2307
  $count_desc = __( ' characters. Most search engines use a maximum of %1$s chars for the %2$s.', 'all-in-one-seo-pack' );
2308
  }
2309
- $buf .= "<br /><input readonly type='text' name='{$prefix}length$n' size='3' maxlength='3' style='width:53px;height:23px;margin:0px;padding:0px 0px 0px 10px;' value='" . $this->strlen( $value ) . "' />"
2310
  . sprintf( $count_desc, $size, trim( $this->strtolower( $options['name'] ), ':' ) );
2311
  if ( ! empty( $onload ) ) {
2312
  $buf .= "<script>jQuery( document ).ready(function() { {$onload} });</script>";
2306
  } else {
2307
  $count_desc = __( ' characters. Most search engines use a maximum of %1$s chars for the %2$s.', 'all-in-one-seo-pack' );
2308
  }
2309
+ $buf .= "<br /><input readonly tabindex='-1' type='text' name='{$prefix}length$n' size='3' maxlength='3' style='width:53px;height:23px;margin:0px;padding:0px 0px 0px 10px;' value='" . $this->strlen( $value ) . "' />"
2310
  . sprintf( $count_desc, $size, trim( $this->strtolower( $options['name'] ), ':' ) );
2311
  if ( ! empty( $onload ) ) {
2312
  $buf .= "<script>jQuery( document ).ready(function() { {$onload} });</script>";
admin/aioseop_module_manager.php CHANGED
@@ -160,7 +160,7 @@ if ( ! class_exists( 'All_in_One_SEO_Pack_Module_Manager' ) ) {
160
  if ( 'performance' === $mod && ! is_super_admin() ) {
161
  return false;
162
  }
163
- if ( ( 'file_editor' === $mod || 'robots' === $mod )
164
  && ( ( defined( 'DISALLOW_FILE_EDIT' ) && DISALLOW_FILE_EDIT )
165
  || ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS )
166
  || ! is_super_admin() )
160
  if ( 'performance' === $mod && ! is_super_admin() ) {
161
  return false;
162
  }
163
+ if ( ( 'file_editor' === $mod )
164
  && ( ( defined( 'DISALLOW_FILE_EDIT' ) && DISALLOW_FILE_EDIT )
165
  || ( defined( 'DISALLOW_FILE_MODS' ) && DISALLOW_FILE_MODS )
166
  || ! is_super_admin() )
admin/display/menu.php CHANGED
@@ -11,6 +11,13 @@ class AIOSEOPAdminMenus {
11
  * Constructor to add the actions.
12
  */
13
  function __construct() {
 
 
 
 
 
 
 
14
  if ( current_user_can( 'manage_options' ) || current_user_can( 'aiosp_manage_seo' ) ) {
15
  add_action( 'admin_menu', array( $this, 'add_pro_submenu' ), 11 );
16
  } else {
@@ -18,6 +25,10 @@ class AIOSEOPAdminMenus {
18
  }
19
  }
20
 
 
 
 
 
21
  /**
22
  * Adds Upgrade link to our menu.
23
  *
@@ -35,4 +46,5 @@ class AIOSEOPAdminMenus {
35
  }
36
  }
37
 
38
- new AIOSEOPAdminMenus();
 
11
  * Constructor to add the actions.
12
  */
13
  function __construct() {
14
+
15
+ add_action( 'network_admin_menu', array( $this, 'remove_menus' ), 15 );
16
+
17
+ if ( is_multisite()){
18
+ return;
19
+ }
20
+
21
  if ( current_user_can( 'manage_options' ) || current_user_can( 'aiosp_manage_seo' ) ) {
22
  add_action( 'admin_menu', array( $this, 'add_pro_submenu' ), 11 );
23
  } else {
25
  }
26
  }
27
 
28
+ function remove_menus(){
29
+ remove_menu_page( AIOSEOP_PLUGIN_DIRNAME . '/aioseop_class.php' ); // Remove AIOSEOP menu from the network admin.
30
+ }
31
+
32
  /**
33
  * Adds Upgrade link to our menu.
34
  *
46
  }
47
  }
48
 
49
+ new AIOSEOPAdminMenus();
50
+
aioseop_class.php CHANGED
@@ -3651,7 +3651,11 @@ class All_in_One_SEO_Pack extends All_in_One_SEO_Pack_Module {
3651
  }
3652
 
3653
  if ( is_admin() ) {
 
 
 
3654
  add_action( 'admin_menu', array( $this, 'admin_menu' ) );
 
3655
  add_action( 'admin_head', array( $this, 'add_page_icon' ) );
3656
  add_action( 'admin_init', 'aioseop_addmycolumns', 1 );
3657
  add_action( 'admin_init', 'aioseop_handle_ignore_notice' );
3651
  }
3652
 
3653
  if ( is_admin() ) {
3654
+ if ( is_multisite() ) {
3655
+ add_action( 'network_admin_menu', array( $this, 'admin_menu' ) );
3656
+ }
3657
  add_action( 'admin_menu', array( $this, 'admin_menu' ) );
3658
+
3659
  add_action( 'admin_head', array( $this, 'add_page_icon' ) );
3660
  add_action( 'admin_init', 'aioseop_addmycolumns', 1 );
3661
  add_action( 'admin_init', 'aioseop_handle_ignore_notice' );
all_in_one_seo_pack.php CHANGED
@@ -4,7 +4,7 @@
4
  Plugin Name: All In One SEO Pack
5
  Plugin URI: https://semperplugins.com/all-in-one-seo-pack-pro-version/
6
  Description: Out-of-the-box SEO for your WordPress blog. Features like XML Sitemaps, SEO for custom post types, SEO for blogs or business sites, SEO for ecommerce sites, and much more. More than 30 million downloads since 2007.
7
- Version: 2.6.1
8
  Author: Michael Torbert
9
  Author URI: https://semperplugins.com/all-in-one-seo-pack-pro-version/
10
  Text Domain: all-in-one-seo-pack
@@ -32,14 +32,14 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
32
  * The original WordPress SEO plugin.
33
  *
34
  * @package All-in-One-SEO-Pack
35
- * @version 2.6.1
36
  */
37
 
38
  if ( ! defined( 'AIOSEOPPRO' ) ) {
39
  define( 'AIOSEOPPRO', false );
40
  }
41
  if ( ! defined( 'AIOSEOP_VERSION' ) ) {
42
- define( 'AIOSEOP_VERSION', '2.6.1' );
43
  }
44
  global $aioseop_plugin_name;
45
  $aioseop_plugin_name = 'All in One SEO Pack';
4
  Plugin Name: All In One SEO Pack
5
  Plugin URI: https://semperplugins.com/all-in-one-seo-pack-pro-version/
6
  Description: Out-of-the-box SEO for your WordPress blog. Features like XML Sitemaps, SEO for custom post types, SEO for blogs or business sites, SEO for ecommerce sites, and much more. More than 30 million downloads since 2007.
7
+ Version: 2.7.2
8
  Author: Michael Torbert
9
  Author URI: https://semperplugins.com/all-in-one-seo-pack-pro-version/
10
  Text Domain: all-in-one-seo-pack
32
  * The original WordPress SEO plugin.
33
  *
34
  * @package All-in-One-SEO-Pack
35
+ * @version 2.7.2
36
  */
37
 
38
  if ( ! defined( 'AIOSEOPPRO' ) ) {
39
  define( 'AIOSEOPPRO', false );
40
  }
41
  if ( ! defined( 'AIOSEOP_VERSION' ) ) {
42
+ define( 'AIOSEOP_VERSION', '2.7.2' );
43
  }
44
  global $aioseop_plugin_name;
45
  $aioseop_plugin_name = 'All in One SEO Pack';
css/modules/aioseop_module.css CHANGED
@@ -810,38 +810,79 @@ div.aioseop_feature#aioseop_coming_soon2 .aioseop_featured_image {
810
  /* max-width: 900px; */
811
  }
812
 
813
- #aiosp_sitemap_addl_pages,
814
- #aiosp_video_sitemap_addl_pages {
815
- clear: left;
816
- margin-left: 20px;
817
- max-width: 1072px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
818
  }
819
 
820
  #aiosp_sitemap_addl_pages_metabox .aioseop_wrapper,
821
  #aiosp_video_sitemap_addl_pages_metabox .aioseop_wrapper {
822
- width: 23%;
823
- min-width: 165px;
 
 
 
824
  display: inline-block;
825
- max-width: 265px;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
826
  }
827
 
828
  #aiosp_sitemap_addl_pages_metabox .aioseop_help_text_div,
829
  #aiosp_video_sitemap_addl_pages_metabox .aioseop_help_text_div {
830
  position: absolute;
 
831
  margin: 5px 0 10px 0;
832
  }
833
 
834
- #aiosp_sitemap_addl_pages_metabox .aioseop_option_input,
835
- #aiosp_video_sitemap_addl_pages_metabox .aioseop_option_input {
836
- width: 94%;
837
- min-width: 94%;
838
- }
839
-
840
  #aiosp_sitemap_addl_pages_metabox table.aioseop_table,
841
  #aiosp_video_sitemap_addl_pages_metabox table.aioseop_table {
842
  width: 96%;
843
  border: 1px solid #CCC;
844
- margin: 5px 0 10px 0;
845
  }
846
 
847
  table.aioseop_table tr:nth-child(odd) {
@@ -913,10 +954,15 @@ table.aioseop_table td {
913
 
914
  #aiosp_sitemap_addl_pages_metabox table.aioseop_table td,
915
  #aiosp_video_sitemap_addl_pages_metabox table.aioseop_table td {
916
- width: 25%;
917
  padding-left: 5%;
918
  }
919
 
 
 
 
 
 
920
  table.aioseop_table td, table.aioseop_table th {
921
  padding: 3px;
922
  }
@@ -952,6 +998,40 @@ table.aioseop_table td, table.aioseop_table th {
952
  clear: right;
953
  }
954
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
955
  #aiosp_settings_form .aioseop_no_label, .aioseop_no_label {
956
  float: left;
957
  width: 92%;
@@ -1189,7 +1269,7 @@ div.aioseop_notice a.aioseop_dismiss_link {
1189
  vertical-align: bottom;
1190
  }
1191
 
1192
- .aiosp_delete_url {
1193
  background-image: url('../../images/delete.png');
1194
  display: inline-block;
1195
  width: 16px;
@@ -1416,4 +1496,10 @@ div#aioseop_snippet > div > span {
1416
  .aioseop_count_ugly {
1417
  color: #fff !important;
1418
  background-color: #f00 !important;
 
 
 
 
 
 
1419
  }
810
  /* max-width: 900px; */
811
  }
812
 
813
+ /*** Sitemap Additional Pages section ***/
814
+ #aiosp_sitemap_addl_pages_metabox .aioseop_options,
815
+ #aiosp_video_sitemap_addl_pages_metabox .aioseop_options {
816
+ width: 97%;
817
+ margin: 5px;
818
+ }
819
+
820
+ #aiosp_sitemap_addl_pages_metabox .aioseop_wrapper#aiosp_sitemap_addl_instructions_wrapper,
821
+ #aiosp_video_sitemap_addl_pages_metabox .aioseop_wrapper#aiosp_video_sitemap_addl_instructions_wrapper {
822
+ display: block;
823
+ width: 100%;
824
+ float: none;
825
+ margin: 0;
826
+ }
827
+
828
+ #aiosp_sitemap_addl_pages_metabox .aioseop_wrapper#aiosp_sitemap_addl_instructions_wrapper .aioseop_input,
829
+ #aiosp_video_sitemap_addl_pages_metabox .aioseop_wrapper#aiosp_video_sitemap_addl_instructions_wrapper .aioseop_input {
830
+ display: block;
831
+ width: 100%;
832
  }
833
 
834
  #aiosp_sitemap_addl_pages_metabox .aioseop_wrapper,
835
  #aiosp_video_sitemap_addl_pages_metabox .aioseop_wrapper {
836
+ padding: 0;
837
+ }
838
+
839
+ #aiosp_sitemap_addl_pages_metabox .aioseop_wrapper .aioseop_input,
840
+ #aiosp_video_sitemap_addl_pages_metabox .aioseop_wrapper .aioseop_input {
841
  display: inline-block;
842
+ vertical-align: middle;
843
+ width: 25%;
844
+ min-width: 120px;
845
+ height: 70px;
846
+ }
847
+
848
+ #aiosp_sitemap_addl_pages_metabox .aioseop_wrapper .aioseop_top_label,
849
+ #aiosp_video_sitemap_addl_pages_metabox .aioseop_wrapper .aioseop_top_label {
850
+ width: 70%;
851
+ margin: 0;
852
+ }
853
+
854
+ #aiosp_sitemap_addl_pages_metabox .aioseop_wrapper .aioseop_option_label,
855
+ #aiosp_video_sitemap_addl_pages_metabox .aioseop_wrapper .aioseop_option_label {
856
+ height: 30px !important;
857
+ }
858
+
859
+ #aiosp_sitemap_addl_pages_metabox .aioseop_wrapper#aiosp_sitemap_addl_mod_wrapper input.aiseop-date,
860
+ #aiosp_video_sitemap_addl_pages_metabox .aioseop_wrapper#aiosp_video_sitemap_addl_mod_wrapper input.aiseop-date {
861
+ height: 36px;
862
+ }
863
+
864
+ #aiosp_sitemap_addl_pages_metabox .aioseop_options .aioseop_submit_type,
865
+ #aiosp_video_sitemap_addl_pages_metabox .aioseop_options .aioseop_submit_type {
866
+ margin: 0;
867
+ }
868
+
869
+ #aiosp_sitemap_addl_pages_metabox .aioseop_options .aioseop_submit_type input.button-primary,
870
+ #aiosp_video_sitemap_addl_pages_metabox .aioseop_options .aioseop_submit_type input.button-primary {
871
+ margin-left: 0 !important;
872
  }
873
 
874
  #aiosp_sitemap_addl_pages_metabox .aioseop_help_text_div,
875
  #aiosp_video_sitemap_addl_pages_metabox .aioseop_help_text_div {
876
  position: absolute;
877
+ width: auto;
878
  margin: 5px 0 10px 0;
879
  }
880
 
 
 
 
 
 
 
881
  #aiosp_sitemap_addl_pages_metabox table.aioseop_table,
882
  #aiosp_video_sitemap_addl_pages_metabox table.aioseop_table {
883
  width: 96%;
884
  border: 1px solid #CCC;
885
+ margin: 5px 5px 10px;
886
  }
887
 
888
  table.aioseop_table tr:nth-child(odd) {
954
 
955
  #aiosp_sitemap_addl_pages_metabox table.aioseop_table td,
956
  #aiosp_video_sitemap_addl_pages_metabox table.aioseop_table td {
957
+ width: 27%;
958
  padding-left: 5%;
959
  }
960
 
961
+ #aiosp_sitemap_addl_pages_metabox table.aioseop_table td:first-child,
962
+ #aiosp_video_sitemap_addl_pages_metabox table.aioseop_table td:first-child {
963
+ padding-left: 2%;
964
+ }
965
+
966
  table.aioseop_table td, table.aioseop_table th {
967
  padding: 3px;
968
  }
998
  clear: right;
999
  }
1000
 
1001
+ #aiosp_robots_rules {
1002
+ clear: left;
1003
+ margin-left: 20px;
1004
+ max-width: 1072px;
1005
+ }
1006
+
1007
+ #aiosp_robots_default_metabox .aioseop_wrapper {
1008
+ width: 31%;
1009
+ min-width: 165px;
1010
+ display: inline-block;
1011
+ max-width: 265px;
1012
+ }
1013
+
1014
+ #aiosp_robots_default_metabox .aioseop_help_text_div {
1015
+ position: absolute;
1016
+ margin: 5px 0 10px 0;
1017
+ }
1018
+
1019
+ #aiosp_robots_default_metabox .aioseop_option_input {
1020
+ width: 94%;
1021
+ min-width: 94%;
1022
+ }
1023
+
1024
+ #aiosp_robots_default_metabox table.aioseop_table {
1025
+ width: 96%;
1026
+ border: 1px solid #CCC;
1027
+ margin: 5px 0 10px 0;
1028
+ }
1029
+
1030
+ #aiosp_robots_default_metabox table.aioseop_table td {
1031
+ width: 25%;
1032
+ padding-left: 5%;
1033
+ }
1034
+
1035
  #aiosp_settings_form .aioseop_no_label, .aioseop_no_label {
1036
  float: left;
1037
  width: 92%;
1269
  vertical-align: bottom;
1270
  }
1271
 
1272
+ .aiosp_delete {
1273
  background-image: url('../../images/delete.png');
1274
  display: inline-block;
1275
  width: 16px;
1496
  .aioseop_count_ugly {
1497
  color: #fff !important;
1498
  background-color: #f00 !important;
1499
+ }
1500
+
1501
+ textarea.robots-text {
1502
+ background-color: #eee;
1503
+ width: 100%;
1504
+ height: 100%;
1505
  }
inc/aioseop_functions.php CHANGED
@@ -925,11 +925,11 @@ if ( ! function_exists( 'fnmatch' ) ) {
925
  }
926
 
927
  if ( ! function_exists( 'aiosp_log' ) ) {
928
- function aiosp_log( $log ) {
929
 
930
  global $aioseop_options;
931
 
932
- if ( ! empty( $aioseop_options ) && isset( $aioseop_options['aiosp_do_log'] ) && $aioseop_options['aiosp_do_log'] ) {
933
 
934
  if ( is_array( $log ) || is_object( $log ) ) {
935
  error_log( print_r( $log, true ) );
925
  }
926
 
927
  if ( ! function_exists( 'aiosp_log' ) ) {
928
+ function aiosp_log( $log, $force = false ) {
929
 
930
  global $aioseop_options;
931
 
932
+ if ( ( ! empty( $aioseop_options ) && isset( $aioseop_options['aiosp_do_log'] ) && $aioseop_options['aiosp_do_log'] ) || $force || defined( 'AIOSEOP_DO_LOG' ) ) {
933
 
934
  if ( is_array( $log ) || is_object( $log ) ) {
935
  error_log( print_r( $log, true ) );
js/modules/aioseop_module.js CHANGED
@@ -282,280 +282,91 @@ jQuery( document ).ready(
282
  }
283
  }
284
  );
285
- }
286
- );
287
-
288
- /**
289
- * @summary Custom jQuery plugin that enables image uploader in wordpress.
290
- *
291
- * @since 2.3.13
292
- * @since 2.4.14 Added success callback and options.
293
- * @see http://www.webmaster-source.com/2013/02/06/using-the-wordpress-3-5-media-uploader-in-your-plugin-or-theme/
294
- *
295
- * @param object options Plugin options.
296
- */
297
- jQuery.fn.aioseopImageUploader = function( options ) {
298
- // Keep reference to this.
299
- var self = this;
300
-
301
- // Options
302
- self.options = jQuery.extend(
303
- {
304
- success: undefined,
305
- }, options
306
- );
307
-
308
- // Set input target when to update image url value
309
- self.target = jQuery( self ).next();
310
-
311
- // Uploader per image button
312
- // * Having only one uploader was causing problems when multiple image buttons where in place
313
- self.uploader = wp.media(
314
- {
315
- title: 'Choose Image',
316
- button: {
317
- text: 'Choose Image'
318
- },
319
- multiple: false
320
- }
321
- );
322
-
323
- /**
324
- * Event handler that will be called when an image is selected from media uploader.
325
- */
326
- self.onSelect = function() {
327
- var url = self.uploader.state().get( 'selection' ).first().toJSON().url;
328
- if ( self.target.length >= 0 ) {
329
- jQuery( self.target ).val( url );
330
- }
331
- if ( self.options.success !== undefined ) {
332
- self.options.success( url, self );
333
- }
334
- };
335
-
336
- /**
337
- * Click event handler.
338
- * @param object e Click event.
339
- */
340
- self.onClick = function( e ) {
341
- e.preventDefault();
342
- self.uploader.open();
343
- };
344
-
345
- // Set uploader select handler
346
- self.uploader.on( 'select', self.onSelect );
347
-
348
- // Set click handler
349
- jQuery( self ).click( self.onClick );
350
- };
351
-
352
- /**
353
- * @summary Javascript for using WP media uploader. Indentifies which DOM should use custom uploader plugin.
354
- *
355
- * @see http://www.webmaster-source.com/2013/02/06/using-the-wordpress-3-5-media-uploader-in-your-plugin-or-theme/
356
- * @since ?
357
- * @since 2.3.11.2 Use WP 3.5 new media uploader
358
- * @since 2.3.13 Fixed issue #[740](https://github.com/semperfiwebdesign/all-in-one-seo-pack/issues/740)
359
- *
360
- */
361
- jQuery( document ).ready(
362
- function($){
363
-
364
- jQuery( '.aioseop_upload_image_button' ).each(
365
- function() {
366
- jQuery( this ).aioseopImageUploader(
367
- {
368
- success: function( url, el ) {
369
- // Update checker
370
- if ( jQuery( el ).prev().length > 0 ) {
371
- jQuery( el ).prev().val( 1 );
372
- }
373
- },
374
- }
375
- );
376
- }
377
- );
378
-
379
- }
380
- );
381
-
382
- /**
383
- * @summary workaround for bug that causes radio inputs to lose settings when meta box is dragged.
384
- *
385
- * props to commentluv for this fix
386
- * @author commentluv.
387
- * @link https://core.trac.wordpress.org/ticket/16972
388
- * @since 1.0.0
389
- */
390
- jQuery( document ).ready(
391
- function() {
392
-
393
- // listen for drag drop of metaboxes , bind mousedown to .hndle so it only fires when starting to drag
394
- jQuery( '.hndle' ).mousedown(
395
- function() {
396
-
397
- // set live event listener for mouse up on the content .wrap and wait a tick to give the dragged div time to settle before firing the reclick function
398
- jQuery( '.wrap' ).mouseup(
399
- function() {
400
- aiosp_store_radio();
401
- setTimeout( function() {
402
- aiosp_reclick_radio();
403
- }, 50 );
404
- }
405
- );
406
- }
407
- );
408
- }
409
- );
410
-
411
- /**
412
- * @summary Stores object of all radio buttons that are checked for entire form.
413
- *
414
- * @since 1.0.0
415
- */
416
- function aiosp_store_radio() {
417
- var radioshack = {};
418
- jQuery( 'input[type="radio"]' ).each(
419
- function() {
420
- if ( jQuery( this ).is( ':checked' ) ) {
421
- radioshack[ jQuery( this ).attr( 'name' ) ] = jQuery( this ).val();
422
- }
423
- jQuery( document ).data( 'radioshack', radioshack );
424
- }
425
- );
426
- }
427
-
428
- /**
429
- * @summary Detects mouseup and restore all radio buttons that were checked.
430
- *
431
- * @since 1.0.0
432
- */
433
- function aiosp_reclick_radio() {
434
-
435
- // gets the object of checked radio button names and values
436
- var radios = jQuery( document ).data( 'radioshack' );
437
-
438
- // steps thru each object element and trigger a click on it's corresponding radio button
439
- for ( var key in radios ) {
440
- jQuery( 'input[name="' + key + '"]' )
441
- .filter( '[value="' + radios[ key ] + '"]' )
442
- .trigger( 'click' );
443
- }
444
- // unbinds the event listener on .wrap (prevents clicks on inputs from triggering function)
445
- jQuery( '.wrap' ).unbind( 'mouseup' );
446
- }
447
-
448
- /**
449
- * @summary Handdles ajax call.
450
- *
451
- * @since 1.0.0
452
- * @param $action.
453
- * @param $setting.
454
- * @param $options.
455
- * @param $success.
456
- */
457
- function aioseop_handle_ajax_call( action, settings, options, success ) {
458
- var aioseop_sack = new sack( ajaxurl );
459
- aioseop_sack.execute = 1;
460
- aioseop_sack.method = 'POST';
461
- aioseop_sack.setVar( "action", action );
462
- aioseop_sack.setVar( "settings", settings );
463
- aioseop_sack.setVar( "options", options );
464
- if ( typeof success != 'undefined' ) {
465
- aioseop_sack.onCompletion = success;
466
- }
467
- aioseop_sack.setVar(
468
- "nonce-aioseop",
469
- jQuery( 'input[name="nonce-aioseop"]' ).val()
470
- );
471
- aioseop_sack.setVar(
472
- "nonce-aioseop-edit",
473
- jQuery( 'input[name="nonce-aioseop-edit"]' ).val()
474
- );
475
- aioseop_sack.onError = function() {
476
- alert( 'Ajax error on saving.' );
477
- };
478
- aioseop_sack.runAJAX();
479
- }
480
 
481
- /**
482
- * @summary Handdles posts URL.
483
- *
484
- * @since 1.0.0
485
- * @param $action.
486
- * @param $setting.
487
- * @param $options.
488
- * @param $success.
489
- */
490
- function aioseop_handle_post_url( action, settings, options, success) {
491
- jQuery( "div#aiosp_" + settings ).fadeOut(
492
- 'fast', function() {
493
- var loading = '<label class="aioseop_loading aioseop_' + settings + '_loading"></label> Please wait...';
494
- jQuery( "div#aiosp_" + settings ).fadeIn(
495
- 'fast', function() {
496
- aioseop_handle_ajax_call( action, settings, options, success );
497
- }
498
- );
499
- jQuery( "div#aiosp_" + settings ).html( loading );
500
- }
501
- );
502
- }
503
-
504
- /**
505
- * @summary Handles when AIOSEOP is overflowed.
506
- *
507
- * @since 1.0.0
508
- * @param $element.
509
- * @return mixed.
510
- */
511
- function aioseop_is_overflowed( element ) {
512
- return element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
513
- }
514
-
515
- /**
516
- * @summary Handles when overflowed border.
517
- *
518
- * @since 1.0.0
519
- * @param $el.
520
- */
521
- function aioseop_overflow_border( el ) {
522
- if ( aioseop_is_overflowed( el ) ) {
523
- el.className = 'aioseop_option_div aioseop_overflowed';
524
- } else {
525
- el.className = 'aioseop_option_div';
526
- }
527
- }
528
-
529
- /**
530
- * @since 1.0.0
531
- * @return mixed.
532
- */
533
- jQuery( document ).ready(
534
- function() {
535
- jQuery( "#poststuff .aioseop_radio_type input[type='radio']" ).on(
536
- 'click', function() {
537
- var previousValue = jQuery( this ).attr( 'previousValue' );
538
- var name = jQuery( this ).attr( 'name' );
539
- if ( typeof previousValue == 'undefined' ) {
540
- if ( jQuery( this ).prop( "checked" ) ) {
541
- jQuery( this ).prop( 'checked', true );
542
- jQuery( this ).attr( 'previousValue', 'checked' );
543
- } else {
544
- jQuery( this ).prop( 'checked', false );
545
- jQuery( this ).attr( 'previousValue', false );
546
- }
547
- return;
548
- }
549
- if ( previousValue == 'checked' ) {
550
- jQuery( this ).prop( 'checked', false );
551
- jQuery( this ).attr( 'previousValue', false );
552
- } else {
553
- jQuery( "input[name=" + name + "]:radio" )
554
- .attr( 'previousValue', false );
555
- jQuery( this ).attr( 'previousValue', 'checked' );
556
- }
557
- }
558
- );
 
 
 
 
 
 
559
  if ( typeof aiosp_data.pointers != 'undefined' ) {
560
 
561
  /**
@@ -800,21 +611,252 @@ jQuery( document ).ready(
800
  return false;
801
  }
802
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
803
  }
804
  );
805
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
806
 
807
- jQuery( document ).ready(
808
- function() {
809
- // TODO: consider moving EVERYTHING that needs ready() to this function
810
- aiospinitAll( jQuery );
811
- aiospinitCounting( jQuery );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
812
  }
813
- );
 
 
814
 
815
- function aiospinitAll($){
816
- if ( $( '.aiseop-date' ).length > 0 && $( '.aiseop-date' ).eq( 0 ).prop( 'type' ).toLowerCase() === 'text' ) {
817
- $( '.aiseop-date' ).datepicker(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
818
  {
819
  dateFormat: "yy-mm-dd"
820
  }
@@ -822,13 +864,12 @@ function aiospinitAll($){
822
  }
823
  }
824
 
825
- function aiospinitCounting($){
826
  /* count them characters */
827
- $( '.aioseop_count_chars' ).on('keyup keydown', function(){
828
- countChars( $(this).eq(0), $(this).parent().find('[name="' + $(this).attr('data-length-field') + '"]').eq(0));
829
  });
830
- $( '.aioseop_count_chars' ).each(function(){
831
- countChars( $(this).eq(0), $(this).parent().find('[name="' + $(this).attr('data-length-field') + '"]').eq(0));
832
  });
833
  }
834
-
282
  }
283
  }
284
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
285
 
286
+ /**
287
+ * @summary workaround for bug that causes radio inputs to lose settings when meta box is dragged.
288
+ *
289
+ * props to commentluv for this fix
290
+ * @author commentluv.
291
+ * @link https://core.trac.wordpress.org/ticket/16972
292
+ * @since 1.0.0
293
+ */
294
+ jQuery(document).ready(
295
+ function () {
296
+ // listen for drag drop of metaboxes , bind mousedown to .hndle so it only fires when starting to drag
297
+ jQuery('.hndle').mousedown(
298
+ function () {
299
+
300
+ // set live event listener for mouse up on the content .wrap and wait a tick to give the dragged div time to settle before firing the reclick function
301
+ jQuery('.wrap').mouseup(
302
+ function () {
303
+ aiosp_store_radio();
304
+ setTimeout(function () {
305
+ aiosp_reclick_radio();
306
+ }, 50);
307
+ }
308
+ );
309
+ }
310
+ );
311
+ }
312
+ );
313
+
314
+ /**
315
+ * @summary Javascript for using WP media uploader. Indentifies which DOM should use custom uploader plugin.
316
+ *
317
+ * @see http://www.webmaster-source.com/2013/02/06/using-the-wordpress-3-5-media-uploader-in-your-plugin-or-theme/
318
+ * @since ?
319
+ * @since 2.3.11.2 Use WP 3.5 new media uploader
320
+ * @since 2.3.13 Fixed issue #[740](https://github.com/semperfiwebdesign/all-in-one-seo-pack/issues/740)
321
+ *
322
+ */
323
+ jQuery(document).ready(
324
+ function ($) {
325
+ jQuery('.aioseop_upload_image_button').each(
326
+ function () {
327
+ jQuery(this).aioseopImageUploader(
328
+ {
329
+ success: function (url, el) {
330
+ // Update checker
331
+ if (jQuery(el).prev().length > 0) {
332
+ jQuery(el).prev().val(1);
333
+ }
334
+ },
335
+ }
336
+ );
337
+ }
338
+ );
339
+ }
340
+ );
341
+
342
+ jQuery(document).ready(
343
+ function () {
344
+ jQuery("#poststuff .aioseop_radio_type input[type='radio']").on(
345
+ 'click', function () {
346
+ var previousValue = jQuery(this).attr('previousValue');
347
+ var name = jQuery(this).attr('name');
348
+ if (typeof previousValue == 'undefined') {
349
+ if (jQuery(this).prop("checked")) {
350
+ jQuery(this).prop('checked', true);
351
+ jQuery(this).attr('previousValue', 'checked');
352
+ } else {
353
+ jQuery(this).prop('checked', false);
354
+ jQuery(this).attr('previousValue', false);
355
+ }
356
+ return;
357
+ }
358
+ if (previousValue == 'checked') {
359
+ jQuery(this).prop('checked', false);
360
+ jQuery(this).attr('previousValue', false);
361
+ } else {
362
+ jQuery("input[name=" + name + "]:radio")
363
+ .attr('previousValue', false);
364
+ jQuery(this).attr('previousValue', 'checked');
365
+ }
366
+ }
367
+ );
368
+ }
369
+ );
370
  if ( typeof aiosp_data.pointers != 'undefined' ) {
371
 
372
  /**
611
  return false;
612
  }
613
  );
614
+
615
+
616
+ jQuery( "div#aiosp_robots_default_metabox" )
617
+ .delegate(
618
+ "a.aiosp_robots_delete_rule", "click", function( e ) {
619
+ e.preventDefault();
620
+ aioseop_handle_post_url(
621
+ 'aioseop_ajax_delete_rule',
622
+ 'robots_rules',
623
+ jQuery( this ).attr( "data-id" ),
624
+ function() {
625
+ window.location.reload();
626
+ }
627
+ );
628
+ return false;
629
+ }
630
+ );
631
+
632
+ jQuery( "a.aiosp_robots_physical" ).on( 'click', function( e ) {
633
+ e.preventDefault();
634
+ aioseop_handle_post_url(
635
+ 'aioseop_ajax_robots_physical',
636
+ 'robots_physical_import_delete',//'robots_metabox', // No element of the ID existed, and unsure which element its intended for.
637
+ jQuery( this ).attr( "data-action" ),
638
+ function( data ) {
639
+ if ( data.data && data.data.message ) {
640
+ alert( data.data.message );
641
+ }
642
+ window.location.reload();
643
+ },
644
+ true
645
+ );
646
+ return false;
647
+ });
648
+
649
+ aiospinitAll();
650
+ aiospinitCounting();
651
+
652
  }
653
  );
654
 
655
+ /**
656
+ * @summary Custom jQuery plugin that enables image uploader in wordpress.
657
+ *
658
+ * @since 2.3.13
659
+ * @since 2.4.14 Added success callback and options.
660
+ * @see http://www.webmaster-source.com/2013/02/06/using-the-wordpress-3-5-media-uploader-in-your-plugin-or-theme/
661
+ *
662
+ * @param object options Plugin options.
663
+ */
664
+ jQuery.fn.aioseopImageUploader = function( options ) {
665
+ // Keep reference to this.
666
+ var self = this;
667
+
668
+ // Options
669
+ self.options = jQuery.extend(
670
+ {
671
+ success: undefined,
672
+ }, options
673
+ );
674
 
675
+ // Set input target when to update image url value
676
+ self.target = jQuery( self ).next();
677
+
678
+ // Uploader per image button
679
+ // * Having only one uploader was causing problems when multiple image buttons where in place
680
+ self.uploader = wp.media(
681
+ {
682
+ title: 'Choose Image',
683
+ button: {
684
+ text: 'Choose Image'
685
+ },
686
+ multiple: false
687
+ }
688
+ );
689
+
690
+ /**
691
+ * Event handler that will be called when an image is selected from media uploader.
692
+ */
693
+ self.onSelect = function() {
694
+ var url = self.uploader.state().get( 'selection' ).first().toJSON().url;
695
+ if ( self.target.length >= 0 ) {
696
+ jQuery( self.target ).val( url );
697
+ }
698
+ if ( self.options.success !== undefined ) {
699
+ self.options.success( url, self );
700
+ }
701
+ };
702
+
703
+ /**
704
+ * Click event handler.
705
+ * @param object e Click event.
706
+ */
707
+ self.onClick = function( e ) {
708
+ e.preventDefault();
709
+ self.uploader.open();
710
+ };
711
+
712
+ // Set uploader select handler
713
+ self.uploader.on( 'select', self.onSelect );
714
+
715
+ // Set click handler
716
+ jQuery( self ).click( self.onClick );
717
+ };
718
+
719
+ /**
720
+ * @summary Stores object of all radio buttons that are checked for entire form.
721
+ *
722
+ * @since 1.0.0
723
+ */
724
+ function aiosp_store_radio() {
725
+ var radioshack = {};
726
+ jQuery( 'input[type="radio"]' ).each(
727
+ function() {
728
+ if ( jQuery( this ).is( ':checked' ) ) {
729
+ radioshack[ jQuery( this ).attr( 'name' ) ] = jQuery( this ).val();
730
+ }
731
+ jQuery( document ).data( 'radioshack', radioshack );
732
+ }
733
+ );
734
+ }
735
+
736
+ /**
737
+ * @summary Detects mouseup and restore all radio buttons that were checked.
738
+ *
739
+ * @since 1.0.0
740
+ */
741
+ function aiosp_reclick_radio() {
742
+
743
+ // gets the object of checked radio button names and values
744
+ var radios = jQuery( document ).data( 'radioshack' );
745
+
746
+ // steps thru each object element and trigger a click on it's corresponding radio button
747
+ for ( var key in radios ) {
748
+ jQuery( 'input[name="' + key + '"]' )
749
+ .filter( '[value="' + radios[ key ] + '"]' )
750
+ .trigger( 'click' );
751
  }
752
+ // unbinds the event listener on .wrap (prevents clicks on inputs from triggering function)
753
+ jQuery( '.wrap' ).unbind( 'mouseup' );
754
+ }
755
 
756
+ /**
757
+ * @summary Handdles ajax call.
758
+ *
759
+ * @since 1.0.0
760
+ * @param $action.
761
+ * @param $setting.
762
+ * @param $options.
763
+ * @param $success.
764
+ */
765
+ function aioseop_handle_ajax_call( action, settings, options, success ) {
766
+ var aioseop_sack = new sack( ajaxurl );
767
+ aioseop_sack.execute = 1;
768
+ aioseop_sack.method = 'POST';
769
+ aioseop_sack.setVar( "action", action );
770
+ aioseop_sack.setVar( "settings", settings );
771
+ aioseop_sack.setVar( "options", options );
772
+ if ( typeof success != 'undefined' ) {
773
+ aioseop_sack.onCompletion = success;
774
+ }
775
+ aioseop_sack.setVar(
776
+ "nonce-aioseop",
777
+ jQuery( 'input[name="nonce-aioseop"]' ).val()
778
+ );
779
+ aioseop_sack.setVar(
780
+ "nonce-aioseop-edit",
781
+ jQuery( 'input[name="nonce-aioseop-edit"]' ).val()
782
+ );
783
+ aioseop_sack.onError = function() {
784
+ alert( 'Ajax error on saving.' );
785
+ };
786
+ aioseop_sack.runAJAX();
787
+ }
788
+
789
+ /**
790
+ * @summary Handdles posts URL.
791
+ *
792
+ * @since 1.0.0
793
+ * @param $action.
794
+ * @param $setting.
795
+ * @param $options.
796
+ * @param $success.
797
+ */
798
+ function aioseop_handle_post_url( action, settings, options, success_function, use_native) {
799
+ jQuery( "div#aiosp_" + settings ).fadeOut(
800
+ 'fast', function() {
801
+ var loading = '<label class="aioseop_loading aioseop_' + settings + '_loading"></label> Please wait...';
802
+ jQuery( "div#aiosp_" + settings ).fadeIn(
803
+ 'fast', function() {
804
+ if(use_native) {
805
+ jQuery.ajax({
806
+ url : ajaxurl,
807
+ method : 'POST',
808
+ dataType: 'json',
809
+ data : {
810
+ 'action' : action,
811
+ 'options' : options,
812
+ 'settings' : settings,
813
+ 'nonce-aioseop': jQuery( 'input[name="nonce-aioseop"]' ).val(),
814
+ 'nonce-aioseop-edit': jQuery( 'input[name="nonce-aioseop-edit"]' ).val()
815
+ },
816
+ success : function(data){
817
+ if(success_function){
818
+ success_function(data);
819
+ }
820
+ }
821
+ });
822
+ }else{
823
+ aioseop_handle_ajax_call( action, settings, options, success_function );
824
+ }
825
+ }
826
+ );
827
+ jQuery( "div#aiosp_" + settings ).html( loading );
828
+ }
829
+ );
830
+ }
831
+
832
+ /**
833
+ * @summary Handles when AIOSEOP is overflowed.
834
+ *
835
+ * @since 1.0.0
836
+ * @param $element.
837
+ * @return mixed.
838
+ */
839
+ function aioseop_is_overflowed( element ) {
840
+ return element.scrollHeight > element.clientHeight || element.scrollWidth > element.clientWidth;
841
+ }
842
+
843
+ /**
844
+ * @summary Handles when overflowed border.
845
+ *
846
+ * @since 1.0.0
847
+ * @param $el.
848
+ */
849
+ function aioseop_overflow_border( el ) {
850
+ if ( aioseop_is_overflowed( el ) ) {
851
+ el.className = 'aioseop_option_div aioseop_overflowed';
852
+ } else {
853
+ el.className = 'aioseop_option_div';
854
+ }
855
+ }
856
+
857
+ function aiospinitAll(){
858
+ if ( jQuery( '.aiseop-date' ).length > 0 && jQuery( '.aiseop-date' ).eq( 0 ).prop( 'type' ).toLowerCase() === 'text' ) {
859
+ jQuery( '.aiseop-date' ).datepicker(
860
  {
861
  dateFormat: "yy-mm-dd"
862
  }
864
  }
865
  }
866
 
867
+ function aiospinitCounting(){
868
  /* count them characters */
869
+ jQuery( '.aioseop_count_chars' ).on('keyup keydown', function(){
870
+ countChars( jQuery(this).eq(0), jQuery(this).parent().find('[name="' + jQuery(this).attr('data-length-field') + '"]').eq(0));
871
  });
872
+ jQuery( '.aioseop_count_chars' ).each(function(){
873
+ countChars( jQuery(this).eq(0), jQuery(this).parent().find('[name="' + jQuery(this).attr('data-length-field') + '"]').eq(0));
874
  });
875
  }
 
modules/aioseop_feature_manager.php CHANGED
@@ -48,7 +48,7 @@ if ( ! class_exists( 'All_in_One_SEO_Pack_Feature_Manager' ) ) {
48
  'file_editor' => array(
49
  /* translators: the File Editor module allows users to edit the robots.txt file or .htaccess file on their site. */
50
  'name' => __( 'File Editor', 'all-in-one-seo-pack' ),
51
- 'description' => __( 'Edit your robots.txt file and your .htaccess file to fine-tune your site.', 'all-in-one-seo-pack' ),
52
  ),
53
  'importer_exporter' => array(
54
  /* translators: the Importer & Exporter module allows users to import/export their All in One SEO Pack
48
  'file_editor' => array(
49
  /* translators: the File Editor module allows users to edit the robots.txt file or .htaccess file on their site. */
50
  'name' => __( 'File Editor', 'all-in-one-seo-pack' ),
51
+ 'description' => __( 'Edit your your .htaccess file to fine-tune your site.', 'all-in-one-seo-pack' ),
52
  ),
53
  'importer_exporter' => array(
54
  /* translators: the Importer & Exporter module allows users to import/export their All in One SEO Pack
modules/aioseop_file_editor.php CHANGED
@@ -20,25 +20,15 @@ if ( ! class_exists( 'All_in_One_SEO_Pack_File_Editor' ) ) {
20
  $this->prefix = 'aiosp_file_editor_'; // option prefix
21
  $this->file = __FILE__; // the current file
22
  parent::__construct();
23
- $this->current_tab = 'robots';
24
  if ( isset( $_REQUEST['tab'] ) ) {
25
  $this->current_tab = $_REQUEST['tab'];
26
  }
27
 
28
  $help_text = array(
29
- 'robotfile' => __( 'Robots.txt editor', 'all-in-one-seo-pack' ),
30
  'htaccfile' => __( '.htaccess editor', 'all-in-one-seo-pack' ),
31
  );
32
  $this->default_options = array(
33
- 'robotfile' => array(
34
- 'name' => __( 'Edit Robots.txt', 'all-in-one-seo-pack' ),
35
- 'save' => false,
36
- 'default' => '',
37
- 'type' => 'textarea',
38
- 'cols' => 70,
39
- 'rows' => 25,
40
- 'label' => 'top',
41
- ),
42
  'htaccfile' => array(
43
  'name' => __( 'Edit .htaccess', 'all-in-one-seo-pack' ),
44
  'save' => false,
@@ -56,16 +46,10 @@ if ( ! class_exists( 'All_in_One_SEO_Pack_File_Editor' ) ) {
56
  }
57
  }
58
  $this->tabs = array(
59
- 'robots' => array( 'name' => __( 'robots.txt' ) ),
60
  'htaccess' => array( 'name' => __( '.htaccess' ) ),
61
  );
62
 
63
  $this->layout = array(
64
- 'robots' => array(
65
- 'name' => __( 'Edit robots.txt', 'all-in-one-seo-pack' ),
66
- 'options' => array( 'robotfile' ),
67
- 'tab' => 'robots',
68
- ),
69
  'htaccess' => array(
70
  'name' => __( 'Edit .htaccess', 'all-in-one-seo-pack' ),
71
  'options' => array( 'htaccfile' ),
@@ -95,13 +79,7 @@ if ( ! class_exists( 'All_in_One_SEO_Pack_File_Editor' ) ) {
95
  function filter_submit( $submit, $location ) {
96
  unset( $submit['Submit_Default'] );
97
  $submit['Submit']['type'] = 'hidden';
98
- if ( 'robots' === $this->current_tab ) {
99
- $submit['Submit_File_Editor'] = array(
100
- 'type' => 'submit',
101
- 'class' => 'button-primary',
102
- 'value' => __( 'Update robots.txt', 'all-in-one-seo-pack' ) . ' &raquo;',
103
- );
104
- } elseif ( 'htaccess' === $this->current_tab ) {
105
  $submit['Submit_htaccess'] = array(
106
  'type' => 'submit',
107
  'class' => 'button-primary',
@@ -120,9 +98,7 @@ if ( ! class_exists( 'All_in_One_SEO_Pack_File_Editor' ) ) {
120
  */
121
  function filter_options( $options, $location ) {
122
  $prefix = $this->get_prefix( $location );
123
- if ( 'robots' === $this->current_tab ) {
124
- $options = $this->load_files( $options, array( 'robotfile' => 'robots.txt' ), $prefix );
125
- } elseif ( 'htaccess' === $this->current_tab ) {
126
  $options = $this->load_files( $options, array( 'htaccfile' => '.htaccess' ), $prefix );
127
  }
128
 
@@ -135,9 +111,7 @@ if ( ! class_exists( 'All_in_One_SEO_Pack_File_Editor' ) ) {
135
  */
136
  function do_file_editor( $options, $location ) {
137
  $prefix = $this->get_prefix( $location );
138
- if ( 'robots' === $this->current_tab && isset( $_POST['Submit_File_Editor'] ) && $_POST['Submit_File_Editor'] ) {
139
- $this->save_files( array( 'robotfile' => 'robots.txt' ), $prefix );
140
- } elseif ( 'htaccess' === $this->current_tab && isset( $_POST['Submit_htaccess'] ) && $_POST['Submit_htaccess'] ) {
141
  $this->save_files( array( 'htaccfile' => '.htaccess' ), $prefix );
142
  }
143
  }
20
  $this->prefix = 'aiosp_file_editor_'; // option prefix
21
  $this->file = __FILE__; // the current file
22
  parent::__construct();
23
+ $this->current_tab = 'htaccess';
24
  if ( isset( $_REQUEST['tab'] ) ) {
25
  $this->current_tab = $_REQUEST['tab'];
26
  }
27
 
28
  $help_text = array(
 
29
  'htaccfile' => __( '.htaccess editor', 'all-in-one-seo-pack' ),
30
  );
31
  $this->default_options = array(
 
 
 
 
 
 
 
 
 
32
  'htaccfile' => array(
33
  'name' => __( 'Edit .htaccess', 'all-in-one-seo-pack' ),
34
  'save' => false,
46
  }
47
  }
48
  $this->tabs = array(
 
49
  'htaccess' => array( 'name' => __( '.htaccess' ) ),
50
  );
51
 
52
  $this->layout = array(
 
 
 
 
 
53
  'htaccess' => array(
54
  'name' => __( 'Edit .htaccess', 'all-in-one-seo-pack' ),
55
  'options' => array( 'htaccfile' ),
79
  function filter_submit( $submit, $location ) {
80
  unset( $submit['Submit_Default'] );
81
  $submit['Submit']['type'] = 'hidden';
82
+ if ( 'htaccess' === $this->current_tab ) {
 
 
 
 
 
 
83
  $submit['Submit_htaccess'] = array(
84
  'type' => 'submit',
85
  'class' => 'button-primary',
98
  */
99
  function filter_options( $options, $location ) {
100
  $prefix = $this->get_prefix( $location );
101
+ if ( 'htaccess' === $this->current_tab ) {
 
 
102
  $options = $this->load_files( $options, array( 'htaccfile' => '.htaccess' ), $prefix );
103
  }
104
 
111
  */
112
  function do_file_editor( $options, $location ) {
113
  $prefix = $this->get_prefix( $location );
114
+ if ( 'htaccess' === $this->current_tab && isset( $_POST['Submit_htaccess'] ) && $_POST['Submit_htaccess'] ) {
 
 
115
  $this->save_files( array( 'htaccfile' => '.htaccess' ), $prefix );
116
  }
117
  }
modules/aioseop_robots.php CHANGED
@@ -9,634 +9,590 @@ if ( ! class_exists( 'All_in_One_SEO_Pack_Robots' ) ) {
9
  class All_in_One_SEO_Pack_Robots extends All_in_One_SEO_Pack_Module {
10
 
11
  function __construct() {
 
 
 
 
 
 
 
12
  $this->name = __( 'Robots.txt', 'all-in-one-seo-pack' ); // Human-readable name of the plugin
13
  $this->prefix = 'aiosp_robots_'; // option prefix
14
  $this->file = __FILE__; // the current file
15
  parent::__construct();
16
 
17
  $help_text = array(
18
- 'additional' => __( 'Rule Type', 'all-in-one-seo-pack' ),
19
- 'useragent' => __( 'User Agent', 'all-in-one-seo-pack' ),
20
  'path' => __( 'Directory Path', 'all-in-one-seo-pack' ),
21
- 'robotgen' => __( 'Robots.txt editor', 'all-in-one-seo-pack' ),
22
  );
23
 
24
  $this->default_options = array(
25
  'usage' => array(
26
  'type' => 'html',
27
  'label' => 'none',
28
- 'default' => __( 'Use the rule builder below to add rules to create a new Robots.txt file.  If you already have a Robots.txt file you should use the File Editor feature in All in One SEO Pack to edit it or you can delete your current Robots.txt file and start a new one with the rule builder below.', 'all-in-one-seo-pack' ),
29
  'save' => false,
30
  ),
31
- 'additional' => array(
32
- 'name' => __( 'Rule Type', 'all-in-one-seo-pack' ),
 
 
 
 
 
33
  'save' => false,
34
- 'type' => 'select',
35
- 'initial_options' => array( 'allow' => 'Allow', 'block' => 'Block' ),
36
- ),
37
- 'useragent' => array(
38
- 'name' => __( 'User Agent', 'all-in-one-seo-pack' ),
39
- 'save' => false,
40
- 'type' => 'text',
41
- ),
42
- 'path' => array(
43
- 'name' => __( 'Directory Path', 'all-in-one-seo-pack' ),
44
- 'save' => false,
45
- 'type' => 'text',
46
- ),
47
- 'robotgen' => array(
48
- 'name' => __( 'Generate Robots.txt', 'all-in-one-seo-pack' ),
49
- 'save' => false,
50
- 'default' => '',
51
- 'type' => 'textarea',
52
- 'cols' => 57,
53
- 'rows' => 20,
54
- 'label' => 'none',
55
- 'readonly' => 'readonly',
56
  ),
57
- 'Submit_Preview' => array(
58
- 'type' => 'submit',
59
- 'class' => 'button-primary MRL',
60
- 'name' => __( 'Add Rule', 'all-in-one-seo-pack' ) . ' &raquo;',
61
- 'nowrap' => 1,
62
- ),
63
- 'Submit_Update' => array(
64
- 'type' => 'submit',
65
- 'class' => 'button-primary',
66
- 'name' => __( 'Save Robots.txt File', 'all-in-one-seo-pack' ) . ' &raquo;',
67
- 'nowrap' => 1,
68
- ),
69
- 'Submit_Delete' => array(
70
- 'type' => 'submit',
71
- 'class' => 'button-primary',
72
- 'name' => __( 'Delete Robots.txt File', 'all-in-one-seo-pack' ) . ' &raquo;',
73
- 'nowrap' => 1,
74
- ),
75
- 'optusage' => array(
76
- 'type' => 'html',
77
- 'label' => 'none',
78
- 'default' => __( 'Click the Optimize button below and All in One SEO Pack will analyze your Robots.txt file to make sure it complies with the standards for Robots.txt files.  The results will be displayed in a table below.', 'all-in-one-seo-pack' ),
79
- 'save' => false,
80
  ),
81
- 'Submit_Opt_Update' => array(
82
- 'type' => 'submit',
83
- 'class' => 'button-primary',
84
- 'name' => __( 'Update Robots.txt File', 'all-in-one-seo-pack' ) . ' &raquo;',
85
- 'nowrap' => 1,
86
- 'style' => 'margin-left: 20px;',
87
- ),
88
- 'Submit_Opt_Preview' => array(
89
- 'type' => 'submit',
90
- 'class' => 'button-primary',
91
- 'name' => __( 'Disregard Changes', 'all-in-one-seo-pack' ) . ' &raquo;',
92
- 'nowrap' => 1,
93
  ),
94
- 'Submit_Optimize' => array(
95
  'type' => 'submit',
96
  'class' => 'button-primary',
97
- 'name' => __( 'Optimize', 'all-in-one-seo-pack' ) . ' &raquo;',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
98
  ),
99
  );
100
 
 
 
 
 
101
  if ( ! empty( $help_text ) ) {
102
  foreach ( $help_text as $k => $v ) {
103
  $this->default_options[ $k ]['help_text'] = $v;
104
  }
105
  }
106
 
107
- $this->locations = array(
108
- 'generator' => array(
109
- 'name' => 'Robots.txt',
110
- 'type' => 'settings',
111
- 'options' => array(
112
- 'usage',
113
- 'additional',
114
- 'useragent',
115
- 'path',
116
- 'Submit_Preview',
117
- 'Submit_Update',
118
- 'Submit_Delete',
119
- 'robotgen',
120
- 'optusage',
121
- 'Submit_Opt_Update',
122
- 'Submit_Opt_Preview',
123
- 'Submit_Optimize',
124
- ),
125
- ),
126
- );
127
-
128
  $this->layout = array(
129
  'default' => array(
130
  'name' => __( 'Create a Robots.txt File', 'all-in-one-seo-pack' ),
131
- 'options' => array(
132
- 'usage',
133
- 'additional',
134
- 'useragent',
135
- 'path',
136
- 'Submit_Preview',
137
- 'Submit_Update',
138
- 'Submit_Delete',
139
- 'robotgen',
140
- ), // this is set below, to the remaining options -- pdb
141
  ),
142
  );
143
- $this->layout['optimize'] = array(
144
- 'name' => __( 'Optimize your Robots.txt File', 'all-in-one-seo-pack' ),
145
- 'options' => array( 'optusage', 'Submit_Optimize' ),
146
- );
147
- if ( isset( $_POST['Submit_Optimize'] ) ) {
148
- $this->layout['optimize']['options'] = array(
149
- 'optusage',
150
- 'Submit_Opt_Update',
151
- 'Submit_Opt_Preview',
152
- 'robothtml',
153
- );
154
- $this->default_options['optusage']['default'] = __( 'Your Robots.txt file has been optimized.  Here are the results and recommendations.  Click the Update Robots.txt File button below to write these changes to your Robots.txt file.  Click the Disregard Changes button to ignore these recommendations and keep your current Robots.txt file.', 'all-in-one-seo-pack' );
155
- }
156
 
157
  // load initial options / set defaults
158
  $this->update_options();
159
 
160
- add_action( $this->prefix . 'settings_update', array( $this, 'do_robots' ), 10, 2 );
161
- add_filter( $this->prefix . 'display_options', array( $this, 'filter_options' ), 10, 2 );
162
- add_filter( $this->prefix . 'submit_options', array( $this, 'filter_submit' ), 10, 2 );
163
- add_filter( $this->prefix . 'display_settings', array( $this, 'filter_settings' ), 10, 2 );
 
 
164
  }
165
 
166
- function filter_settings( $settings, $location ) {
167
- if ( $location == 'generator' ) {
168
- $prefix = $this->get_prefix( $location ) . $location . '_';
169
- if ( isset( $_POST['Submit_Optimize'] ) ) {
170
- if ( isset( $settings[ $prefix . 'robotgen' ] ) ) {
171
- $settings[ $prefix . 'robotgen' ]['type'] = 'hidden';
172
- $settings[ $prefix . 'robotgen' ]['label'] = 'none';
173
- $settings[ $prefix . 'robotgen' ]['help_text'] = '';
174
- $settings[ $prefix . 'robothtml' ] = array(
175
- 'name' => __( 'Robots.txt', 'all-in-one-seo-pack' ),
176
- 'save' => false,
177
- 'default' => '',
178
- 'type' => 'html',
179
- 'label' => 'none',
180
- 'style' => 'margin-top:10px;',
181
- );
182
- }
183
  }
184
- }
185
 
186
- return $settings;
 
 
 
187
  }
188
 
189
- function filter_submit( $submit, $location ) {
190
- if ( $location == 'generator' ) {
191
- unset( $submit['Submit_Default'] );
192
- $submit['Submit']['type'] = 'hidden';
 
 
 
193
  }
194
-
195
- return $submit;
196
  }
197
 
198
  /**
199
- * Returns the sitemap filename;
200
- *
201
- * @return bool
202
  */
203
- function get_sitemap_filename() {
 
 
 
 
 
 
 
 
 
204
 
205
  global $aioseop_options;
206
- if ( isset( $aioseop_options['modules']['aiosp_sitemap_options']['aiosp_sitemap_filename'] ) ) {
207
- return $aioseop_options['modules']['aiosp_sitemap_options']['aiosp_sitemap_filename'];
208
- }
209
 
210
- return false;
 
 
 
211
  }
212
 
213
- /**
214
- * Filters the options.
215
- *
216
- * @todo Much of this couldn't be considered filtering options, and should be extracted to other functions.
217
- * @since ??
218
- * @since 2.3.6
219
- */
220
- function filter_options( $options, $location ) {
221
- if ( $location ) {
222
- $prefix = $this->get_prefix( $location ) . $location . '_';
223
- }
224
- if ( $location === 'generator' ) {
225
- $optimize = false;
226
- $robotgen = '';
227
- if ( ! empty( $_POST[ $prefix . 'robotgen' ] ) ) {
228
- $robotgen = str_replace( "\r\n", "\n", $_POST[ $prefix . 'robotgen' ] );
229
- }
230
- if ( isset( $_POST['Submit_Preview'] ) ) {
231
- $options[ $prefix . 'robotgen' ] = $robotgen;
232
- }
233
- if ( ! isset( $_POST['Submit_Preview'] ) ) {
234
- if ( isset( $_POST['Submit_Optimize'] ) && ! isset( $_POST['Submit_Delete'] ) && ! isset( $_POST['Submit_Update'] ) && ! isset( $_POST['Submit_Opt_Update'] ) ) {
235
- $optimize = true;
236
  }
237
- if ( ! isset( $options[ $prefix . 'robotgen' ] ) || empty( $options[ $prefix . 'robotgen' ] ) ) {
238
- if ( $optimize ) {
239
- $options[ $prefix . 'robotgen' ] = $robotgen;
240
- }
241
- if ( empty( $options[ $prefix . 'robotgen' ] ) ) {
242
- $options = $this->load_files( $options, array( 'robotgen' => 'robots.txt' ), $prefix );
243
- }
244
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
  }
246
- $access = ( get_option( 'blog_public' ) ) ? 'allow' : 'block';
247
- if ( $access ) {
248
- global $aioseop_options;
249
- $sitemapurl = '';
250
- $sitemap_filename = $this->get_sitemap_filename();
251
- if ( $sitemap_filename ) {
252
- $sitemapurl = trailingslashit( get_home_url() ) . $sitemap_filename . '.xml';
253
- }
254
- $allow_rule = "Sitemap: $sitemapurl \n\n# global\nUser-agent: *\nDisallow: /xmlrpc.php\n\n";
255
- $block_rule = "# global\nUser-agent: *\nDisallow: /\n\n";
256
- if ( empty( $options[ $prefix . 'robotgen' ] ) ) {
257
- $options[ $prefix . 'robotgen' ] = '';
258
- }
259
- if ( isset( $_POST['Submit_Preview'] ) && ( ( $options[ $prefix . 'robotgen' ] == $allow_rule ) ||
260
- ( $options[ $prefix . 'robotgen' ] == $block_rule ) )
261
- ) {
262
- $options[ $prefix . 'robotgen' ] = '';
263
- }
264
- if ( $access === 'block' && empty( $options[ $prefix . 'robotgen' ] ) ) {
265
- $options[ $prefix . 'robotgen' ] .= $block_rule;
266
- } elseif ( $access === 'allow' && empty( $options[ $prefix . 'robotgen' ] ) ) {
267
- $options[ $prefix . 'robotgen' ] .= $allow_rule;
268
- }
269
  }
270
- foreach ( array( 'ad' => 'additional', 'ua' => 'useragent', 'dp' => 'path' ) as $k => $v ) {
271
- if ( isset( $_POST[ $prefix . $v ] ) ) {
272
- $$k = $_POST[ $prefix . $v ];
273
- }
 
 
 
 
 
 
 
 
274
  }
275
- if ( ! empty( $ad ) && ! empty( $ua ) && ! empty( $dp ) ) {
276
- if ( $ad === 'allow' ) {
277
- $ad = 'Allow: ';
 
278
  } else {
279
- $ad = 'Disallow: ';
280
- }
281
- $options[ $prefix . 'robotgen' ] .= "User-agent: $ua\n$ad $dp\n\n";
282
- }
283
- $file = explode( "\n", $options[ $prefix . 'robotgen' ] );
284
- if ( $optimize ) {
285
- $rules = $this->parse_robots( $file );
286
- $user_agents = $this->get_robot_user_agents( $rules );
287
- foreach ( $user_agents as $ua => $rules ) {
288
- $user_agents[ $ua ]['disallow'] = $this->opt_robot_rule( $rules['disallow'] );
289
- $user_agents[ $ua ]['allow'] = $this->opt_robot_rule( $rules['allow'] );
290
- }
291
- $rules = $this->flatten_user_agents( $user_agents );
292
- unset( $user_agents );
293
- foreach ( $rules as $r ) {
294
- $r['disallow'] = $this->opt_robot_rule( $r['disallow'] );
295
- $r['allow'] = $this->opt_robot_rule( $r['allow'] );
296
  }
297
- $options[ $prefix . 'robotgen' ] = $this->output_robots( $rules );
298
- $file2 = explode( "\n", $options[ $prefix . 'robotgen' ] );
299
- $options[ $prefix . 'robothtml' ] = '<table width=100%><tr><td valign=top width=45%>' . $this->annotate_robots_html( $file, true, __( 'Current File', 'all-in-one-seo-pack' ) ) . '</td><td><span style="font-size: xx-large">&#8594;</span></td><td valign=top>' . $this->annotate_robots_html( $file2, true, __( 'Proposed Changes', 'all-in-one-seo-pack' ) ) . '</td></tr></table>';
300
- } else {
301
- $options[ $prefix . 'robothtml' ] = $this->annotate_robots_html( $file, true, __( 'Current File', 'all-in-one-seo-pack' ) );
302
  }
303
  }
 
 
304
 
305
- return $options;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
306
  }
307
 
308
- function do_robots( $options, $location ) {
309
- if ( $location ) {
310
- $prefix = $this->get_prefix( $location ) . $location . '_';
 
 
311
  }
312
- if ( $location === 'generator' ) {
313
- if ( isset( $_POST['Submit_Update'] ) || isset( $_POST['Submit_Opt_Update'] ) ) {
314
- $this->save_files( array( 'robotgen' => 'robots.txt' ), $prefix );
315
- } elseif ( isset( $_POST['Submit_Delete'] ) ) {
316
- $this->delete_files( array( 'robotgen' => 'robots.txt' ) );
 
317
  }
 
 
 
 
 
 
 
318
  }
 
319
  }
320
 
321
- function annotate_robots_html( $file, $show_help = false, $title = '' ) {
322
- $robots = $this->annotate_robots( $file );
323
- if ( ! empty( $robots ) ) {
324
- $buf = '<table class="widefat" ><thead>';
325
- if ( ! empty( $title ) ) {
326
- $buf .= '<tr><th colspan=3>' . $title . '</th></tr>';
327
- }
328
- $buf .= '<tr class="aioseop_optimize_thread">';
329
- $buf .= '<th style="width:5%;"></th><th style="width:78%;"><span class="column_label" >Parameter</span></th>';
330
- $buf .= '<th><span class="" >Status</span></th></tr></thead>';
331
- $buf .= '<tbody>';
332
-
333
- foreach ( $robots as $r ) {
334
- $class = 'robots';
335
- $status = '#9cf975';
336
- $help = '';
337
- if ( ! $r['valid'] || ! $r['strict'] ) {
338
- if ( ! $r['strict'] ) {
339
- $class .= ' quirks';
340
- $status = 'yellow';
341
- }
342
- if ( ! $r['valid'] ) {
343
- $class .= ' invalid';
344
- $status = '#f9534a';
345
- }
346
- if ( $show_help ) {
347
- $help = '<a style="cursor:pointer;" class="' . $class . '" title="Click for Help!" onclick="toggleVisibility(\'aiosp_robots_main_legend_tip\');" title="Click for Help">'
348
- . '<div class="aioseop_tip_icon"></div></a>';
349
- }
350
- }
351
- $buf .= "<tr class='entry-row {$class}'><td>{$help}</td><td><span class='entry_label'>{$r['content']}</td><td><div style='background:{$status};'></div></td></tr>";
352
- }
353
- $buf .= '</tbody>';
354
-
355
- $buf .= '</table>';
356
- if ( $show_help ) {
357
- $buf .= '<div class="aioseop_option_docs" id="aiosp_robots_main_legend_tip">
358
- <h3>' . __( 'Legend', 'all-in-one-seo-pack' ) . '</h3>
359
- <ul>
360
- <li>' . __( 'The yellow indicator means that a non-standard extension was recognized; not all crawlers may recognize it or interpret it the same way. The Allow and Sitemap directives are commonly used by Google and Yahoo.', 'all-in-one-seo-pack' ) . '</li>
361
- <li>' . __( 'The red indicator means that the syntax is invalid for a robots.txt file.', 'all-in-one-seo-pack' ) . '</li>
362
- </ul>
363
- <a target="_blank" rel="nofollow" href="https://wikipedia.org/wiki/Robots_exclusion_standard#Nonstandard_extensions">' . __( 'More Information', 'all-in-one-seo-pack' ) . '</a>
364
- </div>';
365
- }
366
- } else {
367
- $buf = '<p class="aioseop_error_notice" ><strong>Your Robots.txt file is either empty, cannot be found, or has invalid data.</strong></p>';
368
  }
 
 
 
 
 
 
369
 
370
- return $buf;
 
 
 
 
 
371
  }
372
 
373
- function annotate_robots( $robots ) {
374
- $state = 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
375
  $rules = array();
376
- foreach ( $robots as $l ) {
377
- $l = trim( $l );
378
- if ( empty( $l[0] ) ) {
379
- if ( $state > 1 ) {
380
- $rules[] = array(
381
- 'state' => 0,
382
- 'type' => 'blank',
383
- 'content' => $l,
384
- 'valid' => true,
385
- 'strict' => true,
386
- );
387
- $state = 0;
388
- }
389
- } elseif ( $l[0] === '#' ) {
390
- if ( $state < 1 ) {
391
- $state = 1;
392
- }
393
- $rules[] = array(
394
- 'state' => $state,
395
- 'type' => 'comment',
396
- 'content' => $l,
397
- 'valid' => true,
398
- 'strict' => true,
399
- );
400
- } elseif ( stripos( $l, 'sitemap' ) === 0 ) {
401
- $state = 2;
402
- $rules[] = array(
403
- 'state' => $state,
404
- 'type' => 'sitemap',
405
- 'content' => $l,
406
- 'valid' => true,
407
- 'strict' => false,
408
- );
409
- } elseif ( stripos( $l, 'crawl-delay' ) === 0 ) {
410
- $state = 3;
411
- $rules[] = array(
412
- 'state' => $state,
413
- 'type' => 'crawl-delay',
414
- 'content' => $l,
415
- 'valid' => true,
416
- 'strict' => false,
417
- );
418
- } elseif ( stripos( $l, 'user-agent' ) === 0 ) {
419
- $state = 3;
420
- $rules[] = array(
421
- 'state' => $state,
422
- 'type' => 'user-agent',
423
- 'content' => $l,
424
- 'valid' => true,
425
- 'strict' => true,
426
- );
427
- } elseif ( stripos( $l, 'useragent' ) === 0 ) {
428
- $state = 3;
429
- $rules[] = array(
430
- 'state' => $state,
431
- 'type' => 'user-agent',
432
- 'content' => $l,
433
- 'valid' => true,
434
- 'strict' => false,
435
- );
436
- } elseif ( stripos( $l, 'disallow' ) === 0 ) {
437
- if ( $state < 3 ) {
438
- $rules[] = array(
439
- 'state' => $state,
440
- 'type' => 'disallow',
441
- 'content' => $l,
442
- 'valid' => false,
443
- 'strict' => false,
444
- );
445
- continue;
446
- }
447
- $state = 3;
448
- $rules[] = array(
449
- 'state' => $state,
450
- 'type' => 'disallow',
451
- 'content' => $l,
452
- 'valid' => true,
453
- 'strict' => true,
454
- );
455
- } elseif ( stripos( $l, 'allow' ) === 0 ) {
456
- if ( $state < 3 ) {
457
- $rules[] = array(
458
- 'state' => $state,
459
- 'type' => 'allow',
460
- 'content' => $l,
461
- 'valid' => false,
462
- 'strict' => false,
463
- );
464
- continue;
465
- }
466
- $state = 3;
467
- $rules[] = array(
468
- 'state' => $state,
469
- 'type' => 'allow',
470
- 'content' => $l,
471
- 'valid' => true,
472
- 'strict' => false,
473
- );
474
- } else {
475
- $rules[] = array(
476
- 'state' => $state,
477
- 'type' => 'unknown',
478
- 'content' => $l,
479
- 'valid' => false,
480
- 'strict' => false,
481
- );
482
  }
 
483
  }
 
 
 
484
 
485
- return $rules;
 
 
 
 
 
 
 
 
486
  }
487
 
488
- function parse_annotated_robots( $robots ) {
489
- $state = 0;
490
- $rules = array();
491
- $opts = array( 'sitemap', 'crawl-delay', 'user-agent', 'allow', 'disallow', 'comment' );
492
- $rule = array();
493
- foreach ( $opts as $o ) {
494
- $rule[ $o ] = array();
495
- }
496
- $blank_rule = $rule;
497
- foreach ( $robots as $l ) {
498
- switch ( $l['type'] ) {
499
- case 'blank':
500
- if ( $state >= 1 ) {
501
- if ( ( $state === 1 ) && ( empty( $rule['user-agent'] ) ) ) {
502
- $rule['user-agent'] = array( null );
503
- }
504
- $rules[] = $rule;
505
- $rule = $blank_rule;
506
- }
507
- continue;
508
- case 'comment':
509
- $rule['comment'][] = $l['content'];
510
- continue;
511
- case 'sitemap':
512
- $rule['sitemap'][] = trim( substr( $l['content'], 8 ) );
513
- break;
514
- case 'crawl-delay':
515
- $rule['crawl-delay'][] = trim( substr( $l['content'], 12 ) );
516
- break;
517
- case 'user-agent':
518
- if ( $l['strict'] ) {
519
- $ua = trim( substr( $l['content'], 11 ) );
520
- } else {
521
- $ua = trim( substr( $l['content'], 10 ) );
522
- }
523
- $rule['user-agent'][] = $ua;
524
- break;
525
- case 'disallow':
526
- if ( $l['valid'] ) {
527
- $rule['disallow'][] = trim( substr( $l['content'], 9 ) );
528
- break;
529
- }
530
- continue;
531
- case 'allow':
532
- if ( $l['valid'] ) {
533
- $rule['allow'][] = trim( substr( $l['content'], 6 ) );
534
- break;
535
- }
536
- continue;
537
- case 'unknown':
538
- default:
539
  }
540
- $state = $l['state'];
541
  }
542
- if ( ( $state === 1 ) && ( empty( $rule['user-agent'] ) ) ) {
543
- $rule['user-agent'] = array( null );
 
 
 
 
 
 
 
 
544
  }
545
- if ( $state >= 1 ) {
546
- $rules[] = $rule;
 
 
547
  }
548
 
549
- return $rules;
 
 
 
550
  }
551
 
552
- function parse_robots( $robots ) {
553
- return $this->parse_annotated_robots( $this->annotate_robots( $robots ) );
554
  }
555
 
556
- function get_robot_user_agents( $rules ) {
557
- $opts = array( 'sitemap', 'crawl-delay', 'user-agent', 'allow', 'disallow', 'comment' );
558
- $user_agents = array();
559
- foreach ( $rules as $r ) {
560
- if ( ! empty( $r['sitemap'] ) && empty( $r['user-agent'] ) ) {
561
- $r['user-agent'] = array( null );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
562
  }
563
- foreach ( $r['user-agent'] as $ua ) {
564
- if ( ! isset( $user_agents[ $ua ] ) ) {
565
- $user_agents[ $ua ] = array();
566
- }
567
- foreach ( $opts as $o ) {
568
- if ( ! isset( $user_agents[ $ua ][ $o ] ) ) {
569
- $user_agents[ $ua ][ $o ] = $r[ $o ];
570
- } else {
571
- $user_agents[ $ua ][ $o ] = array_merge( $user_agents[ $ua ][ $o ], $r[ $o ] );
572
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
573
  }
574
  }
575
  }
576
 
577
- return $user_agents;
 
 
 
 
 
578
  }
579
 
580
- function flatten_user_agents( $user_agents ) {
581
- $rules = array();
582
- foreach ( $user_agents as $ua => $r ) {
583
- $r['user-agent'] = array( $ua );
584
- $rules[] = $r;
585
  }
586
-
587
  return $rules;
588
  }
589
 
590
- function opt_robot_rule( $dis ) {
591
- if ( is_array( $dis ) ) { // unique rules only
592
- $dis = array_unique( $dis, SORT_STRING );
593
- $pd = null;
594
- foreach ( $dis as $k => $d ) {
595
- $d = trim( $d );
596
- if ( ! empty( $pd ) && ! empty( $d ) ) {
597
- if ( strpos( $d, $pd ) === 0 ) {
598
- unset( $dis[ $k ] );
599
- continue; // get rid of subpaths of $pd
600
- }
601
- }
602
- $l = strlen( $d );
603
- if ( ( $l > 0 ) && ( $d[ $l - 1 ] !== '/' ) ) {
604
- continue;
605
- }
606
- $pd = $d; // only allow directory paths for $pd
607
  }
 
608
  }
 
 
609
 
610
- return $dis;
 
 
 
 
 
 
 
 
 
611
  }
612
 
613
- function output_robots( $rules ) {
614
- $robots = '';
615
- foreach ( $rules as $r ) {
616
- foreach ( $r['comment'] as $c ) {
617
- $robots .= "$c\n";
618
- }
619
- foreach ( $r['user-agent'] as $u ) {
620
- if ( $u != '' ) {
621
- $robots .= "User-agent: $u\n";
622
- }
623
- }
624
- foreach ( $r['crawl-delay'] as $c ) {
625
- $robots .= "Crawl-Delay: $c\n";
626
- }
627
- foreach ( $r['allow'] as $a ) {
628
- $robots .= "Allow: $a\n";
629
- }
630
- foreach ( $r['disallow'] as $d ) {
631
- $robots .= "Disallow: $d\n";
632
- }
633
- foreach ( $r['sitemap'] as $s ) {
634
- $robots .= "Sitemap: $s\n";
635
- }
636
- $robots .= "\n";
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
637
  }
638
 
639
- return $robots;
 
 
640
  }
641
  }
642
  }
9
  class All_in_One_SEO_Pack_Robots extends All_in_One_SEO_Pack_Module {
10
 
11
  function __construct() {
12
+ // only for testing
13
+ /*
14
+ if ( ! defined( 'AIOSEOP_DO_LOG' ) ) {
15
+ define( 'AIOSEOP_DO_LOG', true );
16
+ }
17
+ */
18
+
19
  $this->name = __( 'Robots.txt', 'all-in-one-seo-pack' ); // Human-readable name of the plugin
20
  $this->prefix = 'aiosp_robots_'; // option prefix
21
  $this->file = __FILE__; // the current file
22
  parent::__construct();
23
 
24
  $help_text = array(
25
+ 'type' => __( 'Rule Type', 'all-in-one-seo-pack' ),
26
+ 'agent' => __( 'User Agent', 'all-in-one-seo-pack' ),
27
  'path' => __( 'Directory Path', 'all-in-one-seo-pack' ),
 
28
  );
29
 
30
  $this->default_options = array(
31
  'usage' => array(
32
  'type' => 'html',
33
  'label' => 'none',
34
+ 'default' => __( 'Use the rule builder below to add/delete rules.', 'all-in-one-seo-pack' ),
35
  'save' => false,
36
  ),
37
+ );
38
+
39
+ $this->rule_fields = array(
40
+ 'agent' => array(
41
+ 'name' => __( 'User Agent', 'all-in-one-seo-pack' ),
42
+ 'type' => 'text',
43
+ 'label' => 'top',
44
  'save' => false,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  ),
46
+ 'type' => array(
47
+ 'name' => __( 'Rule', 'all-in-one-seo-pack' ),
48
+ 'type' => 'select',
49
+ 'initial_options' => array( 'allow' => __( 'Allow', 'all-in-one-seo-pack' ), 'disallow' => __( 'Block', 'all-in-one-seo-pack' ) ),
50
+ 'label' => 'top',
51
+ 'save' => false,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  ),
53
+ 'path' => array(
54
+ 'name' => __( 'Directory Path', 'all-in-one-seo-pack' ),
55
+ 'type' => 'text',
56
+ 'label' => 'top',
57
+ 'save' => false,
 
 
 
 
 
 
 
58
  ),
59
+ 'Submit' => array(
60
  'type' => 'submit',
61
  'class' => 'button-primary',
62
+ 'name' => __( 'Add Rule', 'all-in-one-seo-pack' ) . ' &raquo;',
63
+ 'style' => 'margin-left: 20px;',
64
+ 'label' => 'none',
65
+ 'save' => false,
66
+ 'value' => 1,
67
+ ),
68
+ 'rules' => array(
69
+ 'name' => __( 'Configured Rules', 'all-in-one-seo-pack' ),
70
+ 'type' => 'custom',
71
+ 'save' => true,
72
+ ),
73
+ 'robots.txt' => array(
74
+ 'name' => __( 'Robots.txt', 'all-in-one-seo-pack' ),
75
+ 'type' => 'custom',
76
+ 'save' => true,
77
  ),
78
  );
79
 
80
+ add_filter( $this->prefix . 'submit_options', array( $this, 'submit_options'), 10, 2 );
81
+
82
+ $this->default_options = array_merge( $this->default_options, $this->rule_fields );
83
+
84
  if ( ! empty( $help_text ) ) {
85
  foreach ( $help_text as $k => $v ) {
86
  $this->default_options[ $k ]['help_text'] = $v;
87
  }
88
  }
89
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
90
  $this->layout = array(
91
  'default' => array(
92
  'name' => __( 'Create a Robots.txt File', 'all-in-one-seo-pack' ),
93
+ 'help_link' => 'https://semperplugins.com/documentation/robots-txt-module/',
94
+ 'options' => array_merge( array( 'usage' ), array_keys( $this->rule_fields ) ),
 
 
 
 
 
 
 
 
95
  ),
96
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
  // load initial options / set defaults
99
  $this->update_options();
100
 
101
+ add_filter( $this->prefix . 'output_option', array( $this, 'display_custom_options' ), 10, 2 );
102
+ add_filter( $this->prefix . 'update_options', array( $this, 'filter_options' ) );
103
+ add_filter( $this->prefix . 'display_options', array( $this, 'filter_display_options' ) );
104
+ add_action( 'wp_ajax_aioseop_ajax_delete_rule', array( $this, 'ajax_delete_rule' ) );
105
+ add_action( 'wp_ajax_aioseop_ajax_robots_physical', array( $this, 'ajax_action_physical_file' ) );
106
+ add_filter( 'robots_txt', array( $this, 'robots_txt' ), 10, 2 );
107
  }
108
 
109
+ function physical_file_check() {
110
+ if ( $this->has_physical_file() ) {
111
+ if ( ( is_multisite() && is_network_admin() ) || ( ! is_multisite() && current_user_can( 'manage_options') ) ) {
112
+ $this->default_options['usage']['default'] .= '<div id="aiosp_robots_physical_import_delete"><p>' . sprintf( __( 'A physical file exists. Do you want to %simport and delete%s it, %sdelete%s it or continue using it?', 'all-in-one-seo-pack' ), '<a href="#" class="aiosp_robots_physical aiosp_robots_import" data-action="import">', '</a>', '<a href="#" class="aiosp_robots_physical aiosp_robots_delete" data-action="delete">', '</a>' ) . '</p></div>';
113
+ } else {
114
+ $this->default_options['usage']['default'] .= '<p>' . __( 'A physical file exists. This feature cannot be used.', 'all-in-one-seo-pack' ) . '</p>';
 
 
 
 
 
 
 
 
 
 
 
115
  }
 
116
 
117
+ return;
118
+ } else {
119
+ add_action( 'admin_init', array( $this, 'import_default_robots' ) );
120
+ }
121
  }
122
 
123
+ function filter_display_options( $options ) {
124
+ $errors = get_transient( "{$this->prefix}errors" . get_current_user_id() );
125
+ if ( false !== $errors ) {
126
+ if ( is_array( $errors ) ) {
127
+ $errors = implode( '<br>', $errors );
128
+ }
129
+ echo sprintf( '<div class="notice notice-error"><p>%s</p></div>', $errors );
130
  }
131
+ return $options;
 
132
  }
133
 
134
  /**
135
+ * First time import of the default robots.txt rules.
 
 
136
  */
137
+ function import_default_robots() {
138
+ $options = $this->get_option_for_blog( $this->get_network_id() );
139
+ if ( array_key_exists( 'default', $options ) ) {
140
+ return;
141
+ }
142
+
143
+ $default = $this->do_robots();
144
+ $lines = explode( "\n", $default );
145
+ $rules = $this->extract_rules( $lines );
146
+ aiosp_log("adding default rules: " . print_r($rules,true));
147
 
148
  global $aioseop_options;
149
+ $aioseop_options['modules']["{$this->prefix}options"]['default'] = $rules;
150
+ update_option( 'aioseop_options', $aioseop_options );
151
+ }
152
 
153
+ function submit_options( $submit_options, $location ) {
154
+ unset( $submit_options['Submit'] );
155
+ unset( $submit_options['Submit_Default'] );
156
+ return $submit_options;
157
  }
158
 
159
+ function ajax_action_physical_file() {
160
+ aioseop_ajax_init();
161
+ $action = $_POST['options'];
162
+
163
+ switch ( $action ) {
164
+ case 'import':
165
+ $this->import_default_robots();
166
+ if ( ! $this->import_physical_file() ) {
167
+ wp_send_json_success( array( 'message' => __( 'Unable to read file', 'all-in-one-seo-pack' ) ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  }
169
+ // fall-through.
170
+ case 'delete':
171
+ if ( ! $this->delete_physical_file() ) {
172
+ wp_send_json_success( array( 'message' => __( 'Unable to delete file', 'all-in-one-seo-pack' ) ) );
 
 
 
173
  }
174
+ break;
175
+ }
176
+
177
+ wp_send_json_success();
178
+ }
179
+
180
+ private function import_physical_file() {
181
+ $wp_filesystem = $this->get_filesystem_object();
182
+ $file = trailingslashit( $wp_filesystem->abspath() ) . 'robots.txt';
183
+ if ( ! $wp_filesystem->is_readable( $file ) ) {
184
+ return false;
185
+ }
186
+
187
+ $lines = $wp_filesystem->get_contents_array( $file );
188
+ if ( ! $lines ) {
189
+ return true;
190
+ }
191
+
192
+ $rules = $this->extract_rules( $lines );
193
+ aiosp_log("importing rules: " . print_r($rules,true));
194
+
195
+ global $aioseop_options;
196
+ $aioseop_options['modules']["{$this->prefix}options"]["{$this->prefix}rules"] = $rules;
197
+ update_option( 'aioseop_options', $aioseop_options );
198
+ return true;
199
+ }
200
+
201
+ private function extract_rules( array $lines ) {
202
+ $rules = array();
203
+ $user_agent = null;
204
+ $rule = array();
205
+ $blog_rules = $this->get_all_rules();
206
+ foreach ( $lines as $line ) {
207
+ if ( empty( $line ) ) {
208
+ continue;
209
  }
210
+ $array = array_map( 'trim', explode( ':', $line ) );
211
+ if ( $array && count( $array ) !== 2 ) {
212
+ aiosp_log( "Ignoring $line from robots.txt" );
213
+ continue;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
  }
215
+ $operand = $array[0];
216
+ switch ( strtolower( $operand ) ) {
217
+ case 'user-agent':
218
+ $user_agent = $array[1];
219
+ break;
220
+ case 'disallow':
221
+ // fall-through.
222
+ case 'allow':
223
+ $rule[ 'agent' ] = $user_agent;
224
+ $rule[ 'type' ] = $operand;
225
+ $rule[ 'path' ] = $array[1];
226
+ break;
227
  }
228
+ if ( $rule ) {
229
+ $rule = $this->validate_rule( $blog_rules, $rule );
230
+ if ( is_wp_error( $rule ) ) {
231
+ $this->add_error( $rule );
232
  } else {
233
+ $rules[] = $rule;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  }
235
+ $rule = array();
 
 
 
 
236
  }
237
  }
238
+ return $rules;
239
+ }
240
 
241
+ private function delete_physical_file() {
242
+ $wp_filesystem = $this->get_filesystem_object();
243
+ $file = trailingslashit( $wp_filesystem->abspath() ) . 'robots.txt';
244
+ return $wp_filesystem->delete( $file );
245
+ }
246
+
247
+ private function has_physical_file() {
248
+ $access_type = get_filesystem_method();
249
+
250
+ if ( 'direct' === $access_type ) {
251
+ $wp_filesystem = $this->get_filesystem_object();
252
+ $file = trailingslashit( $wp_filesystem->abspath() ) . 'robots.txt';
253
+
254
+ return $wp_filesystem->exists( $file );
255
+ }
256
+ }
257
+
258
+ function robots_txt( $output, $public ) {
259
+ return $output . "\r\n" . $this->get_rules();
260
  }
261
 
262
+ private function get_rules() {
263
+ $robots = array();
264
+ $blog_rules = $this->get_all_rules( is_multisite() ? $this->get_network_id() : null );
265
+ if ( is_multisite() && $this->get_network_id() != get_current_blog_id() ) {
266
+ $blog_rules = array_merge( $blog_rules, $this->get_all_rules( get_current_blog_id() ) );
267
  }
268
+ $rules = array();
269
+ foreach ( $blog_rules as $rule ) {
270
+ $condition = sprintf( '%s: %s', $rule['type'], $rule['path'] );
271
+ $agent = $rule['agent'];
272
+ if ( ! array_key_exists( $agent, $rules ) ) {
273
+ $rules[$agent] = array();
274
  }
275
+ $rules[ $agent ][] = $condition;
276
+ }
277
+
278
+ foreach( $rules as $agent => $conditions ) {
279
+ $robots[] = sprintf( 'User-agent: %s', $agent );
280
+ $robots[] = implode( "\r\n", $conditions );
281
+ $robots[] = "";
282
  }
283
+ return implode( "\r\n", $robots );
284
  }
285
 
286
+ private function get_network_id() {
287
+ if ( is_multisite() ) {
288
+ return get_network()->site_id;
289
+ }
290
+ return get_current_blog_id();
291
+ }
292
+
293
+ private function get_option_for_blog( $id = null ) {
294
+ if ( is_null( $id ) ) {
295
+ $id = get_current_blog_id();
296
+ }
297
+ if ( is_multisite() ) {
298
+ switch_to_blog( $id );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
  }
300
+ $options = get_option('aioseop_options');
301
+ if ( is_multisite() ) {
302
+ restore_current_blog();
303
+ }
304
+ return array_key_exists( 'modules', $options ) && array_key_exists( "{$this->prefix}options", $options['modules'] ) ? $options['modules']["{$this->prefix}options"] : array();
305
+ }
306
 
307
+ /**
308
+ * Get all rules defined for the blog.
309
+ */
310
+ private function get_all_rules( $id = null ) {
311
+ $options = $this->get_option_for_blog( $id );
312
+ return array_key_exists( "{$this->prefix}rules", $options ) ? $options[ "{$this->prefix}rules" ] : array();
313
  }
314
 
315
+ /**
316
+ * Get the default robot rules that were saved in the first initialization.
317
+ */
318
+ private function get_default_rules() {
319
+ $options = $this->get_option_for_blog( $this->get_network_id() );
320
+ return array_key_exists( 'default', $options ) ? $options[ 'default' ] : array();
321
+ }
322
+
323
+ function ajax_delete_rule() {
324
+ aioseop_ajax_init();
325
+ $id = $_POST['options'];
326
+
327
+ global $aioseop_options;
328
+
329
+ // first check the defined rules.
330
+ $blog_rules = $this->get_all_rules();
331
  $rules = array();
332
+ foreach ( $blog_rules as $rule ) {
333
+ if ( $id === $rule['id'] ) {
334
+ continue;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
335
  }
336
+ $rules[] = $rule;
337
  }
338
+ $aioseop_options['modules']["{$this->prefix}options"]["{$this->prefix}rules"] = $rules;
339
+ update_option( 'aioseop_options', $aioseop_options );
340
+ }
341
 
342
+
343
+ private function add_error( $error ) {
344
+ $errors = get_transient( "{$this->prefix}errors" . get_current_user_id() );
345
+ if ( false === $errors ) {
346
+ $errors = array();
347
+ }
348
+ $errors[] = $error->get_error_message();
349
+ // set the error in a transient.
350
+ set_transient( "{$this->prefix}errors" . get_current_user_id(), $errors, 5 );
351
  }
352
 
353
+ /**
354
+ * Filter options.
355
+ *
356
+ * @param $options
357
+ *
358
+ * @return mixed
359
+ */
360
+ function filter_options( $options ) {
361
+ $blog_rules = $this->get_all_rules();
362
+ if ( ! empty( $_POST[ "{$this->prefix}path" ] ) ) {
363
+ foreach ( array_keys( $this->rule_fields ) as $field ) {
364
+ $post_field = $this->prefix . "" . $field;
365
+ if ( ! empty( $_POST[ $post_field ] ) ) {
366
+ $_POST[ $post_field ] = esc_attr( wp_kses_post( $_POST[ $post_field ] ) );
367
+ } else {
368
+ $_POST[ $post_field ] = '';
369
+ }
370
+ }
371
+ $new_rule = array(
372
+ 'path' => $_POST[ "{$this->prefix}path" ],
373
+ 'type' => $_POST[ "{$this->prefix}type" ],
374
+ 'agent' => $_POST[ "{$this->prefix}agent" ],
375
+ );
376
+ $rule = $this->validate_rule( $blog_rules, $new_rule );
377
+ if ( is_wp_error( $rule ) ) {
378
+ $this->add_error( $rule );
379
+ } else {
380
+ $blog_rules[] = $rule;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
381
  }
 
382
  }
383
+ // testing only - to clear the rules.
384
+ //$blog_rules = array();
385
+ $options[ "{$this->prefix}rules" ] = $blog_rules;
386
+ return $options;
387
+ }
388
+
389
+ private function sanitize_path( $path ) {
390
+ // if path does not have a trailing wild card (*) or does not refer to a file (with extension), add trailing slash.
391
+ if ( '*' !== substr( $path, -1 ) && false === strpos( $path, '.' ) ) {
392
+ $path = trailingslashit( $path );
393
  }
394
+
395
+ // if path does not have a leading slash, add it.
396
+ if ( '/' !== substr( $path, 0, 1 ) ) {
397
+ $path = '/' . $path;
398
  }
399
 
400
+ // convert everything to lower case.
401
+ $path = strtolower( $path );
402
+
403
+ return $path;
404
  }
405
 
406
+ private function create_rule_id( $type, $agent, $path ) {
407
+ return md5( $type . $agent . $path );
408
  }
409
 
410
+ private function validate_rule( $rules, $new_rule ) {
411
+ if ( empty( $new_rule[ 'agent' ] ) ) {
412
+ return new WP_Error('invalid', __( 'User Agent cannot be empty', 'all-in-one-seo-pack' ) );
413
+ }
414
+ if ( empty( $new_rule[ 'path' ] ) ) {
415
+ return new WP_Error('invalid', __( 'Directory Path cannot be empty', 'all-in-one-seo-pack' ) );
416
+ }
417
+
418
+ $default = $this->get_default_rules();
419
+ $network = $this->get_all_rules( $this->get_network_id() );
420
+ if ( ! is_array( $network ) ) {
421
+ $network = array();
422
+ }
423
+ $network = array_merge( $default, $network, $rules );
424
+
425
+ // sanitize path.
426
+ $path = $this->sanitize_path( $new_rule[ 'path' ] );
427
+
428
+ // generate id to check uniqueness and also for purposes of deletion.
429
+ $id = $this->create_rule_id( $new_rule[ 'type' ], $new_rule[ 'agent' ], $path );
430
+ if ( is_array( $rules ) ) {
431
+ $ids = wp_list_pluck( $rules, 'id' );
432
+ if ( in_array( $id, $ids ) ) {
433
+ aiosp_log("rejected: same rule id exists - " . print_r($new_rule,true) . " vs. " . print_r($rules,true));
434
+ return new WP_Error('duplicate', sprintf( __( 'Identical rule exists: %s', 'all-in-one-seo-pack' ), $new_rule[ 'path' ] ) );
435
  }
436
+ }
437
+
438
+ if ( $network ) {
439
+ $nw_agent_paths = array();
440
+ foreach ( $network as $n ) {
441
+ $nw_agent_paths[] = $n['agent'] . $n['path'];
442
+ }
443
+
444
+ // the same rule cannot be duplicated by the Admin.
445
+ $agent_path = $new_rule[ 'agent' ] . $path;
446
+ if ( in_array( $agent_path, $nw_agent_paths ) ) {
447
+ aiosp_log("rejected: same agent/path being overridden - " . print_r($new_rule,true) . " vs. " . print_r($rules,true));
448
+ return new WP_Error('duplicate', sprintf( __( 'Rule cannot be overridden: %s', 'all-in-one-seo-pack' ), $new_rule[ 'path' ] ) );
449
+ }
450
+
451
+ // an identical path as specified by Network Admin cannot be overriden by Admin.
452
+ $nw_paths = wp_list_pluck( $network, 'path' );
453
+ if ( in_array( $path, $nw_paths ) ) {
454
+ aiosp_log("rejected: same path being overridden - " . print_r($new_rule,true) . " vs. " . print_r($rules,true));
455
+ return new WP_Error('duplicate', sprintf( __( 'Path cannot be overridden: %s', 'all-in-one-seo-pack' ), $new_rule[ 'path' ] ) );
456
+ }
457
+
458
+ // a wild-carded path specified by the Admin cannot override a path specified by Network Admin.
459
+ $pattern = str_replace(
460
+ array(
461
+ '.',
462
+ '/',
463
+ '*',
464
+ ),
465
+ array(
466
+ '\.',
467
+ '\/',
468
+ '(.*)',
469
+ ),
470
+ $path
471
+ );
472
+ foreach ( $nw_paths as $nw_path ) {
473
+ $matches = array();
474
+ preg_match( "/{$pattern}/", $nw_path, $matches );
475
+ if ( ! empty( $matches ) && count( $matches ) >= 2 && ! empty( $matches[1] ) ) {
476
+ aiosp_log("rejected: wild card path being overridden - " . print_r($new_rule,true) . " vs. " . print_r($rules,true));
477
+ return new WP_Error('conflict', sprintf( __( 'Wild-card path cannot be overridden: %s', 'all-in-one-seo-pack' ), $new_rule[ 'path' ] ) );
478
  }
479
  }
480
  }
481
 
482
+ return array(
483
+ 'type' => ucwords( $new_rule[ 'type' ] ),
484
+ 'agent' => $new_rule[ 'agent' ],
485
+ 'path' => $path,
486
+ 'id' => $id,
487
+ );
488
  }
489
 
490
+ private function reorder_rules( $rules ) {
491
+ if ( is_array( $rules ) ) {
492
+ uasort( $rules, array( $this, 'sort_rules' ) );
 
 
493
  }
 
494
  return $rules;
495
  }
496
 
497
+ function sort_rules( $a, $b ) {
498
+ return $a['agent'] > $b['agent'];
499
+ }
500
+
501
+ private function get_display_rules( $rules ) {
502
+ $buf = '';
503
+ if ( ! empty( $rules ) ) {
504
+ $rules = $this->reorder_rules( $rules );
505
+ $buf = "<table class='aioseop_table' cellpadding=0 cellspacing=0>\n";
506
+ $row = "\t<tr><td><a href='#' class='aiosp_delete aiosp_robots_delete_rule' data-id='%s'></a></td><td>%s</td><td>%s</td><td>%s</td></tr>\n";
507
+ foreach ( $rules as $v ) {
508
+ $buf .= sprintf( $row, $v['id'], $v['agent'], $v['type'], $v['path'] );
 
 
 
 
 
509
  }
510
+ $buf .= "</table>\n";
511
  }
512
+ return $buf;
513
+ }
514
 
515
+ private function do_robots() {
516
+ // disable header warnings.
517
+ error_reporting(0);
518
+ ob_start();
519
+ do_action( 'do_robots' );
520
+ if ( is_admin() ) {
521
+ // conflict with WooCommerce etc. cause the page to render as text/plain.
522
+ header( 'Content-Type:text/html' );
523
+ }
524
+ return ob_get_clean();
525
  }
526
 
527
+ /**
528
+ * Custom settings.
529
+ *
530
+ * Displays boxes in a table layout.
531
+ *
532
+ * @param $buf
533
+ * @param $args
534
+ *
535
+ * @return string
536
+ */
537
+ function display_custom_options( $buf, $args ) {
538
+ switch ( $args['name'] ) {
539
+ case "{$this->prefix}rules":
540
+ $buf .= "<div id='{$this->prefix}rules'>";
541
+ $rules = $args['value'];
542
+ $buf .= $this->get_display_rules( $rules );
543
+ $buf .= '</div>';
544
+ break;
545
+ case "{$this->prefix}robots.txt":
546
+ $buf .= "<textarea disabled id='{$this->prefix}robot-txt' class='large-text robots-text' rows='15'>";
547
+ $buf .= $this->do_robots();
548
+ $buf .= "</textarea>";
549
+ break;
550
+ }
551
+
552
+ $args['options']['type'] = 'hidden';
553
+ if ( ! empty( $args['value'] ) ) {
554
+ $args['value'] = wp_json_encode( $args['value'] );
555
+ } else {
556
+ $args['options']['type'] = 'html';
557
+ }
558
+ if ( empty( $args['value'] ) ) {
559
+ $args['value'] = '';
560
+ }
561
+ $buf .= $this->get_option_html( $args );
562
+
563
+ return $buf;
564
+ }
565
+
566
+ /**
567
+ * Add Menu
568
+ *
569
+ * (Parent) Adds the wp-admin menu, and this adds additional menu & load-hooks for
570
+ * the 1mporting and/or deleting the `robot.txt` file.
571
+ *
572
+ * @since 2.7.2
573
+ *
574
+ * @param $parent_slug
575
+ * @return bool
576
+ */
577
+ public function add_menu( $parent_slug ) {
578
+ $hook = 'all-in-one-seo_page_' . AIOSEOP_PLUGIN_DIRNAME . '/modules/aioseop_robots';
579
+ if ( is_multisite() && is_network_admin() ) {
580
+ // Add the robots.txt editor into the network admin menu.
581
+ $hook = add_menu_page(
582
+ 'Robots.txt Editor',
583
+ 'Robots.txt Editor',
584
+ 'edit_themes',
585
+ plugin_basename( $this->file ),
586
+ array(
587
+ $this,
588
+ 'display_settings_page',
589
+ )
590
+ );
591
  }
592
 
593
+ add_action( 'load-' . $hook, array( $this, 'physical_file_check' ) );
594
+
595
+ return parent::add_menu( $parent_slug );
596
  }
597
  }
598
  }
modules/aioseop_sitemap.php CHANGED
@@ -502,7 +502,7 @@ if ( ! class_exists( 'All_in_One_SEO_Pack_Sitemap' ) ) {
502
  if ( is_object( $v ) ) {
503
  $v = (Array) $v;
504
  }
505
- $buf .= "\t<tr><td><a href='#' title='$k' class='aiosp_delete_url'></a> {$k}</td><td>{$v['prio']}</td><td>{$v['freq']}</td><td>{$v['mod']}</td></tr>\n";
506
  }
507
  $buf .= "</table>\n";
508
  }
502
  if ( is_object( $v ) ) {
503
  $v = (Array) $v;
504
  }
505
+ $buf .= "\t<tr><td><a href='#' title='$k' class='aiosp_delete aiosp_delete_url'></a> {$k}</td><td>{$v['prio']}</td><td>{$v['freq']}</td><td>{$v['mod']}</td></tr>\n";
506
  }
507
  $buf .= "</table>\n";
508
  }
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=mrtor
4
  Tags: seo, all in one seo, google, twitter, page, image seo, social, search engine optimization, sitemap, WordPress SEO, meta, meta description, xml sitemap, google sitemap, sitemaps, robots meta, yahoo, bing, news sitemaps, multisite, canonical, nofollow, noindex, keywords, description, webmaster tools, google webmaster tools, google analytics
5
  Requires at least: 4.4
6
  Tested up to: 4.9
7
- Stable tag: 2.6.1
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
4
  Tags: seo, all in one seo, google, twitter, page, image seo, social, search engine optimization, sitemap, WordPress SEO, meta, meta description, xml sitemap, google sitemap, sitemaps, robots meta, yahoo, bing, news sitemaps, multisite, canonical, nofollow, noindex, keywords, description, webmaster tools, google webmaster tools, google analytics
5
  Requires at least: 4.4
6
  Tested up to: 4.9
7
+ Stable tag: 2.7.2
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10