Breadcrumb NavXT - Version 7.1.0

Version Description

Release date: July, 17th 2022

  • Behavior change: BCN_SETTINGS_FAVOR_* constant behavior changed to take advantage of settings changes in 7.0.
  • Behavior change: Settings page now warns if it detected CPTs that were added too late.
  • New feature: Added bcn_before_loop filter which runs at the top of bcn_breadcrumb_trail::display_loop().
  • New feature: Added JSON formatted settings import and export (successor to the old XML settings import/export format).
  • Bug fix: Fixed PHP error caused by term archives that dont know the active term.
  • Bug fix: Fixed PHP error caused by introduction of namespacing in the legacy XML settings importer and exporter.
  • Bug fix: Explicitly set option autoload values, avoiding inappropriate default autoload values being used.
Download this release

Release Info

Developer mtekk
Plugin Icon 128x128 Breadcrumb NavXT
Version 7.1.0
Comparing to
See all releases

Code changes from version 7.0.2 to 7.1.0

breadcrumb-navxt.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Breadcrumb NavXT
4
  Plugin URI: http://mtekk.us/code/breadcrumb-navxt/
5
  Description: Adds a breadcrumb navigation showing the visitor&#39;s path to their current location. For details on how to use this plugin visit <a href="http://mtekk.us/code/breadcrumb-navxt/">Breadcrumb NavXT</a>.
6
- Version: 7.0.2
7
  Author: John Havlik
8
  Author URI: http://mtekk.us/
9
  License: GPL2
@@ -63,7 +63,7 @@ $breadcrumb_navxt = null;
63
  //TODO change to extends \mtekk\plugKit
64
  class breadcrumb_navxt
65
  {
66
- const version = '7.0.2';
67
  protected $name = 'Breadcrumb NavXT';
68
  protected $identifier = 'breadcrumb-navxt';
69
  protected $unique_prefix = 'bcn';
@@ -386,7 +386,7 @@ class breadcrumb_navxt
386
  false,
387
  _x('Paged Breadcrumb', 'Paged as in when on an archive or post that is split into multiple pages', 'breadcrumb-navxt'));
388
  //Post types
