diff --git a/inc/theme-navwalker.php b/inc/theme-navwalker.php index 938737b..5e786b8 100644 --- a/inc/theme-navwalker.php +++ b/inc/theme-navwalker.php @@ -32,33 +32,16 @@ function attributes_filter($var) add_filter('nav_menu_css_class', 'attributes_filter', 100, 1); add_filter('nav_menu_item_id', 'attributes_filter', 100, 1); add_filter('page_css_class', 'attributes_filter', 100, 1); -if (!class_exists('WP_Bootstrap_Navwalker')) : + +if (!class_exists('WP_Bootstrap_Navwalker')) { /** * WP_Bootstrap_Navwalker class. + * + * @extends Walker_Nav_Menu */ class WP_Bootstrap_Navwalker extends Walker_Nav_Menu { - /** - * Whether the items_wrap contains schema microdata or not. - * - * @since 4.2.0 - * @var boolean - */ - private $has_schema = false; - - /** - * Ensure the items_wrap argument contains microdata. - * - * @since 4.2.0 - */ - public function __construct() - { - if (!has_filter('wp_nav_menu_args', array($this, 'add_schema_to_navbar_ul'))) { - add_filter('wp_nav_menu_args', array($this, 'add_schema_to_navbar_ul')); - } - } - /** * Starts the list before the elements are added. * @@ -66,11 +49,11 @@ if (!class_exists('WP_Bootstrap_Navwalker')) : * * @see Walker_Nav_Menu::start_lvl() * - * @param string $output Used to append additional content (passed by reference). - * @param int $depth Depth of menu item. Used for padding. - * @param WP_Nav_Menu_Args $args An object of wp_nav_menu() arguments. + * @param string $output Used to append additional content (passed by reference). + * @param int $depth Depth of menu item. Used for padding. + * @param stdClass $args An object of wp_nav_menu() arguments. */ - public function start_lvl(&$output, $depth = 0, $args = null) + public function start_lvl(&$output, $depth = 0, $args = array()) { if (isset($args->item_spacing) && 'discard' === $args->item_spacing) { $t = ''; @@ -95,12 +78,12 @@ if (!class_exists('WP_Bootstrap_Navwalker')) : $class_names = $class_names ? ' class="' . esc_attr($class_names) . '"' : ''; /* - * The `.dropdown-menu` container needs to have a labelledby - * attribute which points to it's trigger link. - * - * Form a string for the labelledby attribute from the the latest - * link with an id that was added to the $output. - */ + * The `.dropdown-menu` container needs to have a labelledby + * attribute which points to it's trigger link. + * + * Form a string for the labelledby attribute from the the latest + * link with an id that was added to the $output. + */ $labelledby = ''; // Find all links with an id in the output. preg_match_all('/(/im', $output, $matches); @@ -109,7 +92,7 @@ if (!class_exists('WP_Bootstrap_Navwalker')) : // Build a string to use as aria-labelledby. $labelledby = 'aria-labelledby="' . esc_attr(end($matches[2])) . '"'; } - $output .= "{$n}{$indent}{$n}"; + $output .= "{$n}{$indent}{$n}"; } /** @@ -120,13 +103,13 @@ if (!class_exists('WP_Bootstrap_Navwalker')) : * * @see Walker_Nav_Menu::start_el() * - * @param string $output Used to append additional content (passed by reference). - * @param WP_Nav_Menu_Item $item Menu item data object. - * @param int $depth Depth of menu item. Used for padding. - * @param WP_Nav_Menu_Args $args An object of wp_nav_menu() arguments. - * @param int $id Current item ID. + * @param string $output Used to append additional content (passed by reference). + * @param WP_Post $item Menu item data object. + * @param int $depth Depth of menu item. Used for padding. + * @param stdClass $args An object of wp_nav_menu() arguments. + * @param int $id Current item ID. */ - public function start_el(&$output, $item, $depth = 0, $args = null, $id = 0) + public function start_el(&$output, $item, $depth = 0, $args = array(), $id = 0) { if (isset($args->item_spacing) && 'discard' === $args->item_spacing) { $t = ''; @@ -137,35 +120,22 @@ if (!class_exists('WP_Bootstrap_Navwalker')) : } $indent = ($depth) ? str_repeat($t, $depth) : ''; - if (false !== strpos($args->items_wrap, 'itemscope') && false === $this->has_schema) { - $this->has_schema = true; - $args->link_before = '' . $args->link_before; - $args->link_after .= ''; - } - $classes = empty($item->classes) ? array() : (array) $item->classes; - // Updating the CSS classes of a menu item in the WordPress Customizer preview results in all classes defined - // in that particular input box to come in as one big class string. - $split_on_spaces = function ($class) { - return preg_split('/\s+/', $class); - }; - $classes = $this->flatten(array_map($split_on_spaces, $classes)); - /* - * Initialize some holder variables to store specially handled item - * wrappers and icons. - */ + * Initialize some holder variables to store specially handled item + * wrappers and icons. + */ $linkmod_classes = array(); - $icon_classes = array(); + $icon_classes = array(); /* - * Get an updated $classes array without linkmod or icon classes. - * - * NOTE: linkmod and icon class arrays are passed by reference and - * are maybe modified before being used later in this function. - */ - $classes = $this->separate_linkmods_and_icons_from_classes($classes, $linkmod_classes, $icon_classes, $depth); + * Get an updated $classes array without linkmod or icon classes. + * + * NOTE: linkmod and icon class arrays are passed by reference and + * are maybe modified before being used later in this function. + */ + $classes = self::separate_linkmods_and_icons_from_classes($classes, $linkmod_classes, $icon_classes, $depth); // Join any icon classes plucked from $classes into a string. $icon_class_string = join(' ', $icon_classes); @@ -173,18 +143,16 @@ if (!class_exists('WP_Bootstrap_Navwalker')) : /** * Filters the arguments for a single nav menu item. * - * @since WP 4.4.0 + * WP 4.4.0 * - * @param WP_Nav_Menu_Args $args An object of wp_nav_menu() arguments. - * @param WP_Nav_Menu_Item $item Menu item data object. - * @param int $depth Depth of menu item. Used for padding. - * - * @var WP_Nav_Menu_Args + * @param stdClass $args An object of wp_nav_menu() arguments. + * @param WP_Post $item Menu item data object. + * @param int $depth Depth of menu item. Used for padding. */ $args = apply_filters('nav_menu_item_args', $args, $item, $depth); // Add .dropdown or .active classes where they are needed. - if ($this->has_children) { + if (isset($args->has_children) && $args->has_children) { $classes[] = 'dropdown'; } if (in_array('current-menu-item', $classes, true) || in_array('current-menu-parent', $classes, true)) { @@ -208,38 +176,44 @@ if (!class_exists('WP_Bootstrap_Navwalker')) : * @since WP 3.0.1 * @since WP 4.1.0 The `$depth` parameter was added. * - * @param string $menu_id The ID that is applied to the menu item's `
  • ` element. - * @param WP_Nav_Menu_Item $item The current menu item. - * @param WP_Nav_Menu_Args $args An object of wp_nav_menu() arguments. - * @param int $depth Depth of menu item. Used for padding. + * @param string $menu_id The ID that is applied to the menu item's `
  • ` element. + * @param WP_Post $item The current menu item. + * @param stdClass $args An object of wp_nav_menu() arguments. + * @param int $depth Depth of menu item. Used for padding. */ $id = apply_filters('nav_menu_item_id', 'menu-item-' . $item->ID, $item, $args, $depth); $id = $id ? ' id="' . esc_attr($id) . '"' : ''; - $output .= $indent . '
  • '; - - // Initialize array for holding the $atts for the link item. - $atts = array(); - $atts['title'] = !empty($item->attr_title) ? $item->attr_title : ''; - $atts['target'] = !empty($item->target) ? $item->target : ''; - if ('_blank' === $item->target && empty($item->xfn)) { - $atts['rel'] = 'noopener noreferrer'; + if ($args->has_children) { + $output .= $indent . ''; } else { - $atts['rel'] = !empty($item->xfn) ? $item->xfn : ''; + $output .= $indent . ''; } - // If the item has_children add atts to . - if ($this->has_children && 0 === $depth) { - $atts['href'] = '#'; - $atts['data-toggle'] = 'dropdown'; - $atts['aria-expanded'] = 'false'; - $atts['class'] = 'dropdown-toggle nav-link'; - $atts['id'] = 'menu-item-dropdown-' . $item->ID; - } else { - if (true === $this->has_schema) { - $atts['itemprop'] = 'url'; - } + // Initialize array for holding the $atts for the link item. + $atts = array(); + /* + * Set title from item to the $atts array - if title is empty then + * default to item title. + */ + if (empty($item->attr_title)) { + $atts['title'] = !empty($item->title) ? strip_tags($item->title) : ''; + } else { + $atts['title'] = $item->attr_title; + } + + $atts['target'] = !empty($item->target) ? $item->target : ''; + $atts['rel'] = !empty($item->xfn) ? $item->xfn : ''; + // If the item has children, add atts to the . + if (isset($args->has_children) && $args->has_children && 0 === $depth && $args->depth > 1) { + $atts['href'] = '#'; + $atts['data-toggle'] = 'dropdown'; + $atts['aria-haspopup'] = 'true'; + $atts['aria-expanded'] = 'false'; + $atts['class'] = 'dropdown-toggle nav-link'; + $atts['id'] = 'menu-item-dropdown-' . $item->ID; + } else { $atts['href'] = !empty($item->url) ? $item->url : '#'; // For items in dropdowns use .dropdown-item instead of .nav-link. if ($depth > 0) { @@ -252,8 +226,7 @@ if (!class_exists('WP_Bootstrap_Navwalker')) : $atts['aria-current'] = $item->current ? 'page' : ''; // Update atts of this item based on any custom linkmod classes. - $atts = $this->update_atts_for_linkmod_type($atts, $linkmod_classes); - + $atts = self::update_atts_for_linkmod_type($atts, $linkmod_classes); // Allow filtering of the $atts array before using it. $atts = apply_filters('nav_menu_link_attributes', $atts, $item, $args, $depth); @@ -261,34 +234,34 @@ if (!class_exists('WP_Bootstrap_Navwalker')) : $attributes = ''; foreach ($atts as $attr => $value) { if (!empty($value)) { - $value = ('href' === $attr) ? esc_url($value) : esc_attr($value); + $value = ('href' === $attr) ? esc_url($value) : esc_attr($value); $attributes .= ' ' . $attr . '="' . $value . '"'; } } // Set a typeflag to easily test if this is a linkmod or not. - $linkmod_type = $this->get_linkmod_type($linkmod_classes); + $linkmod_type = self::get_linkmod_type($linkmod_classes); // START appending the internal item contents to the output. $item_output = isset($args->before) ? $args->before : ''; /* - * This is the start of the internal nav item. Depending on what - * kind of linkmod we have we may need different wrapper elements. - */ + * This is the start of the internal nav item. Depending on what + * kind of linkmod we have we may need different wrapper elements. + */ if ('' !== $linkmod_type) { // Is linkmod, output the required element opener. - $item_output .= $this->linkmod_element_open($linkmod_type, $attributes); + $item_output .= self::linkmod_element_open($linkmod_type, $attributes); } else { // With no link mod type set this must be a standard tag. $item_output .= ''; } /* - * Initiate empty icon var, then if we have a string containing any - * icon classes form the icon markup with an element. This is - * output inside of the item before the $title (the link text). - */ + * Initiate empty icon var, then if we have a string containing any + * icon classes form the icon markup with an element. This is + * output inside of the item before the $title (the link text). + */ $icon_html = ''; if (!empty($icon_class_string)) { // Append an with the icon classes to what is output before links. @@ -303,16 +276,16 @@ if (!class_exists('WP_Bootstrap_Navwalker')) : * * @since WP 4.4.0 * - * @param string $title The menu item's title. - * @param WP_Nav_Menu_Item $item The current menu item. - * @param WP_Nav_Menu_Args $args An object of wp_nav_menu() arguments. - * @param int $depth Depth of menu item. Used for padding. + * @param string $title The menu item's title. + * @param WP_Post $item The current menu item. + * @param stdClass $args An object of wp_nav_menu() arguments. + * @param int $depth Depth of menu item. Used for padding. */ $title = apply_filters('nav_menu_item_title', $title, $item, $args, $depth); // If the .sr-only class was set apply to the nav items text only. if (in_array('sr-only', $linkmod_classes, true)) { - $title = $this->wrap_for_screen_reader($title); + $title = self::wrap_for_screen_reader($title); $keys_to_unset = array_keys($linkmod_classes, 'sr-only', true); foreach ($keys_to_unset as $k) { unset($linkmod_classes[$k]); @@ -323,12 +296,12 @@ if (!class_exists('WP_Bootstrap_Navwalker')) : $item_output .= isset($args->link_before) ? $args->link_before . $icon_html . $title . $args->link_after : ''; /* - * This is the end of the internal nav item. We need to close the - * correct element depending on the type of link or link mod. - */ + * This is the end of the internal nav item. We need to close the + * correct element depending on the type of link or link mod. + */ if ('' !== $linkmod_type) { // Is linkmod, output the required closing element. - $item_output .= $this->linkmod_element_close($linkmod_type); + $item_output .= self::linkmod_element_close($linkmod_type); } else { // With no link mod type set this must be a standard tag. $item_output .= ''; @@ -341,83 +314,94 @@ if (!class_exists('WP_Bootstrap_Navwalker')) : } /** - * Menu fallback. + * Traverse elements to create list from elements. * - * If this function is assigned to the wp_nav_menu's fallback_cb variable - * and a menu has not been assigned to the theme location in the WordPress - * menu manager the function will display nothing to a non-logged in user, - * and will add a link to the WordPress menu manager if logged in as an admin. + * Display one element if the element doesn't have any children otherwise, + * display the element and its children. Will only traverse up to the max + * depth and no ignore elements under that depth. It is possible to set the + * max depth to include all depths, see walk() method. * - * @param array $args passed from the wp_nav_menu function. - * @return string|void String when echo is false. + * This method should not be called directly, use the walk() method instead. + * + * @since WP 2.5.0 + * + * @see Walker::start_lvl() + * + * @param object $element Data object. + * @param array $children_elements List of elements to continue traversing (passed by reference). + * @param int $max_depth Max depth to traverse. + * @param int $depth Depth of current element. + * @param array $args An array of arguments. + * @param string $output Used to append additional content (passed by reference). */ - public static function fallback($args) + public function display_element($element, &$children_elements, $max_depth, $depth, $args, &$output) { - if (!current_user_can('edit_theme_options')) { + if (!$element) { return; } - - // Initialize var to store fallback html. - $fallback_output = ''; - - // Menu container opening tag. - $show_container = false; - if ($args['container']) { - /** - * Filters the list of HTML tags that are valid for use as menu containers. - * - * @since WP 3.0.0 - * - * @param array $tags The acceptable HTML tags for use as menu containers. - * Default is array containing 'div' and 'nav'. - */ - $allowed_tags = apply_filters('wp_nav_menu_container_allowedtags', array('div', 'nav')); - if (is_string($args['container']) && in_array($args['container'], $allowed_tags, true)) { - $show_container = true; - $class = $args['container_class'] ? ' class="menu-fallback-container ' . esc_attr($args['container_class']) . '"' : ' class="menu-fallback-container"'; - $id = $args['container_id'] ? ' id="' . esc_attr($args['container_id']) . '"' : ''; - $fallback_output .= '<' . $args['container'] . $id . $class . '>'; - } - } - - // The fallback menu. - $class = $args['menu_class'] ? ' class="menu-fallback-menu ' . esc_attr($args['menu_class']) . '"' : ' class="menu-fallback-menu"'; - $id = $args['menu_id'] ? ' id="' . esc_attr($args['menu_id']) . '"' : ''; - $fallback_output .= ''; - $fallback_output .= '
  • '; - $fallback_output .= ''; - - // Menu container closing tag. - if ($show_container) { - $fallback_output .= ''; - } - - // if $args has 'echo' key and it's true echo, otherwise return. - if (array_key_exists('echo', $args) && $args['echo']) { - // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped - echo $fallback_output; - } else { - return $fallback_output; + $id_field = $this->db_fields['id']; + // Display this element. + if (is_object($args[0])) { + $args[0]->has_children = !empty($children_elements[$element->$id_field]); } + parent::display_element($element, $children_elements, $max_depth, $depth, $args, $output); } /** - * Filter to ensure the items_Wrap argument contains microdata. + * Menu Fallback. * - * @since 4.2.0 + * If this function is assigned to the wp_nav_menu's fallback_cb variable + * and a menu has not been assigned to the theme location in the WordPress + * menu manager the function with display nothing to a non-logged in user, + * and will add a link to the WordPress menu manager if logged in as an admin. * - * @param array $args The nav instance arguments. - * @return array $args The altered nav instance arguments. + * @param array $args passed from the wp_nav_menu function. */ - public function add_schema_to_navbar_ul($args) + public static function fallback($args) { - if (isset($args['items_wrap'])) { - $wrap = $args['items_wrap']; - if (strpos($wrap, 'SiteNavigationElement') === false) { - $args['items_wrap'] = preg_replace('/(>).*>?\%3\$s/', ' itemscope itemtype="http://www.schema.org/SiteNavigationElement"$0', $wrap); + if (current_user_can('edit_theme_options')) { + + // Get Arguments. + $container = $args['container']; + $container_id = $args['container_id']; + $container_class = $args['container_class']; + $menu_class = $args['menu_class']; + $menu_id = $args['menu_id']; + + // Initialize var to store fallback html. + $fallback_output = ''; + + if ($container) { + $fallback_output .= '<' . esc_attr($container); + if ($container_id) { + $fallback_output .= ' id="' . esc_attr($container_id) . '"'; + } + if ($container_class) { + $fallback_output .= ' class="' . esc_attr($container_class) . '"'; + } + $fallback_output .= '>'; + } + $fallback_output .= '' . esc_attr__('添加导航', 'kratos') . ''; + $fallback_output .= ''; + if ($container) { + $fallback_output .= ''; + } + + // If $args has 'echo' key and it's true echo, otherwise return. + if (array_key_exists('echo', $args) && $args['echo']) { + echo $fallback_output; // WPCS: XSS OK. + } else { + return $fallback_output; } } - return $args; } /** @@ -443,18 +427,18 @@ if (!class_exists('WP_Bootstrap_Navwalker')) : // Loop through $classes array to find linkmod or icon classes. foreach ($classes as $key => $class) { /* - * If any special classes are found, store the class in it's - * holder array and and unset the item from $classes. - */ + * If any special classes are found, store the class in it's + * holder array and and unset the item from $classes. + */ if (preg_match('/^disabled|^sr-only/i', $class)) { // Test for .disabled or .sr-only classes. $linkmod_classes[] = $class; unset($classes[$key]); } elseif (preg_match('/^dropdown-header|^dropdown-divider|^dropdown-item-text/i', $class) && $depth > 0) { /* - * Test for .dropdown-header or .dropdown-divider and a - * depth greater than 0 - IE inside a dropdown. - */ + * Test for .dropdown-header or .dropdown-divider and a + * depth greater than 0 - IE inside a dropdown. + */ $linkmod_classes[] = $class; unset($classes[$key]); } elseif (preg_match('/^fa-(\S*)?|^fa(s|r|l|b)?(\s?)?$/i', $class)) { @@ -519,9 +503,9 @@ if (!class_exists('WP_Bootstrap_Navwalker')) : foreach ($linkmod_classes as $link_class) { if (!empty($link_class)) { /* - * Update $atts with a space and the extra classname - * so long as it's not a sr-only class. - */ + * Update $atts with a space and the extra classname + * so long as it's not a sr-only class. + */ if ('sr-only' !== $link_class) { $atts['class'] .= ' ' . esc_attr($link_class); } @@ -574,9 +558,9 @@ if (!class_exists('WP_Bootstrap_Navwalker')) : $output .= ''; } elseif ('dropdown-header' === $linkmod_type) { /* - * For a header use a span with the .h6 class instead of a real - * header tag so that it doesn't confuse screen readers. - */ + * For a header use a span with the .h6 class instead of a real + * header tag so that it doesn't confuse screen readers. + */ $output .= ''; } elseif ('dropdown-divider' === $linkmod_type) { // This is a divider. @@ -599,9 +583,9 @@ if (!class_exists('WP_Bootstrap_Navwalker')) : $output = ''; if ('dropdown-header' === $linkmod_type || 'dropdown-item-text' === $linkmod_type) { /* - * For a header use a span with the .h6 class instead of a real - * header tag so that it doesn't confuse screen readers. - */ + * For a header use a span with the .h6 class instead of a real + * header tag so that it doesn't confuse screen readers. + */ $output .= ''; } elseif ('dropdown-divider' === $linkmod_type) { // This is a divider. @@ -609,26 +593,5 @@ if (!class_exists('WP_Bootstrap_Navwalker')) : } return $output; } - - /** - * Flattens a multidimensional array to a simple array. - * - * @param array $array a multidimensional array. - * - * @return array a simple array - */ - public function flatten($array) - { - $result = array(); - foreach ($array as $element) { - if (is_array($element)) { - array_push($result, ...$this->flatten($element)); - } else { - $result[] = $element; - } - } - return $result; - } } - -endif; +}