diff --git a/.codeclimate.yml b/.codeclimate.yml index 8b47214..766d577 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -1,10 +1,12 @@ ---- -exclude_paths: +version: "2" + +exclude_patterns: - images/ - js/vendor/ - languages/ - tests/ - vendor/ +- node_modules/ - "**.min.css" - "**.min.js" - "js/jquery-ui-classic.css" @@ -17,7 +19,7 @@ prepare: - "https://raw.githubusercontent.com/bu-ist/coding-standards/master/code-climate-rule-sets/.mdlrc" - "https://raw.githubusercontent.com/bu-ist/coding-standards/master/code-climate-rule-sets/markdown.rb" -engines: +plugins: csslint: enabled: false duplication: @@ -47,8 +49,3 @@ engines: rulesets: codesize,naming,unusedcode scss-lint: enabled: false -ratings: - paths: - - "**.js" - - "**.php" - - "**.css" diff --git a/bu-navigation-widget.php b/bu-navigation-widget.php index a41a700..833f901 100644 --- a/bu-navigation-widget.php +++ b/bu-navigation-widget.php @@ -1,230 +1,363 @@ ' ); // default HTML fragment open -define( 'BU_WIDGET_CONTENTNAV_AFTER', '' ); // default HTML fragment close +// Default element id for list. +define( 'BU_WIDGET_PAGES_LIST_ID', 'contentnavlist' ); +// Default HTML fragment open. UNUSED in the plugin and the BU CMS build. Should be removed. +define( 'BU_WIDGET_CONTENTNAV_BEFORE', '
' ); + +/** + * Widget for displaying navigation. + */ class BU_Widget_Pages extends WP_Widget { + /** + * Options for displaying the widget title. + * + * @var array + */ public $title_options = array( 'none', 'section', 'static' ); + + /** + * Options for the widget display style. + * + * @var array + */ public $styles = array( 'site', 'section', 'adaptive' ); + /** + * Array of defaults for the widget. + * + * @var array + */ public $defaults = array( - 'navigation_title' => 'none', - 'navigation_title_text' => '', - 'navigation_title_url' => '', - 'navigation_style' => 'site' - ); + 'navigation_title' => 'none', + 'navigation_title_text' => '', + 'navigation_title_url' => '', + 'navigation_style' => 'site', + ); + /** + * Constructor that registers with the parent class. + */ public function __construct() { - $widget_ops = array( 'classname' => 'widget_bu_pages', 'description' => __( "Navigation list of your site's pages", 'bu-navigation' ) ); - parent::__construct( 'bu_pages', __('Content Navigation', 'bu-navigation' ), $widget_ops ); + $widget_ops = array( + 'classname' => 'widget_bu_pages', + 'description' => __( "Navigation list of your site's pages", 'bu-navigation' ), + ); + parent::__construct( 'bu_pages', __( 'Content Navigation', 'bu-navigation' ), $widget_ops ); } /** * Returns HTML fragment containing a section title * - * @param array $args widget args, as passed to WP_Widget::widget - * @param array $instance widget instance args, as passed to WP_Widget::widget + * Uses bu_navigation_gather_sections, bu_navigation_get_pages, bu_navigation_pages_by_parent, bu_navigation_get_label + * from includes/library.php + * + * @param WP_Post $post Root post as passed through global to the widget() method. + * @param array $instance widget instance args, as passed to WP_Widget::widget. * @return string HTML fragment with title */ - public function section_title( $args, $instance ) { - global $post; + public function section_title( $post, $instance ) { + // Format string to deliver the title and href as an HTML fragment. + $wrapped_title_format = '%s'; - $html = $title = $href = ''; - $section_id = 0; - - // Determine which post to use for the section title - if ( ! empty( $instance['navigation_style'] ) && $instance['navigation_style'] != 'site' ) { - - // Gather ancestors - $sections = bu_navigation_gather_sections( $post->ID, array( 'post_types' => $post->post_type ) ); - - // Adaptive navigation style uses the grandparent of current post - if ( $instance['navigation_style'] == 'adaptive' ) { - - // Fetch post list, possibly limited to specific sections - $page_args = array( - 'sections' => $sections, - 'post_types' => array( $post->post_type ), - 'include_links' => false, - ); - $pages = bu_navigation_get_pages( $page_args ); - $pages_by_parent = bu_navigation_pages_by_parent( $pages ); - - $last_section = array_pop( $sections ); - array_push( $sections, $last_section ); - - if ( array_key_exists( $last_section, $pages_by_parent ) && - is_array( $pages_by_parent[$last_section] ) && - ( count( $pages_by_parent[$last_section] ) > 0 ) - ) { - // Last section has children, so its parent will be section title - $grandparent_offset = count( $sections ) - 2; - } else { - // Last section has no children, so its grandparent will be the section title - $grandparent_offset = count( $sections ) - 3; - } - - if ( isset( $sections[$grandparent_offset] ) ) { - $section_id = $sections[$grandparent_offset]; - } - } else { - // Default to top level post (if we have one) - if ( isset( $sections[1] ) ) { - $section_id = $sections[1]; - } - } + $section_id = $this->get_title_post_id_for_child( $post, $instance['navigation_style'] ); + // If no title post is returned, use the site title. + if ( ! $section_id ) { + return $this->get_site_title( $wrapped_title_format ); } - // Use section post for title - if ( $section_id ) { - $section = get_post( $section_id ); + // If there is a title post for this child ("section_id"), then try using it for the title. + $section = get_post( $section_id ); - // Prevent usage of non-published posts as titles - if ( 'publish' === $section->post_status ) { - // Second argument prevents usage of default (no title) label - $title = bu_navigation_get_label( $section, '' ); - $href = get_permalink( $section->ID ); - } - } + // Get title, the second argument prevents usage of default (no title) label. + $title = bu_navigation_get_label( $section, '' ); - // Fallback to site title if we're still empty - if ( empty( $title ) ) { - $title = get_bloginfo( 'name' ); - $href = trailingslashit( get_bloginfo( 'url' ) ); + // Prevent usage of non-published posts or empty titles, use site title instead. + if ( ( 'publish' !== $section->post_status ) || empty( $title ) ) { + return $this->get_site_title( $wrapped_title_format ); } - if ( $title && $href ) { - $html = sprintf( "%s\n", esc_attr( $href ), $title ); - } + $href = get_permalink( $section->ID ); - return $html; + return sprintf( $wrapped_title_format, esc_attr( $href ), $title ); } /** - * Display the content navigation widget + * Get site title + * + * Gets the title and href from the site and returns them formatted for use as a widget title. + * It is a protected convenience method to make it easier to give section_title multiple early return options. + * There are more than one condition that make section_title want to return the overall site title + * as the widget title. + * + * @since 1.2.22 + * + * @param string $wrapped_title_format An sprintf format string to render the title and href as an html fragement. + * @return string Formatted html fragment with the site title and link. + */ + protected function get_site_title( $wrapped_title_format ) { + $title = get_bloginfo( 'name' ); + $href = trailingslashit( get_bloginfo( 'url' ) ); + return sprintf( $wrapped_title_format, esc_attr( $href ), $title ); + } + + /** + * Echos the content navigation widget content, overrides parent method. * - * @param array $args widget args - * @param array $instance widget instance args + * @param array $args Display arguments for WP_Widget. + * @param array $instance The settings for the particular instance of the widget. */ public function widget( $args, $instance ) { global $post; - // Only display navigation widget for supported post types - if ( ! in_array( $post->post_type, bu_navigation_supported_post_types() ) ) + // Only display navigation widget for supported post types. + if ( ! in_array( $post->post_type, bu_navigation_supported_post_types(), true ) ) { return; + } - extract( $args ); - - $title = ''; + $title = $this->get_widget_title( $post, $instance ); - // Set widget title - if ( ( $instance['navigation_title'] == 'static' ) && ( ! empty( $instance['navigation_title_text'] ) ) ) { + // Set list arguments based on post type and navigation style. + $list_args = $this->get_list_args( $post, $instance ); - $title = apply_filters( 'widget_title', $instance['navigation_title_text'] ); + do_action( 'bu_navigation_widget_before_list' ); - // Wrap with anchor tag if URL is present - if ( ! empty( $instance['navigation_title_url'] ) ) { - $title = sprintf( '%s', $instance['navigation_title_url'], $title ); - } + // Fetch markup. + $nav_list_markup = bu_navigation_list_pages( apply_filters( 'widget_bu_pages_args', $list_args ) ); - } else if ( $instance['navigation_title'] == 'section' ) { + // Only output anything at all if there is existing markup from list_pages. + if ( empty( $nav_list_markup ) ) { + return; + } - // Use navigation label of top level post for current section - $title = $this->section_title( $args, $instance ); + // Assemble the markup into $output, starting with the opening tags. + $output = sprintf( '%s ', $nav_list_markup ) . $args['after_widget']; - // Set list arguments based on navigation style - if ( array_key_exists( 'navigation_style', $instance ) ) { + // Echo assembled widget markup. + echo wp_kses_post( $output ); + } - $list_args['style'] = $instance['navigation_style']; + /** + * Save handler for the content navigation widget + * + * @param array $new_instance updated widget parameters. + * @param array $old_instance original widget parameters. + */ + public function update( $new_instance, $old_instance ) { - if ( $instance['navigation_style'] == 'section' ) { - $list_args['navigate_in_section'] = 1; - if ( is_404() ) return ''; - } else if ( $instance['navigation_style'] == 'adaptive' ) { - add_action( 'bu_navigation_widget_before_list', 'bu_navigation_widget_adaptive_before_list' ); - } + $instance = $old_instance; + $instance = wp_parse_args( $instance, $this->defaults ); - } else { - $GLOBALS['bu_navigation_plugin']->log( 'No nav label widget style set!' ); - } + $instance['navigation_title'] = ( in_array( $new_instance['navigation_title'], $this->title_options, true ) ) ? $new_instance['navigation_title'] : 'none'; + $instance['navigation_title_text'] = ( 'static' === $instance['navigation_title'] ) ? sanitize_text_field( $new_instance['navigation_title_text'] ) : ''; + $instance['navigation_title_url'] = ( 'static' === $instance['navigation_title'] ) ? sanitize_text_field( $new_instance['navigation_title_url'] ) : ''; + $instance['navigation_style'] = ( in_array( $new_instance['navigation_style'], $this->styles, true ) ) ? $new_instance['navigation_style'] : 'site'; - do_action( 'bu_navigation_widget_before_list' ); + return $instance; + } - // Fetch markup and display - $out = bu_navigation_list_pages( apply_filters( 'widget_bu_pages_args', $list_args ) ); + /** + * Display the content navigation widget form + * + * @param array $instance the specific widget instance being displayed. + */ + public function form( $instance ) { - if ( ! empty( $out ) ) { + $instance = wp_parse_args( $instance, $this->defaults ); - printf('%s ', $out); + /** + * Gets the widget title based on the instance options. + * + * There are 3 return scenarios: + * 1- empty string for a widget that doesn't render a title at all + * 2- the static title + * 3- the section title as returned by section_title() + * + * This helper function sorts out those scenarios based on the instance options. + * + * @since 1.2.22 + * + * @param WP_Post $post Root post as passed through global to the widget() method. + * @param array $instance The settings for the particular instance of the widget. + * @return string $title Empty string, plain text title, or anchor tag wrapped title string. + */ + protected function get_widget_title( $post, $instance ) { + if ( 'none' === $instance['navigation_title'] ) { + return ''; + } + + if ( 'static' === $instance['navigation_title'] ) { + // Do not make a special condition if the navigation_title_text is empty. + // Empty values for navigation_title_text are valid, it just means the widget doesn't render a title. + $filtered_title = apply_filters( 'widget_title', $instance['navigation_title_text'] ); - echo $after_widget; + // Wrap the title in an anchor tag if a URL was specified, otherwise just return the title. + return ( '' !== $instance['navigation_title_url'] ) ? sprintf( '%s', $instance['navigation_title_url'], $filtered_title ) : $filtered_title; + } + if ( 'section' === $instance['navigation_title'] ) { + return $this->section_title( $post, $instance ); } + // In case the navigation_title option is something else, just return an empty string. + return ''; } /** - * Save handler for the content navigation widget + * Get arguments for the page list query. + * + * A helper method that sets up the list query arguements based on the instance style. + * These arguements are structured for the bu_navigation_list_pages() query in library.php. + * + * @since 1.2.22 * - * @param array $new_instance updated widget parameters - * @param array $old_instance original widget parameters + * @param WP_Post $post The post being rendered. + * @param array $instance The settings for this instance of the widget. + * + * @return array Arguements for the bu_navigation_list_pages() query in library.php */ - public function update( $new_instance, $old_instance ) { + protected function get_list_args( $post, $instance ) { - $instance = $old_instance; - $instance = wp_parse_args( $instance, $this->defaults ); + // Prepare arguments to bu_navigation_list_pages. + $list_args = array( + 'page_id' => $post->ID, + 'title_li' => '', + 'echo' => 0, + 'container_id' => BU_WIDGET_PAGES_LIST_ID, + 'post_types' => $post->post_type, + ); - $instance['navigation_title'] = ( in_array( $new_instance['navigation_title'], $this->title_options ) ) ? $new_instance['navigation_title'] : 'none'; - $instance['navigation_title_text'] = ( $instance['navigation_title'] == 'static' ) ? sanitize_text_field( $new_instance['navigation_title_text'] ) : ''; - $instance['navigation_title_url'] = ( $instance['navigation_title'] == 'static' ) ? sanitize_text_field( $new_instance['navigation_title_url'] ) : ''; - $instance['navigation_style'] = ( in_array( $new_instance['navigation_style'], $this->styles ) ) ? $new_instance['navigation_style'] : 'site'; + // Not sure this check is necessary as there should always be an instance style, but leaving it in to preserve original behavior. + if ( ! array_key_exists( 'navigation_style', $instance ) ) { + $GLOBALS['bu_navigation_plugin']->log( 'No nav label widget style set!' ); + return $list_args; + } - return $instance; + // Include the instance navigation style in the list args. + $list_args['style'] = $instance['navigation_style']; + + // 'section' style has special handling. + if ( 'section' === $instance['navigation_style'] ) { + $list_args['navigate_in_section'] = 1; + // Not sure why it is necessary to check for a 404 here, but this is the original handling. + return ( is_404() ) ? '' : $list_args; + } + + // 'adaptive' style needs an action from included/library.php to be loaded. + if ( 'adaptive' === $instance['navigation_style'] ) { + add_action( 'bu_navigation_widget_before_list', 'bu_navigation_widget_adaptive_before_list' ); + return $list_args; + } + + // 'site' navigation_style doesn't require additional handling. + return $list_args; } /** - * Display the content navigation widget form + * Get adaptive section id. + * + * Adaptive navigation style uses the title of the grandparent of current post. + * Given a post type and the post ids of the current section members, this function + * returns the post id of the grandparent post. * - * @param array $instance the specific widget instance being displayed + * @since 1.2.22 + * + * @param array $sections Array of post ids. + * @param string $post_type Post type of the post being rendered. + * @return string Post Id of the grandparent post for the widget title. */ - public function form( $instance ) { + protected function get_adaptive_section_id( $sections, $post_type ) { + // Fetch post list, possibly limited to specific sections. + $page_args = array( + 'sections' => $sections, + 'post_types' => array( $post_type ), + 'include_links' => false, + ); + $pages = bu_navigation_get_pages( $page_args ); + $pages_by_parent = bu_navigation_pages_by_parent( $pages ); + + // This looks strange, but is just a way to quickly get the last element of an array in php. + $last_section = array_pop( $sections ); + array_push( $sections, $last_section ); + + if ( array_key_exists( $last_section, $pages_by_parent ) && + is_array( $pages_by_parent[ $last_section ] ) && + ( count( $pages_by_parent[ $last_section ] ) > 0 ) + ) { + // Last section has children, so its parent will be section title. + $grandparent_offset = count( $sections ) - 2; + } else { + // Last section has no children, so its grandparent will be the section title. + $grandparent_offset = count( $sections ) - 3; + } - $instance = wp_parse_args( $instance, $this->defaults ); + // Return the calculated grandparent post id, or 0 if none found. + return ( isset( $sections[ $grandparent_offset ] ) ) ? $sections[ $grandparent_offset ] : 0; + } - $navigation_title = ( in_array( $instance['navigation_title'], $this->title_options ) ) ? $instance['navigation_title'] : 'none'; - $navigation_title_text = esc_attr( $instance['navigation_title_text'] ); - $navigation_title_url = esc_attr( $instance['navigation_title_url'] ); - $navigation_style = ( in_array( $instance['navigation_style'], $this->styles ) ) ? $instance['navigation_style'] : 'site'; + /** + * Get the title post id for a given child post. + * + * Given a post in the hierarchy, returns a post id for a "title" post, based on the current navigation style (mode). + * + * @since 1.2.22 + * + * @param WP_Post $post The post object as passed to the the widget() method. + * @param string $nav_style The navigation style of the widget (mode). + * @return int Either a post id for the title post, or zero if there is no appropriate match. + */ + protected function get_title_post_id_for_child( $post, $nav_style ) { + // Site mode doesn't need a title post, skip gather_section(). + if ( 'site' === $nav_style ) { + return 0; + } - include( BU_NAV_PLUGIN_DIR . '/templates/widget-form.php' ); + // Gets an array of page ids representing the "section" for a given post. + $sections = bu_navigation_gather_sections( $post->ID, array( 'post_types' => $post->post_type ) ); - } + if ( 'section' === $nav_style ) { + // Default to top level post of the section (if we have one). + return isset( $sections[1] ) ? $sections[1] : 0; + } + if ( 'adaptive' === $nav_style ) { + return $this->get_adaptive_section_id( $sections, $post->post_type ); + } + + // Default to zero for any unknown $nav_style (mode). + return 0; + } } diff --git a/bu-navigation.php b/bu-navigation.php index 39269bc..d013249 100644 --- a/bu-navigation.php +++ b/bu-navigation.php @@ -5,7 +5,7 @@ * Author: Boston University (IS&T) * Author URI: http://sites.bu.edu/web/ * Description: Provides alternative navigation elements designed for blogs with large page counts - * Version: 1.2.21 + * Version: 1.2.22 * Text Domain: bu-navigation * Domain Path: /languages * License: GPL2+ @@ -81,7 +81,7 @@ class BU_Navigation_Plugin { * * @var string */ - const VERSION = '1.2.21'; + const VERSION = '1.2.22'; /** * Plugin class constructor. diff --git a/package-lock.json b/package-lock.json index 438893e..a999e75 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "bu-navigation", - "version": "1.2.21", + "version": "1.2.22", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 6556173..65fa422 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "bu-navigation", - "version": "1.2.21", + "version": "1.2.22", "description": "Provides alternative navigation elements designed for blogs with large page counts", "main": "bu-navigation.php", "directories": { diff --git a/readme.md b/readme.md index 8b6ee9c..4a8e5e8 100644 --- a/readme.md +++ b/readme.md @@ -3,7 +3,7 @@ **Tags:** navigation, hierarchical, post type, boston university, bu **Requires at least:** 3.1 **Tested up to:** 5.5 -**Stable tag:** 1.2.21 +**Stable tag:** 1.2.22 **License:** GPLv2 or later **License URI:** http://www.gnu.org/licenses/gpl-2.0.html @@ -93,6 +93,10 @@ Please see this page for the details: ## Changelog +### 1.2.22 + +* Refactor bu-navigation-widget.php to improve code standards. + ### 1.2.21 * Updates npm dependencies and Grunt toolchain. diff --git a/readme.txt b/readme.txt index 828f083..00e8b13 100644 --- a/readme.txt +++ b/readme.txt @@ -3,7 +3,7 @@ Contributors: ntk, mgburns, gcorne, jtwiest, awbauer, inderpreet99 Tags: navigation, hierarchical, post type, boston university, bu Requires at least: 3.1 Tested up to: 5.5 -Stable tag: 1.2.21 +Stable tag: 1.2.22 License: GPLv2 or later License URI: http://www.gnu.org/licenses/gpl-2.0.html @@ -78,6 +78,10 @@ Please see this page for the details: == Changelog == += 1.2.22 = + +* Refactor bu-navigation-widget.php to improve code standards. + = 1.2.21 = * Updates npm dependencies and Grunt toolchain