389
- foreach($GLOBALS['wp_post_types']as $post_type)
390
  {
391
  $settings['Hpost_' . $post_type->name . '_template'] = new setting\setting_html(
392
  'post_' . $post_type->name . '_template',
@@ -609,26 +609,27 @@ class breadcrumb_navxt
609
  $opts = adminKit::settings_to_opts($this->settings);
610
  //Run setup_options for compatibilty reasons
611
  breadcrumb_navxt::setup_options($opts);
612
-
613
  //Grab the current settings for the current local site from the db
614
  $this->breadcrumb_trail->opt = wp_parse_args(get_option('bcn_options'), $opts);
615
  //If we're in multisite mode, look at the three BCN_SETTINGS globals
616
  if(is_multisite())
617
  {
 
618
  if(defined('BCN_SETTINGS_USE_NETWORK') && BCN_SETTINGS_USE_NETWORK)
619
  {
620
  //Grab the current network wide settings
621
- $this->breadcrumb_trail->opt = wp_parse_args(get_site_option('bcn_options'), $opts);
622
  }
623
  else if(defined('BCN_SETTINGS_FAVOR_LOCAL') && BCN_SETTINGS_FAVOR_LOCAL)
624
  {
625
- //Grab the current settings for the current local site from the db
626
- $this->breadcrumb_trail->opt = wp_parse_args(get_option('bcn_options'), $this->breadcrumb_trail->opt);
627
  }
628
  else if(defined('BCN_SETTINGS_FAVOR_NETWORK') && BCN_SETTINGS_FAVOR_NETWORK)
629
  {
630
- //Grab the current settings from the db
631
- $this->breadcrumb_trail->opt = wp_parse_args(get_site_option('bcn_options'), get_option('bcn_options'));
632
  }
633
  }
634
  //Currently only support using post_parent for the page hierarchy
3
  Plugin Name: Breadcrumb NavXT
4
  Plugin URI: http://mtekk.us/code/breadcrumb-navxt/
5
  Description: Adds a breadcrumb navigation showing the visitor&#39;s path to their current location. For details on how to use this plugin visit <a href="http://mtekk.us/code/breadcrumb-navxt/">Breadcrumb NavXT</a>.
6
+ Version: 7.1.0
7
  Author: John Havlik
8
  Author URI: http://mtekk.us/
9
  License: GPL2
63
  //TODO change to extends \mtekk\plugKit
64
  class breadcrumb_navxt
65
  {
66
+ const version = '7.1.0';
67
  protected $name = 'Breadcrumb NavXT';
68
  protected $identifier = 'breadcrumb-navxt';
69
  protected $unique_prefix = 'bcn';
386
  false,
387
  _x('Paged Breadcrumb', 'Paged as in when on an archive or post that is split into multiple pages', 'breadcrumb-navxt'));
388
  //Post types
389
+ foreach($GLOBALS['wp_post_types'] as $post_type)
390
  {
391
  $settings['Hpost_' . $post_type->name . '_template'] = new setting\setting_html(
392
  'post_' . $post_type->name . '_template',
609
  $opts = adminKit::settings_to_opts($this->settings);
610
  //Run setup_options for compatibilty reasons
611
  breadcrumb_navxt::setup_options($opts);
612
+ //TODO: Unit tests needed to ensure the expected behavior exists
613
  //Grab the current settings for the current local site from the db
614
  $this->breadcrumb_trail->opt = wp_parse_args(get_option('bcn_options'), $opts);
615
  //If we're in multisite mode, look at the three BCN_SETTINGS globals
616
  if(is_multisite())
617
  {
618
+ $multisite_opts = wp_parse_args(get_site_option('bcn_options'), $opts);
619
  if(defined('BCN_SETTINGS_USE_NETWORK') && BCN_SETTINGS_USE_NETWORK)
620
  {
621
  //Grab the current network wide settings
622
+ $this->breadcrumb_trail->opt = $multisite_opts;
623
  }
624
  else if(defined('BCN_SETTINGS_FAVOR_LOCAL') && BCN_SETTINGS_FAVOR_LOCAL)
625
  {
626
+ //Grab the current local site settings and merge into network site settings + defaults
627
+ $this->breadcrumb_trail->opt = wp_parse_args(get_option('bcn_options'), $multisite_opts);
628
  }
629
  else if(defined('BCN_SETTINGS_FAVOR_NETWORK') && BCN_SETTINGS_FAVOR_NETWORK)
630
  {
631
+ //Grab the current network site settings and merge into local site settings + defaults
632
+ $this->breadcrumb_trail->opt = wp_parse_args(get_site_option('bcn_options'), $this->breadcrumb_trail->opt);
633
  }
634
  }
635
  //Currently only support using post_parent for the page hierarchy
class.bcn_admin.php CHANGED
@@ -44,7 +44,7 @@ use mtekk\adminKit\{adminKit, form, message, setting};
44
  */
45
  class bcn_admin extends adminKit
46
  {
47
- const version = '7.0.2';
48
  protected $full_name = 'Breadcrumb NavXT Settings';
49
  protected $short_name = 'Breadcrumb NavXT';
50
  protected $access_level = 'bcn_manage_options';
@@ -321,6 +321,42 @@ class bcn_admin extends adminKit
321
  }
322
  }
323
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
324
  /**
325
  * Function checks the current site to see if the blog options should be disabled
326
  *
@@ -348,6 +384,8 @@ class bcn_admin extends adminKit
348
  $this->security();
349
  //Do a check on deprecated settings
350
  $this->deprecated_settings_warn();
 
 
351
  //Do a check for multisite settings mode
352
  $this->multisite_settings_warn();
353
  do_action($this->unique_prefix . '_settings_pre_messages', $this->settings);
@@ -416,8 +454,8 @@ class bcn_admin extends adminKit
416
  //Loop through all of the post types in the array
417
  foreach($wp_post_types as $post_type)
418
  {
419
- //Check for non-public CPTs
420
- if(!apply_filters('bcn_show_cpt_private', $post_type->public, $post_type->name))
421
  {
422
  continue;
423
  }
@@ -443,7 +481,13 @@ class bcn_admin extends adminKit
443
  <?php
444
  $this->form->input_check($this->settings['bpost_' . $post_type->name . '_archive_display'], sprintf(__('Show the breadcrumb for the %s post type archives in the breadcrumb trail.', 'breadcrumb-navxt'), $singular_name_lc), !$post_type->has_archive);
445
  }
446
- if(!in_array($post_type->name, array('page')))
 
 
 
 
 
 
447
  {
448
  $this->form->input_check($this->settings['bpost_' . $post_type->name . '_hierarchy_display'], sprintf(__('Show the hierarchy (specified below) leading to a %s in the breadcrumb trail.', 'breadcrumb-navxt'), $singular_name_lc), false, '', 'adminkit-enset-ctrl adminkit-enset');
449
  $this->form->input_check($this->settings['bpost_' . $post_type->name . '_hierarchy_parent_first'], sprintf(__('Use the parent of the %s as the primary hierarchy, falling back to the hierarchy selected below when the parent hierarchy is exhausted.', 'breadcrumb-navxt'), $singular_name_lc), false, '', 'adminkit-enset');
@@ -494,6 +538,7 @@ class bcn_admin extends adminKit
494
  <?php
495
  }
496
  do_action($this->unique_prefix . '_after_settings_tab_post', $this->settings);
 
497
  ?>
498
  </fieldset>
499
  <fieldset id="tax" class="bcn_options alttab">
@@ -523,8 +568,8 @@ class bcn_admin extends adminKit
523
  //Loop through all of the taxonomies in the array
524
  foreach($wp_taxonomies as $taxonomy)
525
  {
526
- //Check for non-public taxonomies
527
- if(!apply_filters('bcn_show_tax_private', $taxonomy->public, $taxonomy->name, null))
528
  {
529
  continue;
530
  }
44
  */
45
  class bcn_admin extends adminKit
46
  {
47
+ const version = '7.1.0';
48
  protected $full_name = 'Breadcrumb NavXT Settings';
49
  protected $short_name = 'Breadcrumb NavXT';
50
  protected $access_level = 'bcn_manage_options';
321
  }
322
  }
323
  }
324
+ /**
325
+ * A message function that checks for post types added after the settings defaults were established
326
+ */
327
+ function unknown_custom_types_warn()
328
+ {
329
+ foreach($GLOBALS['wp_post_types'] as $post_type)
330
+ {
331
+ //If we haven't seen this post type before, warn the user
332
+ if(!isset($this->settings['Hpost_' . $post_type->name . '_template']))
333
+ {
334
+ $this->messages[] = new message(
335
+ sprintf(
336
+ esc_html__('Warning: The post type %1$s (%2$s) was registered after the Breadcrumb NavXT default settings. It will not show up in the settings.', 'breadcrumb-navxt'),
337
+ $post_type->labels->singular_name,
338
+ $post_type->name),
339
+ 'warning',
340
+ true,
341
+ $post_type->name);
342
+ }
343
+ }
344
+ foreach($GLOBALS['wp_taxonomies'] as $taxonomy)
345
+ {
346
+ if(!isset($this->settings['Htax_' . $taxonomy->name . '_template']))
347
+ {
348
+ //If we haven't seen this taxonomy before, warn the user
349
+ $this->messages[] = new message(
350
+ sprintf(
351
+ esc_html__('Warning: The taxonomy %1$s (%2$s) was registered after the Breadcrumb NavXT default settings. It will not show up in the settings.', 'breadcrumb-navxt'),
352
+ $taxonomy->label,
353
+ $taxonomy->name),
354
+ 'warning',
355
+ true,
356
+ $taxonomy->name);
357
+ }
358
+ }
359
+ }
360
  /**
361
  * Function checks the current site to see if the blog options should be disabled
362
  *
384
  $this->security();
385
  //Do a check on deprecated settings
386
  $this->deprecated_settings_warn();
387
+ //Do a check for unknown CPTs and Taxnomies
388
+ $this->unknown_custom_types_warn();
389
  //Do a check for multisite settings mode
390
  $this->multisite_settings_warn();
391
  do_action($this->unique_prefix . '_settings_pre_messages', $this->settings);
454
  //Loop through all of the post types in the array
455
  foreach($wp_post_types as $post_type)
456
  {
457
+ //Check for non-public CPTs and if the CPT wasn't known when defaults were generated
458
+ if(!apply_filters('bcn_show_cpt_private', $post_type->public, $post_type->name) || !isset($this->settings['Hpost_' . $post_type->name . '_template']))
459
  {
460
  continue;
461
  }
481
  <?php
482
  $this->form->input_check($this->settings['bpost_' . $post_type->name . '_archive_display'], sprintf(__('Show the breadcrumb for the %s post type archives in the breadcrumb trail.', 'breadcrumb-navxt'), $singular_name_lc), !$post_type->has_archive);
483
  }
484
+ if(in_array($post_type->name, array('page')))
485
+ {
486
+ $this->form->input_hidden($this->settings['bpost_' . $post_type->name . '_hierarchy_display']);
487
+ $this->form->input_hidden($this->settings['bpost_' . $post_type->name . '_hierarchy_parent_first']);
488
+ $this->form->input_hidden($this->settings['bpost_' . $post_type->name . '_taxonomy_referer']);
489
+ }
490
+ else
491
  {
492
  $this->form->input_check($this->settings['bpost_' . $post_type->name . '_hierarchy_display'], sprintf(__('Show the hierarchy (specified below) leading to a %s in the breadcrumb trail.', 'breadcrumb-navxt'), $singular_name_lc), false, '', 'adminkit-enset-ctrl adminkit-enset');
493
  $this->form->input_check($this->settings['bpost_' . $post_type->name . '_hierarchy_parent_first'], sprintf(__('Use the parent of the %s as the primary hierarchy, falling back to the hierarchy selected below when the parent hierarchy is exhausted.', 'breadcrumb-navxt'), $singular_name_lc), false, '', 'adminkit-enset');
538
  <?php
539
  }
540
  do_action($this->unique_prefix . '_after_settings_tab_post', $this->settings);
541
+ //FIXME: Why don't we use the taxonomy loop for all taxonomies? We do it with posts now
542
  ?>
543
  </fieldset>
544
  <fieldset id="tax" class="bcn_options alttab">
568
  //Loop through all of the taxonomies in the array
569
  foreach($wp_taxonomies as $taxonomy)
570
  {
571
+ //Check for non-public taxonomies and if the taxonomy wasn't known when defaults were generated
572
+ if(!apply_filters('bcn_show_tax_private', $taxonomy->public, $taxonomy->name, null) || !isset($this->settings['Htax_' . $taxonomy->name . '_template']))
573
  {
574
  continue;
575
  }
class.bcn_breadcrumb.php CHANGED
@@ -21,7 +21,7 @@ require_once(dirname(__FILE__) . '/includes/block_direct_access.php');
21
  class bcn_breadcrumb
22
  {
23
  //Our member variables
24
- const version = '7.0.2';
25
  //The main text that will be shown
26
  protected $title;
27
  //The breadcrumb's template, used durring assembly
21
  class bcn_breadcrumb
22
  {
23
  //Our member variables
24
+ const version = '7.1.0';
25
  //The main text that will be shown
26
  protected $title;
27
  //The breadcrumb's template, used durring assembly
class.bcn_breadcrumb_trail.php CHANGED
@@ -21,7 +21,7 @@ require_once(dirname(__FILE__) . '/includes/block_direct_access.php');
21
  class bcn_breadcrumb_trail
22
  {
23
  //Our member variables
24
- const version = '7.0.2';
25
  //An array of breadcrumbs
26
  public $breadcrumbs = array();
27
  public $trail = array();
@@ -576,6 +576,11 @@ class bcn_breadcrumb_trail
576
  */
577
  protected function do_archive_by_term($term, $is_paged = false)
578
  {
 
 
 
 
 
579
  //Place the breadcrumb in the trail, uses the constructor to set the title, template, and type, get a pointer to it in return
580
  $breadcrumb = $this->add(new bcn_breadcrumb(
581
  $term->name,
@@ -1045,15 +1050,18 @@ class bcn_breadcrumb_trail
1045
  //For posts
1046
  else if(is_singular())
1047
  {
1048
- $this->do_post(get_post(), false, (get_query_var('page') > 1));
 
 
1049
  //If this is an attachment then we need to change the queried object to the parent post
1050
  if(is_attachment())
1051
  {
1052
- //Could use the $post global, but we can't really trust it
1053
- $post = get_post();
1054
- $type = get_post($post->post_parent); //TODO check for WP_Error?
 
 
1055
  }
1056
- $this->do_root($type->post_type, $this->opt['apost_' . $type->post_type . '_root'], is_paged(), false);
1057
  }
1058
  //For searches
1059
  else if(is_search())
@@ -1100,7 +1108,7 @@ class bcn_breadcrumb_trail
1100
  $this->do_archive_by_post_type($this->get_type_string_query_var(), false, is_paged(), true);
1101
  }
1102
  //For taxonomy based archives
1103
- else if(is_category() || is_tag() || is_tax())
1104
  {
1105
  $this->do_archive_by_term($type, is_paged());
1106
  $type_str = $this->type_archive($type);
@@ -1226,6 +1234,7 @@ class bcn_breadcrumb_trail
1226
  protected function display_loop($breadcrumbs, $linked, $reverse, $template, $outer_template, $separator)
1227
  {
1228
  $position = 1;
 
1229
  $last_position = count($breadcrumbs);
1230
  if($reverse)
1231
  {
21
  class bcn_breadcrumb_trail
22
  {
23
  //Our member variables
24
+ const version = '7.1.0';
25
  //An array of breadcrumbs
26
  public $breadcrumbs = array();
27
  public $trail = array();
576
  */
577
  protected function do_archive_by_term($term, $is_paged = false)
578
  {
579
+ if(!($term instanceof WP_Term))
580
+ {
581
+ _doing_it_wrong(__CLASS__ . '::' . __FUNCTION__, __('$term global is not of type WP_Term', 'breadcrumb-navxt'), '7.0.3');
582
+ return;
583
+ }
584
  //Place the breadcrumb in the trail, uses the constructor to set the title, template, and type, get a pointer to it in return
585
  $breadcrumb = $this->add(new bcn_breadcrumb(
586
  $term->name,
1050
  //For posts
1051
  else if(is_singular())
1052
  {
1053
+ //Could use the $post global, but we can't really trust it
1054
+ $type = get_post();
1055
+ $this->do_post($type, false, (get_query_var('page') > 1));
1056
  //If this is an attachment then we need to change the queried object to the parent post
1057
  if(is_attachment())
1058
  {
1059
+ $type = get_post($type->post_parent); //TODO check for WP_Error?
1060
+ }
1061
+ if($type instanceof WP_Post)
1062
+ {
1063
+ $this->do_root($type->post_type, $this->opt['apost_' . $type->post_type . '_root'], is_paged(), false);
1064
  }
 
1065
  }
1066
  //For searches
1067
  else if(is_search())
1108
  $this->do_archive_by_post_type($this->get_type_string_query_var(), false, is_paged(), true);
1109
  }
1110
  //For taxonomy based archives
1111
+ else if((is_category() || is_tag() || is_tax()) && $type instanceof WP_Term)
1112
  {
1113
  $this->do_archive_by_term($type, is_paged());
1114
  $type_str = $this->type_archive($type);
1234
  protected function display_loop($breadcrumbs, $linked, $reverse, $template, $outer_template, $separator)
1235
  {
1236
  $position = 1;
1237
+ $breadcrumbs = apply_filters('bcn_before_loop', $breadcrumbs);
1238
  $last_position = count($breadcrumbs);
1239
  if($reverse)
1240
  {
class.bcn_network_admin.php CHANGED
@@ -29,7 +29,7 @@ use mtekk\adminKit\{adminKit, form, message, setting};
29
  */
30
  class bcn_network_admin extends bcn_admin
31
  {
32
- const version = '7.0.2';
33
  protected $full_name = 'Breadcrumb NavXT Network Settings';
34
  protected $access_level = 'manage_network_options';
35
  /**
@@ -109,7 +109,7 @@ class bcn_network_admin extends bcn_admin
109
  * @param mixed $newvalue The new value to set the option to
110
  *
111
  */
112
- function update_option($option, $newvalue)
113
  {
114
  return update_site_option($option, $newvalue);
115
  }
29
  */
30
  class bcn_network_admin extends bcn_admin
31
  {
32
+ const version = '7.1.0';
33
  protected $full_name = 'Breadcrumb NavXT Network Settings';
34
  protected $access_level = 'manage_network_options';
35
  /**
109
  * @param mixed $newvalue The new value to set the option to
110
  *
111
  */
112
+ function update_option($option, $newvalue, $autoload = null)
113
  {
114
  return update_site_option($option, $newvalue);
115
  }
class.bcn_widget.php CHANGED
@@ -19,7 +19,7 @@
19
  require_once(dirname(__FILE__) . '/includes/block_direct_access.php');
20
  class bcn_widget extends WP_Widget
21
  {
22
- const version = '7.0.2';
23
  protected $allowed_html = array();
24
  protected $defaults = array('title' => '', 'pretext' => '', 'type' => 'microdata', 'linked' => true, 'reverse' => false, 'front' => false, 'force' => false);
25
  //Default constructor
19
  require_once(dirname(__FILE__) . '/includes/block_direct_access.php');
20
  class bcn_widget extends WP_Widget
21
  {
22
+ const version = '7.1.0';
23
  protected $allowed_html = array();
24
  protected $defaults = array('title' => '', 'pretext' => '', 'type' => 'microdata', 'linked' => true, 'reverse' => false, 'front' => false, 'force' => false);
25
  //Default constructor
includes/adminKit/class-mtekk_adminkit.php CHANGED
@@ -67,7 +67,7 @@ if(!class_exists('form'))
67
  }
68
  abstract class adminKit
69
  {
70
- const version = '3.0.2';
71
  protected $full_name;
72
  protected $short_name;
73
  protected $plugin_basename;
@@ -187,17 +187,17 @@ abstract class adminKit
187
  //Run the reset function on init if reset form has been submitted
188
  $this->opts_reset();
189
  }
190
- //Admin Options export hook
191
- else if(isset($_POST[$this->unique_prefix . '_admin_export']))
192
  {
193
  //Run the export function on init if export form has been submitted
194
- $this->opts_export();
195
  }
196
- //Admin Options import hook
197
- else if(isset($_FILES[$this->unique_prefix . '_admin_import_file']) && !empty($_FILES[$this->unique_prefix . '_admin_import_file']['name']))
198
  {
199
  //Run the import function on init if import form has been submitted
200
- $this->opts_import();
201
  }
202
  //Admin Options rollback hook
203
  else if(isset($_GET[$this->unique_prefix . '_admin_undo']))
@@ -330,9 +330,9 @@ abstract class adminKit
330
  {
331
  //Add the options, we only store differences from defaults now, so start with blank array
332
  $this->add_option($this->unique_prefix . '_options', array());
333
- $this->add_option($this->unique_prefix . '_options_bk', array(), '', 'no');
334
  //Add the version, no need to autoload the db version
335
- $this->update_option($this->unique_prefix . '_version', $this::version, 'no');
336
  }
337
  else
338
  {
@@ -343,9 +343,9 @@ abstract class adminKit
343
  //Run the settings update script
344
  $this->opts_upgrade($opts, $db_version);
345
  //Always have to update the version
346
- $this->update_option($this->unique_prefix . '_version', $this::version);
347
  //Store the options
348
- $this->update_option($this->unique_prefix . '_options', $this->opt);
349
  }
350
  }
351
  }
@@ -443,7 +443,7 @@ abstract class adminKit
443
  function opts_backup()
444
  {
445
  //Set the backup options in the DB to the current options
446
- $this->update_option($this->unique_prefix . '_options_bk', $this->get_option($this->unique_prefix . '_options'));
447
  }
448
  /**
449
  * The new, simpler settings update loop, handles the new settings array and replaces the old opts_update_loop
@@ -553,6 +553,16 @@ abstract class adminKit
553
  {
554
  $this->settings[$key]->set_value($this->settings[$key]->validate($value));
555
  }
 
 
 
 
 
 
 
 
 
 
556
  }
557
  }
558
  /**
@@ -624,7 +634,7 @@ abstract class adminKit
624
  $this->opt = adminKit::parse_args($this->get_option($this->unique_prefix . '_options'), $this->opt);
625
  $this->opt = apply_filters($this->unique_prefix . '_opts_update_prebk', $this->opt);
626
  //Update our backup options
627
- $this->update_option($this->unique_prefix . '_options_bk', $this->opt);
628
  $opt_prev = $this->opt;
629
  //Grab our incomming array (the data is dirty)
630
  $input = $_POST[$this->unique_prefix . '_options'];
@@ -635,7 +645,7 @@ abstract class adminKit
635
  //Convert to opts array for saving
636
  $this->opt = adminKit::settings_to_opts($new_settings);
637
  //Commit the option changes
638
- $updated = $this->update_option($this->unique_prefix . '_options', $this->opt);
639
  //Check if known settings match attempted save
640
  if($updated && count(array_diff_key($input, $this->settings)) == 0)
641
  {
@@ -670,14 +680,90 @@ abstract class adminKit
670
  */
671
  function settings_export()
672
  {
673
- //TODO: implement this, see #239
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
674
  }
675
  /**
676
  * Imports JSON settings into database
677
  */
678
- function settings_inport()
679
  {
680
- //TODO: implement this, see #239
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
681
  }
682
  /**
683
  * Exports a XML options document
@@ -689,7 +775,7 @@ abstract class adminKit
689
  //Update our internal settings
690
  $this->opt = $this->get_option($this->unique_prefix . '_options');
691
  //Create a DOM document
692
- $dom = new DOMDocument('1.0', 'UTF-8');
693
  //Adds in newlines and tabs to the output
694
  $dom->formatOutput = true;
695
  //We're not using a DTD therefore we need to specify it as a standalone document
@@ -737,25 +823,25 @@ abstract class adminKit
737
  function opts_import()
738
  {
739
  //Our quick and dirty error supressor
740
- function error($errno, $errstr, $eerfile, $errline)
741
  {
742
  return true;
743
- }
744
  //Do a nonce check, prevent malicious link/form problems
745
  check_admin_referer($this->unique_prefix . '_admin_import_export');
746
  //Set the backup options in the DB to the current options
747
  $this->opts_backup();
748
  //Create a DOM document
749
- $dom = new DOMDocument('1.0', 'UTF-8');
750
  //We want to catch errors ourselves
751
- set_error_handler('error');
752
  //Load the user uploaded file, handle failure gracefully
753
  if(is_uploaded_file($_FILES[$this->unique_prefix . '_admin_import_file']['tmp_name']) && $dom->load($_FILES[$this->unique_prefix . '_admin_import_file']['tmp_name']))
754
  {
755
  $opts_temp = array();
756
  $version = '';
757
  //Have to use an xpath query otherwise we run into problems
758
- $xpath = new DOMXPath($dom);
759
  $option_sets = $xpath->query('plugin');
760
  //Loop through all of the xpath query results
761
  foreach($option_sets as $options)
@@ -776,7 +862,7 @@ abstract class adminKit
776
  //Make sure we safely import and upgrade settings if needed
777
  $this->opts_upgrade($opts_temp, $version);
778
  //Commit the loaded options to the database
779
- $this->update_option($this->unique_prefix . '_options', $this->opt);
780
  //Everything was successful, let the user know
781
  $this->messages[] = new message(esc_html__('Settings successfully imported from the uploaded file.', $this->identifier)
782
  . $this->admin_anchor('undo', __('Undo the options import.', $this->identifier), __('Undo', $this->identifier)), 'success');
@@ -801,7 +887,7 @@ abstract class adminKit
801
  //Set the backup options in the DB to the current options
802
  $this->opts_backup();
803
  //Load in the hard coded default option values
804
- $this->update_option($this->unique_prefix . '_options', adminKit::settings_to_opts($this->settings));
805
  //Reset successful, let the user know
806
  $this->messages[] = new message(esc_html__('Settings successfully reset to the default values.', $this->identifier)
807
  . $this->admin_anchor('undo', __('Undo the options reset.', $this->identifier), __('Undo', $this->identifier)), 'success');
@@ -817,9 +903,9 @@ abstract class adminKit
817
  //Set the options array to the current options
818
  $opt = $this->get_option($this->unique_prefix . '_options');
819
  //Set the options in the DB to the backup options
820
- $this->update_option($this->unique_prefix . '_options', $this->get_option($this->unique_prefix . '_options_bk'));
821
  //Set the backup options to the undone options
822
- $this->update_option($this->unique_prefix . '_options_bk', $opt);
823
  //Send the success/undo message
824
  $this->messages[] = new message(esc_html__('Settings successfully undid the last operation.', $this->identifier)
825
  . $this->admin_anchor('undo', __('Undo the last undo operation.', $this->identifier), __('Undo', $this->identifier)), 'success');
@@ -855,9 +941,9 @@ abstract class adminKit
855
  //Feed the just read options into the upgrade function
856
  $this->opts_upgrade($opts, $this->get_option($this->unique_prefix . '_version'));
857
  //Always have to update the version
858
- $this->update_option($this->unique_prefix . '_version', $this::version);
859
  //Store the options
860
- $this->update_option($this->unique_prefix . '_options', $this->opt);
861
  //Send the success message
862
  $this->messages[] = new message(esc_html__('Settings successfully migrated.', $this->identifier), 'success');
863
  }
@@ -982,16 +1068,16 @@ abstract class adminKit
982
  $form .= wp_nonce_field($this->unique_prefix . '_admin_import_export', '_wpnonce', true, false);
983
  $form .= sprintf('<fieldset id="import_export" class="%s_options">', esc_attr($this->unique_prefix));
984
  $form .= '<legend class="screen-reader-text">' . esc_html__( 'Import settings', $this->identifier ) . '</legend>';
985
- $form .= '<p>' . esc_html__('Import settings from a XML file, export the current settings to a XML file, or reset to the default settings.', $this->identifier) . '</p>';
986
  $form .= '<table class="form-table"><tr valign="top"><th scope="row">';
987
  $form .= sprintf('<label for="%s_admin_import_file">', esc_attr($this->unique_prefix));
988
  $form .= esc_html__('Settings File', $this->identifier);
989
  $form .= '</label></th><td>';
990
  $form .= sprintf('<input type="file" name="%1$s_admin_import_file" id="%1$s_admin_import_file" size="32" /><p class="description">', esc_attr($this->unique_prefix));
991
- $form .= esc_html__('Select a XML settings file to upload and import settings from.', 'breadcrumb_navxt');
992
  $form .= '</p></td></tr></table><p class="submit">';
993
- $form .= sprintf('<input type="submit" class="button" name="%1$s_admin_import" value="%2$s"/>', $this->unique_prefix, esc_attr__('Import', $this->identifier));
994
- $form .= sprintf('<input type="submit" class="button" name="%1$s_admin_export" value="%2$s"/>', $this->unique_prefix, esc_attr__('Export', $this->identifier));
995
  $form .= sprintf('<input type="submit" class="button" name="%1$s_admin_reset" value="%2$s"/>', $this->unique_prefix, esc_attr__('Reset', $this->identifier));
996
  $form .= '</p></fieldset></form></div>';
997
  return $form;
@@ -1314,9 +1400,9 @@ abstract class adminKit
1314
  * @param string $option The name of the option to update
1315
  * @param mixed $newvalue The new value to set the option to
1316
  */
1317
- function update_option($option, $newvalue)
1318
  {
1319
- return update_option($option, $newvalue);
1320
  }
1321
  /**
1322
  * A local pass through for add_option so that we can hook in and pick the correct method if needed
67
  }
68
  abstract class adminKit
69
  {
70
+ const version = '3.1.0';
71
  protected $full_name;
72
  protected $short_name;
73
  protected $plugin_basename;
187
  //Run the reset function on init if reset form has been submitted
188
  $this->opts_reset();
189
  }
190
+ //Admin Settings export hook
191
+ else if(isset($_POST[$this->unique_prefix . '_admin_settings_export']))
192
  {
193
  //Run the export function on init if export form has been submitted
194
+ $this->settings_export();
195
  }
196
+ //Admin Settings import hook
197
+ else if(isset($_POST[$this->unique_prefix . '_admin_settings_import']) && isset($_FILES[$this->unique_prefix . '_admin_import_file']) && !empty($_FILES[$this->unique_prefix . '_admin_import_file']['name']))
198
  {
199
  //Run the import function on init if import form has been submitted
200
+ $this->settings_import();
201
  }
202
  //Admin Options rollback hook
203
  else if(isset($_GET[$this->unique_prefix . '_admin_undo']))
330
  {
331
  //Add the options, we only store differences from defaults now, so start with blank array
332
  $this->add_option($this->unique_prefix . '_options', array());
333
+ $this->add_option($this->unique_prefix . '_options_bk', array(), '', false);
334
  //Add the version, no need to autoload the db version
335
+ $this->update_option($this->unique_prefix . '_version', $this::version, false);
336
  }
337
  else
338
  {
343
  //Run the settings update script
344
  $this->opts_upgrade($opts, $db_version);
345
  //Always have to update the version
346
+ $this->update_option($this->unique_prefix . '_version', $this::version, false);
347
  //Store the options
348
+ $this->update_option($this->unique_prefix . '_options', $this->opt, true);
349
  }
350
  }
351
  }
443
  function opts_backup()
444
  {
445
  //Set the backup options in the DB to the current options
446
+ $this->update_option($this->unique_prefix . '_options_bk', $this->get_option($this->unique_prefix . '_options'), false);
447
  }
448
  /**
449
  * The new, simpler settings update loop, handles the new settings array and replaces the old opts_update_loop
553
  {
554
  $this->settings[$key]->set_value($this->settings[$key]->validate($value));
555
  }
556
+ else if(isset($this->settings[$key]) && is_array($this->settings[$key]) && is_array($value))
557
+ {
558
+ foreach($value as $subkey => $subvalue)
559
+ {
560
+ if(isset($this->settings[$key][$subkey]) && $this->settings[$key][$subkey]instanceof setting)
561
+ {
562
+ $this->settings[$key][$subkey]->set_value($this->settings[$key][$subkey]->validate($subvalue));
563
+ }
564
+ }
565
+ }
566
  }
567
  }
568
  /**
634
  $this->opt = adminKit::parse_args($this->get_option($this->unique_prefix . '_options'), $this->opt);
635
  $this->opt = apply_filters($this->unique_prefix . '_opts_update_prebk', $this->opt);
636
  //Update our backup options
637
+ $this->update_option($this->unique_prefix . '_options_bk', $this->opt, false);
638
  $opt_prev = $this->opt;
639
  //Grab our incomming array (the data is dirty)
640
  $input = $_POST[$this->unique_prefix . '_options'];
645
  //Convert to opts array for saving
646
  $this->opt = adminKit::settings_to_opts($new_settings);
647
  //Commit the option changes
648
+ $updated = $this->update_option($this->unique_prefix . '_options', $this->opt, true);
649
  //Check if known settings match attempted save
650
  if($updated && count(array_diff_key($input, $this->settings)) == 0)
651
  {
680
  */
681
  function settings_export()
682
  {
683
+ //Do a nonce check, prevent malicious link/form problems
684
+ check_admin_referer($this->unique_prefix . '_admin_import_export');
685
+ //Must clone the defaults since PHP normally shallow copies
686
+ $default_settings = array_map('mtekk\adminKit\adminKit::setting_cloner', $this->settings);
687
+ //Get the database options, and load
688
+ //FIXME: This changes once we save settings to the db instead of opts
689
+ $this->load_opts_into_settings($this->get_option($this->unique_prefix . '_options'));
690
+ //Get the unique settings
691
+ $export_settings = apply_filters($this->unique_prefix . '_settings_to_export', array_udiff_assoc($this->settings, $default_settings, array($this, 'setting_equal_check')));
692
+ //Change our headder to application/json for direct save
693
+ header('Cache-Control: public');
694
+ //The next two will cause good browsers to download instead of displaying the file
695
+ header('Content-Description: File Transfer');
696
+ header('Content-disposition: attachemnt; filename=' . $this->unique_prefix . '_settings.json');
697
+ header('Content-Type: application/json');
698
+ //JSON encode our settings array
699
+ $output = json_encode(
700
+ (object)array(
701
+ 'plugin' => $this->short_name,
702
+ 'version' => $this::version,
703
+ 'settings' => $export_settings)
704
+ , JSON_UNESCAPED_SLASHES, 32);
705
+ //Let the browser know how long the file is
706
+ header('Content-Length: ' . strlen($output)); // binary length
707
+ //Output the file
708
+ echo $output;
709
+ //Prevent WordPress from continuing on
710
+ die();
711
  }
712
  /**
713
  * Imports JSON settings into database
714
  */
715
+ function settings_import()
716
  {
717
+ //Do a nonce check, prevent malicious link/form problems
718
+ check_admin_referer($this->unique_prefix . '_admin_import_export');
719
+ //Set the backup options in the DB to the current options
720
+ $this->opts_backup();
721
+ //Load the user uploaded file, handle failure gracefully
722
+ if(is_uploaded_file($_FILES[$this->unique_prefix . '_admin_import_file']['tmp_name']))
723
+ {
724
+ //Grab the json settings from the temp file, treat as associative array so we can just throw the settings subfield at the update loop
725
+ $settings_upload = json_decode(file_get_contents($_FILES[$this->unique_prefix . '_admin_import_file']['tmp_name']), true);
726
+ //Only continue if we have a JSON object that is for this plugin (the the WP rest_is_object() function is handy here as the REST API passes JSON)
727
+ if(rest_is_object($settings_upload) && isset($settings_upload['plugin']) && $settings_upload['plugin'] === $this->short_name)
728
+ {
729
+ //FIXME: Much of the innards of this are the same as ops_update, probably an opportunity for refactoring simplification
730
+ //Must clone the defaults since PHP normally shallow copies
731
+ $default_settings = array_map('mtekk\adminKit\adminKit::setting_cloner', $this->settings);
732
+ //Act as if the JSON file was just a bunch of POST entries for a settings save
733
+ $this->settings_update_loop($this->settings, $settings_upload['settings']);
734
+ $new_settings = apply_filters($this->unique_prefix . '_opts_update_to_save', array_udiff_assoc($this->settings, $default_settings, array($this, 'setting_equal_check')));
735
+ //FIXME: Eventually we'll save the object array, but not today
736
+ //Convert to opts array for saving
737
+ $this->opt = adminKit::settings_to_opts($new_settings);
738
+ //Run opts through update script
739
+ //Make sure we safely import and upgrade settings if needed
740
+ $this->opts_upgrade($this->opt, $settings_upload['version']);
741
+ //Commit the option changes
742
+ $updated = $this->update_option($this->unique_prefix . '_options', $this->opt, true);
743
+ //Check if known settings match attempted save
744
+ if($updated && count(array_diff_key($settings_upload['settings'], $this->settings)) == 0)
745
+ {
746
+ //Let the user know everything went ok
747
+ $this->messages[] = new message(esc_html__('Settings successfully imported from the uploaded file.', $this->identifier)
748
+ . $this->admin_anchor('undo', __('Undo the options import.', $this->identifier), __('Undo', $this->identifier)), 'success');
749
+ }
750
+ else
751
+ {
752
+ $this->messages[] = new message(esc_html__('No settings were imported. Settings from uploaded file matched existing settings.', $this->identifier), 'info');
753
+ }
754
+ //Output any messages that there may be
755
+ add_action('admin_notices', array($this, 'messages'));
756
+ //And return as we're successful
757
+ return;
758
+ }
759
+ //If it wasn't JSON, try XML
760
+ else
761
+ {
762
+ return $this->opts_import();
763
+ }
764
+ }
765
+ //Throw an error since we could not load the file for various reasons
766
+ $this->messages[] = new message(esc_html__('Importing settings from file failed.', $this->identifier), 'error');
767
  }
768
  /**
769
  * Exports a XML options document
775
  //Update our internal settings
776
  $this->opt = $this->get_option($this->unique_prefix . '_options');
777
  //Create a DOM document
778
+ $dom = new \DOMDocument('1.0', 'UTF-8');
779
  //Adds in newlines and tabs to the output
780
  $dom->formatOutput = true;
781
  //We're not using a DTD therefore we need to specify it as a standalone document
823
  function opts_import()
824
  {
825
  //Our quick and dirty error supressor
826
+ $error_handler = function($errno, $errstr, $eerfile, $errline, $errcontext)
827
  {
828
  return true;
829
+ };
830
  //Do a nonce check, prevent malicious link/form problems
831
  check_admin_referer($this->unique_prefix . '_admin_import_export');
832
  //Set the backup options in the DB to the current options
833
  $this->opts_backup();
834
  //Create a DOM document
835
+ $dom = new \DOMDocument('1.0', 'UTF-8');
836
  //We want to catch errors ourselves
837
+ set_error_handler($error_handler);
838
  //Load the user uploaded file, handle failure gracefully
839
  if(is_uploaded_file($_FILES[$this->unique_prefix . '_admin_import_file']['tmp_name']) && $dom->load($_FILES[$this->unique_prefix . '_admin_import_file']['tmp_name']))
840
  {
841
  $opts_temp = array();
842
  $version = '';
843
  //Have to use an xpath query otherwise we run into problems
844
+ $xpath = new \DOMXPath($dom);
845
  $option_sets = $xpath->query('plugin');
846
  //Loop through all of the xpath query results
847
  foreach($option_sets as $options)
862
  //Make sure we safely import and upgrade settings if needed
863
  $this->opts_upgrade($opts_temp, $version);
864
  //Commit the loaded options to the database
865
+ $this->update_option($this->unique_prefix . '_options', $this->opt, true);
866
  //Everything was successful, let the user know
867
  $this->messages[] = new message(esc_html__('Settings successfully imported from the uploaded file.', $this->identifier)
868
  . $this->admin_anchor('undo', __('Undo the options import.', $this->identifier), __('Undo', $this->identifier)), 'success');
887
  //Set the backup options in the DB to the current options
888
  $this->opts_backup();
889
  //Load in the hard coded default option values
890
+ $this->update_option($this->unique_prefix . '_options', adminKit::settings_to_opts($this->settings), true);
891
  //Reset successful, let the user know
892
  $this->messages[] = new message(esc_html__('Settings successfully reset to the default values.', $this->identifier)
893
  . $this->admin_anchor('undo', __('Undo the options reset.', $this->identifier), __('Undo', $this->identifier)), 'success');
903
  //Set the options array to the current options
904
  $opt = $this->get_option($this->unique_prefix . '_options');
905
  //Set the options in the DB to the backup options
906
+ $this->update_option($this->unique_prefix . '_options', $this->get_option($this->unique_prefix . '_options_bk'), true);
907
  //Set the backup options to the undone options
908
+ $this->update_option($this->unique_prefix . '_options_bk', $opt, false);
909
  //Send the success/undo message
910
  $this->messages[] = new message(esc_html__('Settings successfully undid the last operation.', $this->identifier)
911
  . $this->admin_anchor('undo', __('Undo the last undo operation.', $this->identifier), __('Undo', $this->identifier)), 'success');
941
  //Feed the just read options into the upgrade function
942
  $this->opts_upgrade($opts, $this->get_option($this->unique_prefix . '_version'));
943
  //Always have to update the version
944
+ $this->update_option($this->unique_prefix . '_version', $this::version, false);
945
  //Store the options
946
+ $this->update_option($this->unique_prefix . '_options', $this->opt, true);
947
  //Send the success message
948
  $this->messages[] = new message(esc_html__('Settings successfully migrated.', $this->identifier), 'success');
949
  }
1068
  $form .= wp_nonce_field($this->unique_prefix . '_admin_import_export', '_wpnonce', true, false);
1069
  $form .= sprintf('<fieldset id="import_export" class="%s_options">', esc_attr($this->unique_prefix));
1070
  $form .= '<legend class="screen-reader-text">' . esc_html__( 'Import settings', $this->identifier ) . '</legend>';
1071
+ $form .= '<p>' . esc_html__('Import settings from a JSON or XML file, export the current settings to a JSON file, or reset to the default settings.', $this->identifier) . '</p>';
1072
  $form .= '<table class="form-table"><tr valign="top"><th scope="row">';
1073
  $form .= sprintf('<label for="%s_admin_import_file">', esc_attr($this->unique_prefix));
1074
  $form .= esc_html__('Settings File', $this->identifier);
1075
  $form .= '</label></th><td>';
1076
  $form .= sprintf('<input type="file" name="%1$s_admin_import_file" id="%1$s_admin_import_file" size="32" /><p class="description">', esc_attr($this->unique_prefix));
1077
+ $form .= esc_html__('Select a JSON or XML settings file to upload and import settings from.', 'breadcrumb_navxt');
1078
  $form .= '</p></td></tr></table><p class="submit">';
1079
+ $form .= sprintf('<input type="submit" class="button" name="%1$s_admin_settings_import" value="%2$s"/>', $this->unique_prefix, esc_attr__('Import', $this->identifier));
1080
+ $form .= sprintf('<input type="submit" class="button" name="%1$s_admin_settings_export" value="%2$s"/>', $this->unique_prefix, esc_attr__('Export', $this->identifier));
1081
  $form .= sprintf('<input type="submit" class="button" name="%1$s_admin_reset" value="%2$s"/>', $this->unique_prefix, esc_attr__('Reset', $this->identifier));
1082
  $form .= '</p></fieldset></form></div>';
1083
  return $form;
1400
  * @param string $option The name of the option to update
1401
  * @param mixed $newvalue The new value to set the option to
1402
  */
1403
+ function update_option($option, $newvalue, $autoload = null)
1404
  {
1405
+ return update_option($option, $newvalue, $autoload);
1406
  }
1407
  /**
1408
  * A local pass through for add_option so that we can hook in and pick the correct method if needed
includes/adminKit/class-mtekk_adminkit_form.php CHANGED
@@ -274,7 +274,7 @@ class form
274
  printf('<input type="checkbox" name="%1$s" id="%2$s" value="%3$s" class="%4$s" %5$s %6$s/>',
275
  esc_attr($opt_name),
276
  esc_attr($opt_id),
277
- esc_attr($option->get_value()),
278
  esc_attr($class),
279
  disabled($disable, true, false),
280
  checked($option->get_value(), true, false));
274
  printf('<input type="checkbox" name="%1$s" id="%2$s" value="%3$s" class="%4$s" %5$s %6$s/>',
275
  esc_attr($opt_name),
276
  esc_attr($opt_id),
277
+ 1,
278
  esc_attr($class),
279
  disabled($disable, true, false),
280
  checked($option->get_value(), true, false));
includes/adminKit/setting/class-mtekk_adminkit_setting_base.php CHANGED
@@ -23,7 +23,7 @@ if(!interface_exists('mtekk_adminKit_setting'))
23
  {
24
  require_once( __DIR__ . '/interface-mtekk_adminkit_setting.php');
25
  }
26
- abstract class setting_base implements setting
27
  {
28
  const version = '1.0.0';
29
  protected $name = '';
@@ -63,6 +63,10 @@ abstract class setting_base implements setting
63
  {
64
  $this->allow_empty = $allow_empty;
65
  }
 
 
 
 
66
  /**
67
  * Basic updateFromFormInput method
68
  *
23
  {
24
  require_once( __DIR__ . '/interface-mtekk_adminkit_setting.php');
25
  }
26
+ abstract class setting_base implements setting,\JsonSerializable
27
  {
28
  const version = '1.0.0';
29
  protected $name = '';
63
  {
64
  $this->allow_empty = $allow_empty;
65
  }
66
+ public function jsonSerialize()
67
+ {
68
+ return $this->value;
69
+ }
70
  /**
71
  * Basic updateFromFormInput method
72
  *
includes/adminKit/setting/class-mtekk_adminkit_setting_bool.php CHANGED
@@ -61,6 +61,14 @@ class setting_bool extends setting_base
61
  */
62
  public function maybe_update_from_form_input($input)
63
  {
64
- $this->set_value($this->validate(isset($input[$this->get_opt_name()])));
 
 
 
 
 
 
 
 
65
  }
66
  }
61
  */
62
  public function maybe_update_from_form_input($input)
63
  {
64
+ if(isset($input[$this->get_opt_name()]) && ($input[$this->get_opt_name()] === true || $input[$this->get_opt_name()] === '1'))
65
+ {
66
+ $newval = true;
67
+ }
68
+ else
69
+ {
70
+ $newval = false;
71
+ }
72
+ $this->set_value($this->validate($newval));
73
  }
74
  }
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=FD5XE
4
  Tags: breadcrumb, breadcrumbs, trail, navigation, menu, widget
5
  Requires at least: 4.9
6
  Tested up to: 6.0
7
- Stable tag: 7.0.2
8
  Requires PHP: 7.0
9
  License: GPLv2 or later
10
  Adds breadcrumb navigation showing the visitor's path to their current location.
@@ -49,6 +49,16 @@ Please visit [Breadcrumb NavXT's Documentation](http://mtekk.us/code/breadcrumb-
49
  6. A screenshot of the Settings Import/Export/Reset form under the Help menu
50
 
51
  == Changelog ==
 
 
 
 
 
 
 
 
 
 
52
 
53
  = 7.0.2 =
54
  Release date: January, 4th 2022
@@ -191,8 +201,5 @@ Release date: December, 26th 2017
191
  * Bug fix: Removed use of `create_function` in registering the widget as it was deprecated in PHP 7.2.
192
 
193
  == Upgrade Notice ==
194
- = 6.3.0 =
195
- This version requires PHP5.5 or newer. This version introduces a Gutenberg Breadcrumb Trail block.
196
-
197
- = 6.0.0 =
198
- This version requires PHP5.3 or newer. This version introduces three new filters and deprecates a filter.
4
  Tags: breadcrumb, breadcrumbs, trail, navigation, menu, widget
5
  Requires at least: 4.9
6
  Tested up to: 6.0
7
+ Stable tag: 7.1.0
8
  Requires PHP: 7.0
9
  License: GPLv2 or later
10
  Adds breadcrumb navigation showing the visitor's path to their current location.
49
  6. A screenshot of the Settings Import/Export/Reset form under the Help menu
50
 
51
  == Changelog ==
52
+ = 7.1.0 =
53
+ Release date: July, 17th 2022
54
+
55
+ * Behavior change: BCN_SETTINGS_FAVOR_* constant behavior changed to take advantage of settings changes in 7.0.
56
+ * Behavior change: Settings page now warns if it detected CPTs that were added too late.
57
+ * New feature: Added bcn_before_loop filter which runs at the top of bcn_breadcrumb_trail::display_loop().
58
+ * New feature: Added JSON formatted settings import and export (successor to the old XML settings import/export format).
59
+ * Bug fix: Fixed PHP error caused by term archives that don’t know the active term.
60
+ * Bug fix: Fixed PHP error caused by introduction of namespacing in the legacy XML settings importer and exporter.
61
+ * Bug fix: Explicitly set option autoload values, avoiding inappropriate default autoload values being used.
62
 
63
  = 7.0.2 =
64
  Release date: January, 4th 2022
201
  * Bug fix: Removed use of `create_function` in registering the widget as it was deprecated in PHP 7.2.
202
 
203
  == Upgrade Notice ==
204
+ = 7.1.0 =
205
+ This version requires PHP7.0 or newer. This version introduces a new JSON settings import/export feature.