From b60b90836e445e0f4424cf12346b7399f603154f Mon Sep 17 00:00:00 2001 From: Ashley Gibson Date: Sun, 21 Nov 2021 13:42:58 +0000 Subject: [PATCH 1/3] Rework main plugin class & add LegacyServiceProvider. --- novelist.php | 281 +----------------- src/Plugin.php | 156 ++++++++++ .../LegacyServiceProvider.php | 88 ++++++ src/ServiceProviders/ServiceProvider.php | 4 +- 4 files changed, 254 insertions(+), 275 deletions(-) create mode 100644 src/Plugin.php create mode 100644 src/ServiceProviders/LegacyServiceProvider.php diff --git a/novelist.php b/novelist.php index d76293e..bcab3fd 100644 --- a/novelist.php +++ b/novelist.php @@ -40,279 +40,14 @@ exit; } -require __DIR__.'/vendor/autoload.php'; - -if (! class_exists('Novelist')) : - - class Novelist - { - /** @var \Novelist\Container\Container */ - private $container; - - /** - * Service providers to boot. - * - * @see Novelist::boot() - * @see Novelist::loadServiceProviders() - * - * @var string[] - */ - private $serviceProviders = []; - - /** @var bool */ - private $serviceProvidersLoaded = false; - - /** - * Novelist_Roles object - * - * @var Novelist_Roles - * @since 1.0.0 - */ - public $roles; - - /** - * HTML elements helper class. - * - * @var Novelist_HTML - * @since 1.1.0 - */ - public $html; - - public function __construct() - { - $this->container = new \Novelist\Container\Container(); - } - - /** - * Initializes the plugin. - * - * @since 2.0 - */ - public function boot() - { - if (version_compare(phpversion(), '7.1', '<')) { - return; - } - - $this->setup_constants(); - $this->loadServiceProviders(); - - add_action('plugins_loaded', [$this, 'load_textdomain']); - - $this->includes(); - $this->roles = new Novelist_Roles(); - $this->html = new Novelist_HTML(); - } - - /** - * Novelist instance. - * - * Insures that only one instance of Novelist exists at any one time. - * - * @deprecated 2.0 - * - * @uses Novelist::setup_constants() Set up the plugin constants. - * @uses Novelist::includes() Include any required files. - * @uses Novelist::load_textdomain() Load the language files. - * - * @access public - * @since 1.0.0 - * @return Novelist Instance of Novelist class - */ - public static function instance() - { - return novelist(); - } - - /** - * Properties are loaded from the service container. - * - * @since 2.0 - * - * @param string $property - * - * @return mixed|object - * @throws Exception - */ - public function __get($property) - { - return $this->container->get($property); - } - - /** - * Magic methods are passed to the service container. - * - * @param $name - * @param $arguments - * - * @return mixed - */ - public function __call($name, $arguments) - { - return call_user_func_array([$this->container, $name], $arguments); - } - - /** - * Throw error on object clone. - * - * The whole idea of the singleton design pattern is that there is a single - * object therefore, we don't want the object to be cloned. - * - * @access protected - * @since 1.0.0 - * @return void - */ - public function __clone() - { - // Cloning instances of the class is forbidden. - _doing_it_wrong(__FUNCTION__, __('Cheatin’ huh?', 'novelist'), '1.0.0'); - } - - /** - * Disable unserializing of the class. - * - * @access protected - * @since 1.0.0 - * @return void - */ - public function __wakeup() - { - // Unserializing instances of the class is forbidden. - _doing_it_wrong(__FUNCTION__, __('Cheatin’ huh?', 'novelist'), '1.0.0'); - } - - /** - * Setup plugin constants. - * - * @access private - * @since 1.0.0 - * @return void - */ - private function setup_constants() - { - // Plugin version. - if (! defined('NOVELIST_VERSION')) { - define('NOVELIST_VERSION', '1.1.11'); - } - - // Plugin Folder Path. - if (! defined('NOVELIST_PLUGIN_DIR')) { - define('NOVELIST_PLUGIN_DIR', plugin_dir_path(__FILE__)); - } - - // Plugin Folder URL. - if (! defined('NOVELIST_PLUGIN_URL')) { - define('NOVELIST_PLUGIN_URL', plugin_dir_url(__FILE__)); - } - - // Plugin Root File. - if (! defined('NOVELIST_PLUGIN_FILE')) { - define('NOVELIST_PLUGIN_FILE', __FILE__); - } - } - - /** - * Registers and boots all service providers. - * - * @since 2.0 - */ - private function loadServiceProviders() - { - if ($this->serviceProvidersLoaded) { - return; - } - - $providers = []; - foreach ($this->serviceProviders as $serviceProvider) { - if (! is_subclass_of($serviceProvider, \Novelist\ServiceProviders\ServiceProvider::class)) { - throw new \InvalidArgumentException(sprintf( - '%s class must implement the ServiceProvider interface.', - $serviceProvider - )); - } - - /** @var \Novelist\ServiceProviders\ServiceProvider $serviceProvider */ - $serviceProvider = new $serviceProvider(); - $serviceProvider->register(); - $providers[] = $serviceProvider; - } - - foreach ($providers as $serviceProvider) { - $serviceProvider->boot(); - } - - $this->serviceProvidersLoaded = true; - } - - /** - * Include Required Files - * - * @access private - * @since 1.0.0 - * @return void - */ - private function includes() - { - - global $novelist_options; - - // Settings. - require_once NOVELIST_PLUGIN_DIR.'includes/admin/settings/register-settings.php'; - if (empty($novelist_options)) { - $novelist_options = novelist_get_settings(); - } - - require_once NOVELIST_PLUGIN_DIR.'includes/class-novelist-html.php'; - require_once NOVELIST_PLUGIN_DIR.'includes/class-novelist-roles.php'; - require_once NOVELIST_PLUGIN_DIR.'includes/post-types.php'; - require_once NOVELIST_PLUGIN_DIR.'includes/class-novelist-book.php'; - require_once NOVELIST_PLUGIN_DIR.'includes/class-novelist-shortcodes.php'; - require_once NOVELIST_PLUGIN_DIR.'includes/book-functions.php'; - require_once NOVELIST_PLUGIN_DIR.'includes/book-filters.php'; - require_once NOVELIST_PLUGIN_DIR.'includes/load-assets.php'; - require_once NOVELIST_PLUGIN_DIR.'includes/misc-functions.php'; - require_once NOVELIST_PLUGIN_DIR.'includes/template-functions.php'; - require_once NOVELIST_PLUGIN_DIR.'includes/widgets/widget-book.php'; - require_once NOVELIST_PLUGIN_DIR.'includes/widgets/widget-books-by-series.php'; - require_once NOVELIST_PLUGIN_DIR.'includes/widgets/widget-word-count.php'; - - if (is_admin()) { - require_once NOVELIST_PLUGIN_DIR.'includes/admin/admin-actions.php'; - require_once NOVELIST_PLUGIN_DIR.'includes/admin/extensions.php'; - require_once NOVELIST_PLUGIN_DIR.'includes/admin/thickbox.php'; - require_once NOVELIST_PLUGIN_DIR.'includes/admin/tools.php'; - require_once NOVELIST_PLUGIN_DIR.'includes/admin/admin-pages.php'; - require_once NOVELIST_PLUGIN_DIR.'includes/admin/class-novelist-notices.php'; - require_once NOVELIST_PLUGIN_DIR.'includes/admin/class-welcome.php'; - require_once NOVELIST_PLUGIN_DIR.'includes/admin/settings/display-settings.php'; - require_once NOVELIST_PLUGIN_DIR.'includes/admin/books/meta-box.php'; - require_once NOVELIST_PLUGIN_DIR.'includes/admin/books/sanitize-meta-fields.php'; - require_once NOVELIST_PLUGIN_DIR.'includes/admin/books/dashboard-columns.php'; - require_once NOVELIST_PLUGIN_DIR.'includes/admin/books/demo-book.php'; - require_once NOVELIST_PLUGIN_DIR.'includes/admin/upgrades/upgrade-functions.php'; - } - - require_once NOVELIST_PLUGIN_DIR.'includes/install.php'; - - } - - /** - * Loads the plugin language files. - * - * @access public - * @since 1.0.0 - * @return void - */ - public function load_textdomain() - { - $lang_dir = dirname(plugin_basename(NOVELIST_PLUGIN_FILE)).'/languages/'; - $lang_dir = apply_filters('novelist/languages-directory', $lang_dir); - load_plugin_textdomain('novelist', false, $lang_dir); - } +if (version_compare(phpversion(), '7.1', '<')) { + return; +} - } +const NOVELIST_VERSION = '1.1.11'; +const NOVELIST_PLUGIN_FILE = __FILE__; -endif; // End class exists check. +require __DIR__.'/vendor/autoload.php'; /** * Get Novelist up and running. @@ -320,14 +55,14 @@ public function load_textdomain() * This function returns an instance of the Novelist class. * * @since 1.0.0 - * @return Novelist|object + * @return \Novelist\Plugin|object */ function novelist($abstract = null) { static $instance = null; if ($instance === null) { - $instance = new Novelist(); + $instance = new \Novelist\Plugin(); } if ($abstract !== null) { diff --git a/src/Plugin.php b/src/Plugin.php new file mode 100644 index 0000000..e5437bf --- /dev/null +++ b/src/Plugin.php @@ -0,0 +1,156 @@ +container = new Container\Container(); + } + + public function boot(): void + { + $this->setupConstants(); + $this->loadServiceProviders(); + + add_action('plugins_loaded', [$this, 'load_textdomain']); + } + + /** + * Novelist instance. + * + * Insures that only one instance of Novelist exists at any one time. + * + * @deprecated 2.0 + * + * @access public + * @since 1.0.0 + * @return Plugin Instance of Novelist class + */ + public static function instance(): Plugin + { + return novelist(); + } + + /** + * Properties are loaded from the service container. + * + * @since 2.0 + * + * @param string $property + * + * @return mixed|object + * @throws \Exception + */ + public function __get($property) + { + return $this->container->get($property); + } + + /** + * Magic methods are passed to the service container. + * + * @param $name + * @param $arguments + * + * @return mixed + */ + public function __call($name, $arguments) + { + return call_user_func_array([$this->container, $name], $arguments); + } + + private function setupConstants(): void + { + // Plugin Folder Path. + if (! defined('NOVELIST_PLUGIN_DIR')) { + define('NOVELIST_PLUGIN_DIR', plugin_dir_path(NOVELIST_PLUGIN_FILE)); + } + + // Plugin Folder URL. + if (! defined('NOVELIST_PLUGIN_URL')) { + define('NOVELIST_PLUGIN_URL', plugin_dir_url(NOVELIST_PLUGIN_FILE)); + } + } + + /** + * Registers and boots all service providers. + * + * @since 2.0 + */ + private function loadServiceProviders() + { + if ($this->serviceProvidersLoaded) { + return; + } + + $providers = []; + foreach ($this->serviceProviders as $serviceProvider) { + if (! is_subclass_of($serviceProvider, ServiceProviders\ServiceProvider::class)) { + throw new \InvalidArgumentException(sprintf( + '%s class must implement the ServiceProvider interface.', + $serviceProvider + )); + } + + /** @var ServiceProviders\ServiceProvider $serviceProvider */ + $serviceProvider = new $serviceProvider(); + $serviceProvider->register(); + $providers[] = $serviceProvider; + } + + foreach ($providers as $serviceProvider) { + $serviceProvider->boot(); + } + + $this->serviceProvidersLoaded = true; + } + + /** + * Loads the plugin language files. + * + * @access public + * @since 1.0.0 + * @return void + */ + public function load_textdomain(): void + { + $lang_dir = dirname(plugin_basename(NOVELIST_PLUGIN_FILE)).'/languages/'; + $lang_dir = apply_filters('novelist/languages-directory', $lang_dir); + load_plugin_textdomain('novelist', false, $lang_dir); + } + +} diff --git a/src/ServiceProviders/LegacyServiceProvider.php b/src/ServiceProviders/LegacyServiceProvider.php new file mode 100644 index 0000000..6d3f83d --- /dev/null +++ b/src/ServiceProviders/LegacyServiceProvider.php @@ -0,0 +1,88 @@ +includeFiles(); + $this->registerClassAliases(); + $this->bindClasses(); + } + + public function boot(): void + { + + } + + private function includeFiles(): void + { + global $novelist_options; + + // Settings. + require_once NOVELIST_PLUGIN_DIR.'includes/admin/settings/register-settings.php'; + if (empty($novelist_options)) { + $novelist_options = novelist_get_settings(); + } + + require_once NOVELIST_PLUGIN_DIR.'includes/class-novelist-html.php'; + require_once NOVELIST_PLUGIN_DIR.'includes/class-novelist-roles.php'; + require_once NOVELIST_PLUGIN_DIR.'includes/post-types.php'; + require_once NOVELIST_PLUGIN_DIR.'includes/class-novelist-book.php'; + require_once NOVELIST_PLUGIN_DIR.'includes/class-novelist-shortcodes.php'; + require_once NOVELIST_PLUGIN_DIR.'includes/book-functions.php'; + require_once NOVELIST_PLUGIN_DIR.'includes/book-filters.php'; + require_once NOVELIST_PLUGIN_DIR.'includes/load-assets.php'; + require_once NOVELIST_PLUGIN_DIR.'includes/misc-functions.php'; + require_once NOVELIST_PLUGIN_DIR.'includes/template-functions.php'; + require_once NOVELIST_PLUGIN_DIR.'includes/widgets/widget-book.php'; + require_once NOVELIST_PLUGIN_DIR.'includes/widgets/widget-books-by-series.php'; + require_once NOVELIST_PLUGIN_DIR.'includes/widgets/widget-word-count.php'; + + if (is_admin()) { + require_once NOVELIST_PLUGIN_DIR.'includes/admin/admin-actions.php'; + require_once NOVELIST_PLUGIN_DIR.'includes/admin/extensions.php'; + require_once NOVELIST_PLUGIN_DIR.'includes/admin/thickbox.php'; + require_once NOVELIST_PLUGIN_DIR.'includes/admin/tools.php'; + require_once NOVELIST_PLUGIN_DIR.'includes/admin/admin-pages.php'; + require_once NOVELIST_PLUGIN_DIR.'includes/admin/class-novelist-notices.php'; + require_once NOVELIST_PLUGIN_DIR.'includes/admin/class-welcome.php'; + require_once NOVELIST_PLUGIN_DIR.'includes/admin/settings/display-settings.php'; + require_once NOVELIST_PLUGIN_DIR.'includes/admin/books/meta-box.php'; + require_once NOVELIST_PLUGIN_DIR.'includes/admin/books/sanitize-meta-fields.php'; + require_once NOVELIST_PLUGIN_DIR.'includes/admin/books/dashboard-columns.php'; + require_once NOVELIST_PLUGIN_DIR.'includes/admin/books/demo-book.php'; + require_once NOVELIST_PLUGIN_DIR.'includes/admin/upgrades/upgrade-functions.php'; + } + + require_once NOVELIST_PLUGIN_DIR.'includes/install.php'; + } + + private function registerClassAliases(): void + { + class_alias(Plugin::class, 'Novelist'); + } + + /** + * Binds classes that used to be properties in the `Novelist` class. (now `Novelist\Plugin`) + */ + private function bindClasses(): void + { + novelist()->bind(\Novelist_Roles::class); + novelist()->alias(\Novelist_Roles::class, 'roles'); + + novelist()->bind(\Novelist_HTML::class); + novelist()->alias(\Novelist_HTML::class, 'html'); + } +} diff --git a/src/ServiceProviders/ServiceProvider.php b/src/ServiceProviders/ServiceProvider.php index 8032e0f..6188179 100644 --- a/src/ServiceProviders/ServiceProvider.php +++ b/src/ServiceProviders/ServiceProvider.php @@ -18,7 +18,7 @@ interface ServiceProvider * * @return void */ - public function register(); + public function register(): void; /** * Bootstraps the service after all of the services have been registered. @@ -28,5 +28,5 @@ public function register(); * * @return void */ - public function boot(); + public function boot(): void; } From 0aa5121eeb54c5825200c10acf9ca34ea6a66722 Mon Sep 17 00:00:00 2001 From: Ashley Gibson Date: Sun, 21 Nov 2021 14:02:41 +0000 Subject: [PATCH 2/3] Refactor shortcodes. --- includes/class-novelist-html.php | 242 -------- includes/class-novelist-shortcodes.php | 547 +----------------- src/Helpers/Html.php | 233 ++++++++ src/Plugin.php | 5 +- .../LegacyServiceProvider.php | 7 +- .../ShortcodeServiceProvider.php | 38 ++ src/Shortcodes/BookGridShortcode.php | 389 +++++++++++++ src/Shortcodes/BooksInShortcode.php | 20 + src/Shortcodes/SeriesGridShortcode.php | 153 +++++ src/Shortcodes/Shortcode.php | 37 ++ src/Shortcodes/ShowBookShortcode.php | 63 ++ 11 files changed, 955 insertions(+), 779 deletions(-) delete mode 100644 includes/class-novelist-html.php create mode 100644 src/Helpers/Html.php create mode 100644 src/ServiceProviders/ShortcodeServiceProvider.php create mode 100644 src/Shortcodes/BookGridShortcode.php create mode 100644 src/Shortcodes/BooksInShortcode.php create mode 100644 src/Shortcodes/SeriesGridShortcode.php create mode 100644 src/Shortcodes/Shortcode.php create mode 100644 src/Shortcodes/ShowBookShortcode.php diff --git a/includes/class-novelist-html.php b/includes/class-novelist-html.php deleted file mode 100644 index e66c2ec..0000000 --- a/includes/class-novelist-html.php +++ /dev/null @@ -1,242 +0,0 @@ - 'novelist_select_series', - 'id' => 'novelist_select_series', - 'class' => '', - 'multiple' => false, - 'selected' => 0, - 'number' => 30, - 'standalones' => __( 'Standalones', 'novelist' ), - 'show_option_all' => __( 'All Series', 'novelist' ), - 'term_args' => array( - 'taxonomy' => 'novelist-series' - ) - ); - - $args = wp_parse_args( $args, $defaults ); - $options = array(); - - $series = get_terms( $args['term_args'] ); - - if ( $series ) { - foreach ( $series as $term ) { - $options[ $term->term_id ] = $term->name; - } - } - - if ( $args['standalones'] ) { - $options['none'] = $args['standalones']; - } - - $this->select( array( - 'name' => $args['name'], - 'selected' => $args['selected'], - 'id' => $args['id'], - 'class' => $args['class'], - 'options' => $options, - 'multiple' => $args['multiple'], - 'show_option_all' => $args['show_option_all'], - 'show_option_none' => false - ) ); - - } - - /** - * Genre Dropdown - * - * @param array $args Arguments to override the defaults. - * - * @access public - * @since 1.1.0 - * @return void - */ - public function genre_dropdown( $args = array() ) { - - $defaults = array( - 'name' => 'novelist_genre_dropdown', - 'id' => 'novelist_genre_dropdown', - 'class' => '', - 'multiple' => false, - 'selected' => 0, - 'number' => 30, - 'show_option_all' => __( 'All Genres', 'novelist' ), - 'term_args' => array( - 'taxonomy' => 'novelist-genre' - ) - ); - - $args = wp_parse_args( $args, $defaults ); - $options = array(); - - $series = get_terms( $args['term_args'] ); - - if ( $series ) { - foreach ( $series as $term ) { - $options[ $term->term_id ] = $term->name; - } - } - - $this->select( array( - 'name' => $args['name'], - 'selected' => $args['selected'], - 'id' => $args['id'], - 'class' => $args['class'], - 'options' => $options, - 'multiple' => $args['multiple'], - 'show_option_all' => $args['show_option_all'], - 'show_option_none' => false - ) ); - - } - - /** - * Render Text Field - * - * @param array $args Arguments to override the defaults. - * - * @access public - * @since 1.1.0 - * @return void - */ - public function text( $args = array() ) { - - $defaults = array( - 'id' => '', - 'name' => '', - 'type' => 'text', - 'value' => '', - 'placeholder' => '', - 'class' => '', - 'disabled' => false - ); - - $args = wp_parse_args( $args, $defaults ); - - $class = implode( ' ', array_map( 'sanitize_html_class', explode( ' ', $args['class'] ) ) ); - $disabled = ''; - if ( $args['disabled'] ) { - $disabled = ' disabled="disabled"'; - } - ?> - > - array(), - 'name' => '', - 'class' => '', - 'id' => '', - 'selected' => 0, - 'placeholder' => '', - 'multiple' => false, - 'show_option_all' => '', - 'show_option_none' => '' - ); - - $args = wp_parse_args( $args, $defaults ); - - if ( $args['multiple'] ) { - $multiple = ' MULTIPLE'; - } else { - $multiple = ''; - } - - $class = implode( ' ', array_map( 'sanitize_html_class', explode( ' ', $args['class'] ) ) ); - ?> - - '', - // Comma-separated list of series IDs or names to display results from. Or "none" to only get standalones. - 'genre' => '', - // Comma-separated list of genre IDs or names to display results from. - 'relation' => 'OR', - // Relation between taxonomy term queries. - 'columns' => 4, - // Number of columns in each row. - 'title' => 'false', - // Whether or not to display the title. - 'series-number' => 'false', - // Whether or not to display the number in the series. - 'excerpt' => 'false', - // Excerpt from the synopsis. - 'full-content' => 'false', - // Fully formatted book content. - 'full-synopsis' => 'false', - // Fully synopsis - 'button' => 'true', - // Button to single book page. Text in here becomes the button text. - 'covers' => 'true', - // Whether or not to display book cover. - 'covers-align' => 'center', - // Cover alignment. - 'orderby' => 'menu_order', - // How to order the results. Options: `name`, `title`, `random`, `publication`, `date`, `menu_order`, `series_number` - 'order' => 'ASC', - // Order of the results. - 'pagination' => 'true', - // Whether or not to include pagination. - 'number' => '12', - // Number of results per page. - 'offset' => 0, - // Number of books to skip - 'display' => '', - 'ids' => '' - // Specific book IDs - ), $atts, 'novelist-books' ); - - if ( ! empty( $atts['ids'] ) ) { - - $book_ids = explode( ',', $atts['ids'] ); - $book_ids = array_map( 'absint', $book_ids ); - - $args = array( - 'post_type' => 'book', - 'posts_per_page' => - 1, - 'post__in' => $book_ids - ); - - } else { - - /* - * Start building our query args. - */ - $args = array( - 'post_type' => 'book', - 'orderby' => $atts['orderby'], - 'order' => $atts['order'], - 'meta_query' => array( - array( - 'key' => 'novelist_hide', - 'compare' => 'NOT EXISTS' - ) - ) - ); - - /* - * Add in offset args. - */ - if ( $atts['offset'] > 0 ) { - $args['offset'] = absint( $atts['offset'] ); - } - - /* - * Catch out invalid orderby dates. - */ - switch ( $atts['orderby'] ) { - - case 'name' : - // nothing here intentionally - // use below value - - case 'title' : - $args['orderby'] = 'title'; - break; - - case 'rand' : - // nothing here intentionally - // use below value - - case 'random' : - $args['orderby'] = 'rand'; - break; - - case 'publication' : - $args['orderby'] = 'meta_value_num'; - $args['meta_key'] = 'novelist_pub_date_timestamp'; - break; - - case 'series_number' : - $args['orderby'] = 'meta_value_num'; - $args['meta_key'] = 'novelist_series'; - break; - - case 'date' : - $args['orderby'] = 'date'; - break; - - default : - $args['orderby'] = 'menu_order title'; - break; - - } - - /* - * Add taxonomy relation. - */ - if ( $atts['series'] || $atts['genre'] ) { - $args['tax_query'] = array( - 'relation' => $atts['relation'] - ); - } - - /* - * Filter by series. - */ - if ( $atts['series'] ) { - if ( $atts['series'] == 'none' ) { - $get_terms_args = array( - 'taxonomy' => 'novelist-series', - 'hide_empty' => false, - 'fields' => 'ids' - ); - $all_series = get_terms( $get_terms_args ); - - $args['tax_query'][] = array( - 'taxonomy' => 'novelist-series', - 'field' => 'term_id', - 'terms' => $all_series, - 'operator' => 'NOT IN' - ); - - // We need to unset this to prevent funky errors. - if ( array_key_exists( 'meta_key', $args ) && $args['meta_key'] == 'novelist_series' ) { - unset( $args['meta_key'] ); - $args['orderby'] = 'title'; - } - - } else { - - $series_names = explode( ',', $atts['series'] ); - - foreach ( $series_names as $name ) { - if ( is_numeric( $name ) ) { - $term_id = $name; - } else { - $term = get_term_by( 'name', trim( $name ), 'novelist-series' ); - - if ( ! $term ) { - continue; - } - - $term_id = $term->term_id; - } - - $args['tax_query'][] = array( - 'taxonomy' => 'novelist-series', - 'field' => 'term_id', - 'terms' => $term_id - ); - } - - } - } - - /* - * Filter by genre. - */ - if ( $atts['genre'] ) { - $genre_names = explode( ',', $atts['genre'] ); - - foreach ( $genre_names as $name ) { - if ( is_numeric( $name ) ) { - $term_id = $name; - } else { - $term = get_term_by( 'name', trim( $name ), 'novelist-genre' ); - - if ( ! $term ) { - continue; - } - - $term_id = $term->term_id; - } - - $args['tax_query'][] = array( - 'taxonomy' => 'novelist-genre', - 'field' => 'term_id', - 'terms' => $term_id - ); - } - } - - } - - /* - * Add in pagination args. - */ - if ( filter_var( $atts['pagination'], FILTER_VALIDATE_BOOLEAN ) || ( ! filter_var( $atts['pagination'], FILTER_VALIDATE_BOOLEAN ) && $atts['number'] > 0 ) ) { - $args['posts_per_page'] = (int) $atts['number']; - if ( $args['posts_per_page'] < 0 ) { - $args['posts_per_page'] = abs( $args['posts_per_page'] ); - } - } else { - $args['nopaging'] = true; - $args['no_found_rows'] = true; - } - - /* - * Add pagination. - */ - if ( get_query_var( 'paged' ) ) { - $args['paged'] = get_query_var( 'paged' ); - } elseif ( get_query_var( 'page' ) ) { - $args['paged'] = get_query_var( 'page' ); - } else { - $args['paged'] = 1; - } - - /* - * Let other plugins manipulate these arguments. - */ - $args = apply_filters( 'novelist/shortcode/book/query-args', $args, $atts ); - - $books = new WP_Query( $args ); - - if ( $books->have_posts() ) { - - $class = 'novelist-book-columns-' . intval( $atts['columns'] ); - ob_start(); ?> -
- have_posts() ) : $books->the_post(); ?> -
- -
- -
- - get_permalink() . '%#%', - 'format' => '?paged=%#%', - 'current' => max( 1, $args['paged'] ), - 'total' => $books->max_num_pages - ), $atts, $books, $args ) ); - } else { - $big = 999999; - $search_for = array( $big, '#038;' ); - $replace_with = array( '%#%', '&' ); - $pagination = paginate_links( apply_filters( 'novelist/shortcode/book/pagination-args', array( - 'base' => str_replace( $search_for, $replace_with, get_pagenum_link( $big ) ), - 'format' => '?paged=%#%', - 'current' => max( 1, $args['paged'] ), - 'total' => $books->max_num_pages - ), $atts, $books, $args ) ); - } - - if ( ! empty( $pagination ) ) { - ?> - - make($atts, $content); } /** @@ -407,6 +40,8 @@ public function books( $atts, $content = null ) { * `series-name` * `series-exclude` * `standalones` + * + * @deprecated 2.0 * * @param array $atts Shortcode attributes * @param null $content Content @@ -415,130 +50,17 @@ public function books( $atts, $content = null ) { * @return string */ public function series_grid( $atts, $content = null ) { + _deprecated_function(__CLASS__.'::'.__METHOD__, '2.0', \Novelist\Shortcodes\SeriesGridShortcode::class); - $atts = shortcode_atts( array( - 'series-name' => 'true', - // Whether or not to show the name of the series before the grid. - 'exclude' => '', - // Comma-separated list of series IDs or names to exclude. - 'standalones' => 'true', - // Whether or not to show standalones at the end. - 'columns' => 4, - // Number of columns in each row. - 'title' => 'false', - // Whether or not to display the title. - 'series-number' => 'false', - // Whether or not to display the number in the series. - 'excerpt' => 'false', - // Excerpt from the synopsis. - 'full-content' => 'false', - // Fully formatted book content. - 'full-synopsis' => 'false', - // Fully synopsis - 'button' => 'true', - // Button to single book page. Text in here becomes the button text. - 'covers' => 'true', - // Whether or not to display book cover. - 'covers-align' => 'center', - // Cover alignment. - 'orderby' => 'series_number', - // How to order the results. Options: `name`, `title`, `random`, `publication`, `date`, `menu_order`, `series_number` - 'order' => 'ASC', - // Order of the results. - 'offset' => 0 - // Number of books to skip - ), $atts, 'novelist-series-grid' ); - - // No pagination because WTF. - $atts['pagination'] = 'false'; - $atts['number'] = - 1; - - $series_args = array( - 'taxonomy' => 'novelist-series', - ); - $excluded_ids = array(); - - $excluded_series = explode( ',', $atts['exclude'] ); - - foreach ( $excluded_series as $name ) { - if ( is_numeric( $name ) ) { - $excluded_ids[] = $name; - } else { - $term = get_term_by( 'name', trim( $name ), 'novelist-series' ); - - if ( ! $term ) { - continue; - } - - $excluded_ids[] = $term->term_id; - } - } - - if ( count( $excluded_ids ) ) { - $series_args['exclude'] = $excluded_ids; - } - - $all_series = get_terms( apply_filters( 'novelist/shortcode/series-grid/get-terms-args', $series_args ) ); - $output = ''; - - /* - * Display each series. - */ - if ( $all_series && is_array( $all_series ) ) { - - foreach ( $all_series as $series ) { - - // Manually add some attributes. - $this_series_atts = $atts; - $this_series_atts['series'] = $series->term_id; - - $output .= '
'; - - // Display the series name - if ( filter_var( $atts['series-name'], FILTER_VALIDATE_BOOLEAN ) ) { - $output .= apply_filters( 'novelist/shortcode/series-grid/series-name', '

' . esc_html( $series->name ) . '

' ); - } - - // Main grid shortcode. - $output .= $this->books( $this_series_atts ); - - $output .= '
'; - - } - - } - - /* - * Display standalones. - */ - if ( filter_var( $atts['standalones'], FILTER_VALIDATE_BOOLEAN ) ) { - $this_series_atts = $atts; - $this_series_atts['series'] = 'none'; - $standalone_grid = $this->books( $this_series_atts ); - - if ( ! empty( $standalone_grid ) ) { - $output .= '
'; - - // Display the series name - if ( filter_var( $atts['series-name'], FILTER_VALIDATE_BOOLEAN ) ) { - $output .= apply_filters( 'novelist/shortcode/series-grid/standalone-series-name', '

' . __( 'Standalones', 'novelist' ) . '

' ); - } - - // Main grid shortcode. - $output .= $standalone_grid; - - $output .= '
'; - } - } - - return $output; - + return novelist(\Novelist\Shortcodes\SeriesGridShortcode::class)->make($atts, $content); } /** * Show Book * * Displays the fully rendered book information for a single book. + * + * @deprecated 2.0 * * @param array $atts Shortcode attributes * @param null $content Content @@ -548,48 +70,9 @@ public function series_grid( $atts, $content = null ) { * @return string */ public function show_book( $atts, $content = null ) { + _deprecated_function(__CLASS__.'::'.__METHOD__, '2.0', \Novelist\Shortcodes\ShowBookShortcode::class); - $atts = shortcode_atts( array( - 'title' => null, // Title of the book - 'id' => null, // ID of the book - ), $atts, 'show-book' ); - - $args = array( - 'post_type' => 'book', - 'posts_per_page' => 1, - ); - - if ( ! empty( $atts['id'] ) ) { - $args['p'] = intval( $atts['id'] ); - } elseif ( ! empty( $atts['title'] ) ) { - $args['s'] = strip_tags( $atts['title'] ); - } - - $book_query = new WP_Query( apply_filters( 'novelist/shortcode/show-book/query-args', $args ) ); - - // No results - bail. - if ( ! $book_query->have_posts() ) { - return __( '[show-book] Error: No book found with this title.', 'novelist' ); - } - - ob_start(); - - while ( $book_query->have_posts() ) : $book_query->the_post(); - - do_action( 'novelist/shortcode/show-book/before-content', get_the_ID(), $atts ); - - novelist_get_template_part( 'book', 'content' ); - - do_action( 'novelist/shortcode/show-book/after-content', get_the_ID(), $atts ); - - endwhile; - - wp_reset_postdata(); - - return ob_get_clean(); - + return novelist(\Novelist\Shortcodes\ShowBookShortcode::class)->make($atts, $content); } } - -new Novelist_Shortcodes(); diff --git a/src/Helpers/Html.php b/src/Helpers/Html.php new file mode 100644 index 0000000..eca1f78 --- /dev/null +++ b/src/Helpers/Html.php @@ -0,0 +1,233 @@ + 'novelist_select_series', + 'id' => 'novelist_select_series', + 'class' => '', + 'multiple' => false, + 'selected' => 0, + 'number' => 30, + 'standalones' => __('Standalones', 'novelist'), + 'show_option_all' => __('All Series', 'novelist'), + 'term_args' => array( + 'taxonomy' => 'novelist-series' + ) + ); + + $args = wp_parse_args($args, $defaults); + $options = array(); + + $series = get_terms($args['term_args']); + + if ($series) { + foreach ($series as $term) { + $options[$term->term_id] = $term->name; + } + } + + if ($args['standalones']) { + $options['none'] = $args['standalones']; + } + + $this->select(array( + 'name' => $args['name'], + 'selected' => $args['selected'], + 'id' => $args['id'], + 'class' => $args['class'], + 'options' => $options, + 'multiple' => $args['multiple'], + 'show_option_all' => $args['show_option_all'], + 'show_option_none' => false, + )); + } + + /** + * Genre Dropdown + * + * @param array $args Arguments to override the defaults. + * + * @access public + * @since 1.1.0 + * @return void + */ + public function genre_dropdown(array $args = array()): void + { + $defaults = array( + 'name' => 'novelist_genre_dropdown', + 'id' => 'novelist_genre_dropdown', + 'class' => '', + 'multiple' => false, + 'selected' => 0, + 'number' => 30, + 'show_option_all' => __('All Genres', 'novelist'), + 'term_args' => array( + 'taxonomy' => 'novelist-genre' + ) + ); + + $args = wp_parse_args($args, $defaults); + $options = array(); + + $series = get_terms($args['term_args']); + + if ($series) { + foreach ($series as $term) { + $options[$term->term_id] = $term->name; + } + } + + $this->select(array( + 'name' => $args['name'], + 'selected' => $args['selected'], + 'id' => $args['id'], + 'class' => $args['class'], + 'options' => $options, + 'multiple' => $args['multiple'], + 'show_option_all' => $args['show_option_all'], + 'show_option_none' => false, + )); + } + + /** + * Render Text Field + * + * @param array $args Arguments to override the defaults. + * + * @access public + * @since 1.1.0 + * @return void + */ + public function text(array $args = array()): void + { + $defaults = array( + 'id' => '', + 'name' => '', + 'type' => 'text', + 'value' => '', + 'placeholder' => '', + 'class' => '', + 'disabled' => false + ); + + $args = wp_parse_args($args, $defaults); + + $class = implode(' ', array_map('sanitize_html_class', explode(' ', $args['class']))); + $disabled = ''; + if ($args['disabled']) { + $disabled = ' disabled="disabled"'; + } + ?> + > + array(), + 'name' => '', + 'class' => '', + 'id' => '', + 'selected' => 0, + 'placeholder' => '', + 'multiple' => false, + 'show_option_all' => '', + 'show_option_none' => '' + ); + + $args = wp_parse_args($args, $defaults); + + if ($args['multiple']) { + $multiple = ' MULTIPLE'; + } else { + $multiple = ''; + } + + $class = implode(' ', array_map('sanitize_html_class', explode(' ', $args['class']))); + ?> + + bind(\Novelist_Roles::class); novelist()->alias(\Novelist_Roles::class, 'roles'); - novelist()->bind(\Novelist_HTML::class); - novelist()->alias(\Novelist_HTML::class, 'html'); + novelist()->bind(Html::class); + novelist()->alias(Html::class, 'html'); } } diff --git a/src/ServiceProviders/ShortcodeServiceProvider.php b/src/ServiceProviders/ShortcodeServiceProvider.php new file mode 100644 index 0000000..fca1cd3 --- /dev/null +++ b/src/ServiceProviders/ShortcodeServiceProvider.php @@ -0,0 +1,38 @@ +shortcodes as $shortcode) { + if (is_subclass_of($shortcode, Shortcodes\Shortcode::class)) { + /** @var Shortcodes\Shortcode $shortcode */ + $shortcode = novelist()->make($shortcode); + add_shortcode($shortcode::tag(), [$shortcode, 'make']); + } + } + } +} diff --git a/src/Shortcodes/BookGridShortcode.php b/src/Shortcodes/BookGridShortcode.php new file mode 100644 index 0000000..c3d23c2 --- /dev/null +++ b/src/Shortcodes/BookGridShortcode.php @@ -0,0 +1,389 @@ + '', + // Comma-separated list of series IDs or names to display results from. Or "none" to only get standalones. + 'genre' => '', + // Comma-separated list of genre IDs or names to display results from. + 'relation' => 'OR', + // Relation between taxonomy term queries. + 'columns' => 4, + // Number of columns in each row. + 'title' => 'false', + // Whether or not to display the title. + 'series-number' => 'false', + // Whether or not to display the number in the series. + 'excerpt' => 'false', + // Excerpt from the synopsis. + 'full-content' => 'false', + // Fully formatted book content. + 'full-synopsis' => 'false', + // Fully synopsis + 'button' => 'true', + // Button to single book page. Text in here becomes the button text. + 'covers' => 'true', + // Whether or not to display book cover. + 'covers-align' => 'center', + // Cover alignment. + 'orderby' => 'menu_order', + // How to order the results. Options: `name`, `title`, `random`, `publication`, `date`, `menu_order`, `series_number` + 'order' => 'ASC', + // Order of the results. + 'pagination' => 'true', + // Whether or not to include pagination. + 'number' => '12', + // Number of results per page. + 'offset' => 0, + // Number of books to skip + 'display' => '', + 'ids' => '' + // Specific book IDs + ), $atts, 'novelist-books'); + + if (! empty($atts['ids'])) { + + $book_ids = explode(',', $atts['ids']); + $book_ids = array_map('absint', $book_ids); + + $args = array( + 'post_type' => 'book', + 'posts_per_page' => -1, + 'post__in' => $book_ids + ); + + } else { + + /* + * Start building our query args. + */ + $args = array( + 'post_type' => 'book', + 'orderby' => $atts['orderby'], + 'order' => $atts['order'], + 'meta_query' => array( + array( + 'key' => 'novelist_hide', + 'compare' => 'NOT EXISTS' + ) + ) + ); + + /* + * Add in offset args. + */ + if ($atts['offset'] > 0) { + $args['offset'] = absint($atts['offset']); + } + + /* + * Catch out invalid orderby dates. + */ + switch ($atts['orderby']) { + + case 'name' : + // nothing here intentionally + // use below value + + case 'title' : + $args['orderby'] = 'title'; + break; + + case 'rand' : + // nothing here intentionally + // use below value + + case 'random' : + $args['orderby'] = 'rand'; + break; + + case 'publication' : + $args['orderby'] = 'meta_value_num'; + $args['meta_key'] = 'novelist_pub_date_timestamp'; + break; + + case 'series_number' : + $args['orderby'] = 'meta_value_num'; + $args['meta_key'] = 'novelist_series'; + break; + + case 'date' : + $args['orderby'] = 'date'; + break; + + default : + $args['orderby'] = 'menu_order title'; + break; + + } + + /* + * Add taxonomy relation. + */ + if ($atts['series'] || $atts['genre']) { + $args['tax_query'] = array( + 'relation' => $atts['relation'] + ); + } + + /* + * Filter by series. + */ + if ($atts['series']) { + if ($atts['series'] == 'none') { + $get_terms_args = array( + 'taxonomy' => 'novelist-series', + 'hide_empty' => false, + 'fields' => 'ids' + ); + $all_series = get_terms($get_terms_args); + + $args['tax_query'][] = array( + 'taxonomy' => 'novelist-series', + 'field' => 'term_id', + 'terms' => $all_series, + 'operator' => 'NOT IN' + ); + + // We need to unset this to prevent funky errors. + if (array_key_exists('meta_key', $args) && $args['meta_key'] == 'novelist_series') { + unset($args['meta_key']); + $args['orderby'] = 'title'; + } + + } else { + + $series_names = explode(',', $atts['series']); + + foreach ($series_names as $name) { + if (is_numeric($name)) { + $term_id = $name; + } else { + $term = get_term_by('name', trim($name), 'novelist-series'); + + if (! $term) { + continue; + } + + $term_id = $term->term_id; + } + + $args['tax_query'][] = array( + 'taxonomy' => 'novelist-series', + 'field' => 'term_id', + 'terms' => $term_id + ); + } + + } + } + + /* + * Filter by genre. + */ + if ($atts['genre']) { + $genre_names = explode(',', $atts['genre']); + + foreach ($genre_names as $name) { + if (is_numeric($name)) { + $term_id = $name; + } else { + $term = get_term_by('name', trim($name), 'novelist-genre'); + + if (! $term) { + continue; + } + + $term_id = $term->term_id; + } + + $args['tax_query'][] = array( + 'taxonomy' => 'novelist-genre', + 'field' => 'term_id', + 'terms' => $term_id + ); + } + } + + } + + /* + * Add in pagination args. + */ + if ( + filter_var($atts['pagination'], FILTER_VALIDATE_BOOLEAN) || + (! filter_var($atts['pagination'], FILTER_VALIDATE_BOOLEAN) && $atts['number'] > 0) + ) { + $args['posts_per_page'] = (int) $atts['number']; + if ($args['posts_per_page'] < 0) { + $args['posts_per_page'] = abs($args['posts_per_page']); + } + } else { + $args['nopaging'] = true; + $args['no_found_rows'] = true; + } + + /* + * Add pagination. + */ + if (get_query_var('paged')) { + $args['paged'] = get_query_var('paged'); + } elseif (get_query_var('page')) { + $args['paged'] = get_query_var('page'); + } else { + $args['paged'] = 1; + } + + /* + * Let other plugins manipulate these arguments. + */ + $args = apply_filters('novelist/shortcode/book/query-args', $args, $atts); + + $books = new \WP_Query($args); + + if ($books->have_posts()) { + + $class = 'novelist-book-columns-'.intval($atts['columns']); + ob_start(); ?> +
+ have_posts()) : $books->the_post(); ?> +
+ +
+ +
+ + get_permalink().'%#%', + 'format' => '?paged=%#%', + 'current' => max(1, $args['paged']), + 'total' => $books->max_num_pages + ), $atts, $books, $args)); + } else { + $big = 999999; + $search_for = array($big, '#038;'); + $replace_with = array('%#%', '&'); + $pagination = paginate_links(apply_filters('novelist/shortcode/book/pagination-args', array( + 'base' => str_replace($search_for, $replace_with, get_pagenum_link($big)), + 'format' => '?paged=%#%', + 'current' => max(1, $args['paged']), + 'total' => $books->max_num_pages + ), $atts, $books, $args)); + } + + if (! empty($pagination)) { + ?> + + 'true', + // Whether or not to show the name of the series before the grid. + 'exclude' => '', + // Comma-separated list of series IDs or names to exclude. + 'standalones' => 'true', + // Whether or not to show standalones at the end. + 'columns' => 4, + // Number of columns in each row. + 'title' => 'false', + // Whether or not to display the title. + 'series-number' => 'false', + // Whether or not to display the number in the series. + 'excerpt' => 'false', + // Excerpt from the synopsis. + 'full-content' => 'false', + // Fully formatted book content. + 'full-synopsis' => 'false', + // Fully synopsis + 'button' => 'true', + // Button to single book page. Text in here becomes the button text. + 'covers' => 'true', + // Whether or not to display book cover. + 'covers-align' => 'center', + // Cover alignment. + 'orderby' => 'series_number', + // How to order the results. Options: `name`, `title`, `random`, `publication`, `date`, `menu_order`, `series_number` + 'order' => 'ASC', + // Order of the results. + 'offset' => 0 + // Number of books to skip + ), $atts, 'novelist-series-grid'); + + // No pagination because WTF. + $atts['pagination'] = 'false'; + $atts['number'] = -1; + + $series_args = array( + 'taxonomy' => 'novelist-series', + ); + $excluded_ids = array(); + + $excluded_series = explode(',', $atts['exclude']); + + foreach ($excluded_series as $name) { + if (is_numeric($name)) { + $excluded_ids[] = $name; + } else { + $term = get_term_by('name', trim($name), 'novelist-series'); + + if (! $term) { + continue; + } + + $excluded_ids[] = $term->term_id; + } + } + + if (count($excluded_ids)) { + $series_args['exclude'] = $excluded_ids; + } + + $all_series = get_terms(apply_filters('novelist/shortcode/series-grid/get-terms-args', $series_args)); + $output = ''; + + /* + * Display each series. + */ + if ($all_series && is_array($all_series)) { + + foreach ($all_series as $series) { + + // Manually add some attributes. + $this_series_atts = $atts; + $this_series_atts['series'] = $series->term_id; + + $output .= '
'; + + // Display the series name + if (filter_var($atts['series-name'], FILTER_VALIDATE_BOOLEAN)) { + $output .= apply_filters( + 'novelist/shortcode/series-grid/series-name', + '

'.esc_html($series->name).'

' + ); + } + + // Main grid shortcode. + $output .= novelist(BookGridShortcode::class)->make($this_series_atts); + + $output .= '
'; + + } + + } + + /* + * Display standalones. + */ + if (filter_var($atts['standalones'], FILTER_VALIDATE_BOOLEAN)) { + $this_series_atts = $atts; + $this_series_atts['series'] = 'none'; + $standalone_grid = novelist(BookGridShortcode::class)->make($this_series_atts); + + if (! empty($standalone_grid)) { + $output .= '
'; + + // Display the series name + if (filter_var($atts['series-name'], FILTER_VALIDATE_BOOLEAN)) { + $output .= apply_filters( + 'novelist/shortcode/series-grid/standalone-series-name', + '

'.__('Standalones', 'novelist').'

' + ); + } + + // Main grid shortcode. + $output .= $standalone_grid; + + $output .= '
'; + } + } + + return $output; + } +} diff --git a/src/Shortcodes/Shortcode.php b/src/Shortcodes/Shortcode.php new file mode 100644 index 0000000..94536cc --- /dev/null +++ b/src/Shortcodes/Shortcode.php @@ -0,0 +1,37 @@ + null, // Title of the book + 'id' => null, // ID of the book + ), $atts, 'show-book'); + + $args = array( + 'post_type' => 'book', + 'posts_per_page' => 1, + ); + + if (! empty($atts['id'])) { + $args['p'] = intval($atts['id']); + } elseif (! empty($atts['title'])) { + $args['s'] = strip_tags($atts['title']); + } + + $book_query = new \WP_Query(apply_filters('novelist/shortcode/show-book/query-args', $args)); + + // No results - bail. + if (! $book_query->have_posts()) { + return __('[show-book] Error: No book found with this title.', 'novelist'); + } + + ob_start(); + + while ($book_query->have_posts()) : $book_query->the_post(); + + do_action('novelist/shortcode/show-book/before-content', get_the_ID(), $atts); + + novelist_get_template_part('book', 'content'); + + do_action('novelist/shortcode/show-book/after-content', get_the_ID(), $atts); + + endwhile; + + wp_reset_postdata(); + + return ob_get_clean(); + } +} From 3ce42c5f91dc5aac22071cbed7cd2144b4d65c95 Mon Sep 17 00:00:00 2001 From: Ashley Gibson Date: Sun, 21 Nov 2021 14:20:59 +0000 Subject: [PATCH 3/3] Refactor widgets. --- includes/widgets/widget-book.php | 342 -------------- includes/widgets/widget-books-by-series.php | 228 --------- includes/widgets/widget-word-count.php | 205 -------- src/Plugin.php | 1 + .../LegacyServiceProvider.php | 10 +- .../WidgetServiceProvider.php | 37 ++ src/Widgets/BookWidget.php | 445 ++++++++++++++++++ src/Widgets/BooksBySeriesWidget.php | 236 ++++++++++ src/Widgets/WordCountWidget.php | 219 +++++++++ 9 files changed, 945 insertions(+), 778 deletions(-) delete mode 100644 includes/widgets/widget-book.php delete mode 100644 includes/widgets/widget-books-by-series.php delete mode 100644 includes/widgets/widget-word-count.php create mode 100644 src/ServiceProviders/WidgetServiceProvider.php create mode 100644 src/Widgets/BookWidget.php create mode 100644 src/Widgets/BooksBySeriesWidget.php create mode 100644 src/Widgets/WordCountWidget.php diff --git a/includes/widgets/widget-book.php b/includes/widgets/widget-book.php deleted file mode 100644 index 70536e6..0000000 --- a/includes/widgets/widget-book.php +++ /dev/null @@ -1,342 +0,0 @@ - __( 'Showcase one of your books.', 'novelist' ) ) - ); - - } - - /** - * Front-end display of the widget. - * - * @see WP_Widget::widget() - * - * @param array $args Widget arguments - * @param array $instance Saved widget settings - * - * @access public - * @since 1.0.0 - * @return void - */ - public function widget( $args, $instance ) { - - echo $args['before_widget']; - - if ( ! empty( $instance['title'] ) ) { - echo $args['before_title'] . apply_filters( 'widget_title', $instance['title'] ) . $args['after_title']; - } - - do_action( 'novelist/widget/book/before-widget', $args, $instance ); - - $template = novelist_get_template_part( 'widgets/widget', 'book', false ); - - if ( $template ) { - include $template; - } - - do_action( 'novelist/widget/book/after-widget', $args, $instance ); - - echo $args['after_widget']; - - } - - /** - * Back-end widget form. - * - * @see WP_Widget::form() - * - * @param array $instance Previously saved values from database. - * - * @access public - * @since 1.0.0 - * @return void - */ - public function form( $instance ) { - - $title = ! empty( $instance['title'] ) ? $instance['title'] : ''; - $book_id = ! empty( $instance['book'] ) ? $instance['book'] : ''; - $show_book_cover = ! empty( $instance['show_book_cover'] ) ? $instance['show_book_cover'] : false; - $book_cover_size = ! empty( $instance['book_cover_size'] ) ? $instance['book_cover_size'] : 'medium'; - $book_cover_align = ! empty( $instance['book_cover_align'] ) ? $instance['book_cover_align'] : 'center'; - $book_cover_width = ! empty( $instance['book_cover_width'] ) ? intval( $instance['book_cover_width'] ) : ''; - $book_cover_height = ! empty( $instance['book_cover_height'] ) ? intval( $instance['book_cover_height'] ) : ''; - $show_synopsis = ! empty( $instance['show_synopsis'] ) ? $instance['show_synopsis'] : false; - $show_link = ! empty( $instance['show_link'] ) ? $instance['show_link'] : false; - $link_text = ! empty( $instance['link_text'] ) ? $instance['link_text'] : ''; - $text_before = ! empty( $instance['text_before'] ) ? $instance['text_before'] : ''; - $text_after = ! empty( $instance['text_after'] ) ? $instance['text_after'] : ''; - - $args = array( - 'orderby' => 'title', - 'order' => 'ASC', - 'post_type' => 'book', - 'post_status' => 'any', - 'posts_per_page' => 500, - 'no_found_rows' => true, - 'update_post_meta_cache' => false, - 'update_post_term_cache' => false - ); - $book_query = new WP_Query( apply_filters( 'novelist/widget/book/book-query-args', $args ) ); - ?> -

- - -

- -

- - have_posts() ) : ?> - - -
add one?', 'novelist' ), novelist_get_label_plural(), esc_url( admin_url( '/post-new.php?post_type=book' ) ) ); ?> - - - -

- -
- -

- -

- > - -

- -
- - - - - - - - - -
- - - - - -
- - - - - -
-
- -
- -

- > - -

- -

- > - -

- - - -
- -

- -

- - -

- -

- - -

- get_alignments() ) ) ? strip_tags( $new_instance['book_cover_align'] ) : 'center'; - $instance['book_cover_width'] = ( ! empty( $new_instance['book_cover_width'] ) && is_numeric( $new_instance['book_cover_width'] ) ) ? intval( $new_instance['book_cover_width'] ) : ''; - $instance['book_cover_height'] = ( ! empty( $new_instance['book_cover_height'] ) && is_numeric( $new_instance['book_cover_height'] ) ) ? intval( $new_instance['book_cover_height'] ) : ''; - $instance['show_synopsis'] = ( ! empty( $new_instance['show_synopsis'] ) ) ? 'on' : false; - $instance['show_link'] = ( ! empty( $new_instance['show_link'] ) ) ? 'on' : false; - $instance['link_text'] = ( ! empty( $new_instance['link_text'] ) ) ? wp_kses( $new_instance['link_text'], $this->get_allowed_html() ) : ''; - $instance['text_before'] = ( ! empty( $new_instance['text_before'] ) ) ? wp_kses_post( $new_instance['text_before'] ) : ''; - $instance['text_after'] = ( ! empty( $new_instance['text_after'] ) ) ? wp_kses_post( $new_instance['text_after'] ) : ''; - - do_action( 'novelist/widget/book/update', $instance, $new_instance, $old_instance ); - - return $instance; - - } - - /** - * Get Image Sizes - * - * @deprecated - * - * @access public - * @since 1.0.0 - * @return array - */ - public function get_image_sizes() { - - global $_wp_additional_image_sizes; - - $sizes = array(); - - foreach ( get_intermediate_image_sizes() as $_size ) { - if ( in_array( $_size, array( 'thumbnail', 'medium', 'medium_large', 'large' ) ) ) { - $sizes[ $_size ]['width'] = get_option( "{$_size}_size_w" ); - $sizes[ $_size ]['height'] = get_option( "{$_size}_size_h" ); - $sizes[ $_size ]['crop'] = (bool) get_option( "{$_size}_crop" ); - } elseif ( isset( $_wp_additional_image_sizes[ $_size ] ) ) { - $sizes[ $_size ] = array( - 'width' => $_wp_additional_image_sizes[ $_size ]['width'], - 'height' => $_wp_additional_image_sizes[ $_size ]['height'], - 'crop' => $_wp_additional_image_sizes[ $_size ]['crop'], - ); - } - } - - $sizes['full'] = array(); - $sizes['custom'] = array(); - - return apply_filters( 'novelist/widget/book/image-sizes', $sizes ); - - } - - /** - * Get Image Alignments - * - * @access public - * @since 1.0.0 - * @return array - */ - public function get_alignments() { - - $alignments = array( - 'alignleft' => __( 'Left', 'novelist' ), - 'aligncenter' => __( 'Center', 'novelist' ), - 'alignright' => __( 'Right', 'novelist' ) - ); - - return apply_filters( 'novelist/widget/book/image-alignments', $alignments ); - - } - - /** - * Get Allowed HTML - * - * (For the link text) - * - * @access public - * @since 1.0.0 - * @return array - */ - public function get_allowed_html() { - - $allowed_html = array( - 'span' => array() - ); - - return apply_filters( 'novelist/widget/book/allowed-link-text-html', $allowed_html ); - - } - - /** - * Get Chosen Image Size - * - * Returns the chosen image size, to be used in wp_get_attachment_image() - * Will either be a string value of one of the pre-defined sizes, or - * an array of width and height values (in that order). - * - * @param array $instance - * - * @access public - * @since 1.0.0 - * @return array|string - */ - public function get_chosen_size( $instance ) { - $size = $instance['book_cover_size']; - - if ( $size != 'custom' ) { - return strip_tags( $size ); - } - - $width = $instance['book_cover_width']; - $height = $instance['book_cover_height']; - - return array( $width, $height ); - } - -} - -add_action( 'widgets_init', function () { - register_widget( 'Novelist_Book_Widget' ); -} ); \ No newline at end of file diff --git a/includes/widgets/widget-books-by-series.php b/includes/widgets/widget-books-by-series.php deleted file mode 100644 index 60aaf29..0000000 --- a/includes/widgets/widget-books-by-series.php +++ /dev/null @@ -1,228 +0,0 @@ - __( 'List all of the books in a series.', 'novelist' ) ) - ); - - } - - /** - * Front-end display of the widget. - * - * @see WP_Widget::widget() - * - * @param array $args Widget arguments - * @param array $instance Saved widget settings - * - * @access public - * @since 1.0.0 - * @return void - */ - public function widget( $args, $instance ) { - - echo $args['before_widget']; - - if ( ! empty( $instance['title'] ) ) { - echo $args['before_title'] . apply_filters( 'widget_title', $instance['title'] ) . $args['after_title']; - } - - do_action( 'novelist/widget/books-by-series/before-widget', $args, $instance ); - - if ( ! array_key_exists( 'series', $instance ) || $instance['series'] === '' ) { - $instance['series'] = 0; - } - - if ( $instance['text_before'] ) { - echo wpautop( $instance['text_before'] ); - } - - if ( $instance['series'] || $instance['series'] === 0 ) { - - // Build the query args. - $query_args = array( - 'post_type' => 'book', - 'posts_per_page' => - 1, - 'nopaging' => true, - 'orderby' => 'meta_value_num', - 'meta_key' => 'novelist_series', - 'order' => 'ASC' - ); - - if ( is_numeric( $instance['series'] ) && $instance['series'] > 0 ) { - $query_args['tax_query'] = array( - array( - 'taxonomy' => 'novelist-series', - 'field' => 'id', - 'terms' => array( $instance['series'] ) - ) - ); - } elseif ( (int) $instance['series'] === - 1 ) { - $all_series = get_terms( 'novelist-series', array( 'fields' => 'ids' ) ); - - $query_args['tax_query'] = array( - array( - 'taxonomy' => 'novelist-series', - 'field' => 'id', - 'terms' => $all_series, - 'operator' => 'NOT IN' - ) - ); - } - - // Query for books. - $books = new WP_Query( apply_filters( 'novelist/widget/books-by-series/query-args', $query_args ) ); - - if ( $books->have_posts() ) { - - ?> -
    - - have_posts() ) : $books->the_post(); - - $book_obj = new Novelist_Book( get_the_ID() ); - $book_title = $book_obj->get_title(); - $series_name = novelist_get_formatted_series( get_the_ID() ); - - // Append the series name. - if ( $series_name ) { - $book_title = sprintf( '%s (%s)', $book_title, $series_name ); - } - - $final_html = '
  • ' . $book_title . '
  • '; - - echo apply_filters( 'novelist/widget/books-by-series/book-entry', $final_html, $book_obj, $book_title, $series_name ); - - endwhile; - wp_reset_postdata(); - ?> - -
- -

- - -

- -

- - __( 'All Series', 'novelist' ), - 'show_option_none' => __( 'Standalones', 'novelist' ), - 'orderby' => 'NAME', - 'hide_empty' => false, - 'name' => $this->get_field_name( 'series' ), - 'id' => $this->get_field_id( 'series' ), - 'class' => 'widefat', - 'selected' => $series, - 'taxonomy' => 'novelist-series', - ); - wp_dropdown_categories( $args ); - ?> -

- -
- -

- -

- - -

- -

- - -

- __( 'Keep track of your progress towards a word count goal.', 'novelist' ) ) - ); - - } - - /** - * Front-end display of the widget. - * - * @see WP_Widget::widget() - * - * @param array $args Widget arguments - * @param array $instance Saved widget settings - * - * @access public - * @since 1.0.0 - * @return void - */ - public function widget( $args, $instance ) { - - echo $args['before_widget']; - - if ( ! empty( $instance['title'] ) ) { - echo $args['before_title'] . apply_filters( 'widget_title', $instance['title'] ) . $args['after_title']; - } - - do_action( 'novelist/widget/word-count/before-widget' ); - - $template = novelist_get_template_part( 'widgets/widget', 'word-count', false ); - - if ( $template ) { - include $template; - } - - do_action( 'novelist/widget/word-count/after-widget' ); - - echo $args['after_widget']; - - } - - /** - * Back-end widget form. - * - * @see WP_Widget::form() - * - * @param array $instance Previously saved values from database. - * - * @access public - * @since 1.0.0 - * @return void - */ - public function form( $instance ) { - - $instance = wp_parse_args( (array) $instance, array( - 'title' => __( 'Word Count', 'novelist' ), - 'goal' => '', - 'current' => '', - 'color' => '#333333', - 'label' => '[current] / [goal]', - 'text_before' => '', - 'text_after' => '' - ) ); - - $title = ! empty( $instance['title'] ) ? $instance['title'] : ''; - $goal = ( ! empty( $instance['goal'] ) && is_numeric( $instance['goal'] ) ) ? $instance['goal'] : ''; - $current = ( ! empty( $instance['current'] ) && is_numeric( $instance['current'] ) ) ? $instance['current'] : 0; - $color = ! empty( $instance['color'] ) ? $instance['color'] : '#333333'; - $label = ! empty( $instance['label'] ) ? $instance['label'] : ''; - $text_before = ! empty( $instance['text_before'] ) ? $instance['text_before'] : ''; - $text_after = ! empty( $instance['text_after'] ) ? $instance['text_after'] : ''; - - do_action( 'novelist/widget/word-count/before-form' ); - - ?> - - -

- - -

- -

- - -

- -

- - -

- -

- -
- -

- -
- -

-
- [current] as a placeholder for your current word count, and [goal] as a placeholder for your goal.', 'novelist'); ?>
- -

- -
- -

- -

- - -

- -

- - -

- sanitize_hex_color( $new_instance['color'] ) : '#333333'; - $instance['label'] = ( ! empty( $new_instance['label'] ) ) ? strip_tags( $new_instance['label'] ) : ''; - $instance['text_before'] = ( ! empty( $new_instance['text_before'] ) ) ? wp_kses_post( $new_instance['text_before'] ) : ''; - $instance['text_after'] = ( ! empty( $new_instance['text_after'] ) ) ? wp_kses_post( $new_instance['text_after'] ) : ''; - - do_action( 'novelist/widget/word-count/update', $instance, $new_instance, $old_instance ); - - return $instance; - - } - - /** - * Sanitize Hex Color - * - * @param string $color - * - * @access public - * @since 1.0.0 - * @return string - */ - public function sanitize_hex_color( $color ) { - if ( ! empty( $color ) && preg_match( '|^#([A-Fa-f0-9]{3}){1,2}$|', $color ) ) { - return $color; - } - - return ''; - } - -} - -add_action( 'widgets_init', function () { - register_widget( 'Novelist_Word_Count_Widget' ); -} ); \ No newline at end of file diff --git a/src/Plugin.php b/src/Plugin.php index 07d76f3..705e577 100644 --- a/src/Plugin.php +++ b/src/Plugin.php @@ -32,6 +32,7 @@ class Plugin private $serviceProviders = [ ServiceProviders\LegacyServiceProvider::class, ServiceProviders\ShortcodeServiceProvider::class, + ServiceProviders\WidgetServiceProvider::class, ]; /** @var bool */ diff --git a/src/ServiceProviders/LegacyServiceProvider.php b/src/ServiceProviders/LegacyServiceProvider.php index 519d353..78eb63a 100644 --- a/src/ServiceProviders/LegacyServiceProvider.php +++ b/src/ServiceProviders/LegacyServiceProvider.php @@ -11,6 +11,9 @@ use Novelist\Helpers\Html; use Novelist\Plugin; +use Novelist\Widgets\BooksBySeriesWidget; +use Novelist\Widgets\BookWidget; +use Novelist\Widgets\WordCountWidget; class LegacyServiceProvider implements ServiceProvider { @@ -46,9 +49,6 @@ private function includeFiles(): void require_once NOVELIST_PLUGIN_DIR.'includes/load-assets.php'; require_once NOVELIST_PLUGIN_DIR.'includes/misc-functions.php'; require_once NOVELIST_PLUGIN_DIR.'includes/template-functions.php'; - require_once NOVELIST_PLUGIN_DIR.'includes/widgets/widget-book.php'; - require_once NOVELIST_PLUGIN_DIR.'includes/widgets/widget-books-by-series.php'; - require_once NOVELIST_PLUGIN_DIR.'includes/widgets/widget-word-count.php'; if (is_admin()) { require_once NOVELIST_PLUGIN_DIR.'includes/admin/admin-actions.php'; @@ -73,6 +73,10 @@ private function registerClassAliases(): void { class_alias(Plugin::class, 'Novelist'); class_alias(Html::class, 'Novelist_HTML'); + + class_alias(BookWidget::class, 'Novelist_Book_Widget'); + class_alias(BooksBySeriesWidget::class, 'Novelist_Books_By_Series'); + class_alias(WordCountWidget::class, 'Novelist_Word_Count_Widget'); } /** diff --git a/src/ServiceProviders/WidgetServiceProvider.php b/src/ServiceProviders/WidgetServiceProvider.php new file mode 100644 index 0000000..44271ed --- /dev/null +++ b/src/ServiceProviders/WidgetServiceProvider.php @@ -0,0 +1,37 @@ +widgets as $widget) { + register_widget($widget); + } + }); + } +} diff --git a/src/Widgets/BookWidget.php b/src/Widgets/BookWidget.php new file mode 100644 index 0000000..af6afc6 --- /dev/null +++ b/src/Widgets/BookWidget.php @@ -0,0 +1,445 @@ + __('Showcase one of your books.', 'novelist')) + ); + } + + /** + * Front-end display of the widget. + * + * @see WP_Widget::widget() + * + * @param array $args Widget arguments + * @param array $instance Saved widget settings + * + * @access public + * @since 1.0.0 + * @return void + */ + public function widget($args, $instance) + { + echo $args['before_widget']; + + if (! empty($instance['title'])) { + echo $args['before_title'].apply_filters('widget_title', $instance['title']).$args['after_title']; + } + + do_action('novelist/widget/book/before-widget', $args, $instance); + + $template = novelist_get_template_part('widgets/widget', 'book', false); + + if ($template) { + include $template; + } + + do_action('novelist/widget/book/after-widget', $args, $instance); + + echo $args['after_widget']; + } + + /** + * Back-end widget form. + * + * @see WP_Widget::form() + * + * @param array $instance Previously saved values from database. + * + * @access public + * @since 1.0.0 + * @return void + */ + public function form($instance) + { + $title = ! empty($instance['title']) ? $instance['title'] : ''; + $book_id = ! empty($instance['book']) ? $instance['book'] : ''; + $show_book_cover = ! empty($instance['show_book_cover']) ? $instance['show_book_cover'] : false; + $book_cover_size = ! empty($instance['book_cover_size']) ? $instance['book_cover_size'] : 'medium'; + $book_cover_align = ! empty($instance['book_cover_align']) ? $instance['book_cover_align'] : 'center'; + $book_cover_width = ! empty($instance['book_cover_width']) ? intval($instance['book_cover_width']) : ''; + $book_cover_height = ! empty($instance['book_cover_height']) ? intval($instance['book_cover_height']) : ''; + $show_synopsis = ! empty($instance['show_synopsis']) ? $instance['show_synopsis'] : false; + $show_link = ! empty($instance['show_link']) ? $instance['show_link'] : false; + $link_text = ! empty($instance['link_text']) ? $instance['link_text'] : ''; + $text_before = ! empty($instance['text_before']) ? $instance['text_before'] : ''; + $text_after = ! empty($instance['text_after']) ? $instance['text_after'] : ''; + + $args = array( + 'orderby' => 'title', + 'order' => 'ASC', + 'post_type' => 'book', + 'post_status' => 'any', + 'posts_per_page' => 500, + 'no_found_rows' => true, + 'update_post_meta_cache' => false, + 'update_post_term_cache' => false, + ); + $book_query = new WP_Query(apply_filters('novelist/widget/book/book-query-args', $args)); + ?> +

+ + +

+ +

+ + have_posts()) : ?> + + +
+ add one?', 'novelist'), + novelist_get_label_plural(), + esc_url(admin_url('/post-new.php?post_type=book')) + ); + ?> + + + +

+ +
+ +

+ +

+ + > + +

+ +
+ + + + + + + + + +
+ + + + + +
+ + + + + +
+
+ +
+ +

+ + > + +

+ +

+ + > + +

+ + + +
+ +

+ +

+ + +

+ +

+ + +

+ get_alignments())) ? strip_tags($new_instance['book_cover_align']) : 'center'; + $instance['book_cover_width'] = (! empty($new_instance['book_cover_width']) && is_numeric($new_instance['book_cover_width'])) ? intval($new_instance['book_cover_width']) : ''; + $instance['book_cover_height'] = (! empty($new_instance['book_cover_height']) && is_numeric($new_instance['book_cover_height'])) ? intval($new_instance['book_cover_height']) : ''; + $instance['show_synopsis'] = (! empty($new_instance['show_synopsis'])) ? 'on' : false; + $instance['show_link'] = (! empty($new_instance['show_link'])) ? 'on' : false; + $instance['link_text'] = (! empty($new_instance['link_text'])) ? wp_kses($new_instance['link_text'], + $this->get_allowed_html()) : ''; + $instance['text_before'] = (! empty($new_instance['text_before'])) ? wp_kses_post($new_instance['text_before']) : ''; + $instance['text_after'] = (! empty($new_instance['text_after'])) ? wp_kses_post($new_instance['text_after']) : ''; + + do_action('novelist/widget/book/update', $instance, $new_instance, $old_instance); + + return $instance; + } + + /** + * Get Image Sizes + * + * @deprecated + * + * @access public + * @since 1.0.0 + * @return array + */ + public function get_image_sizes(): array + { + global $_wp_additional_image_sizes; + + $sizes = array(); + + foreach (get_intermediate_image_sizes() as $_size) { + if (in_array($_size, array('thumbnail', 'medium', 'medium_large', 'large'))) { + $sizes[$_size]['width'] = get_option("{$_size}_size_w"); + $sizes[$_size]['height'] = get_option("{$_size}_size_h"); + $sizes[$_size]['crop'] = (bool) get_option("{$_size}_crop"); + } elseif (isset($_wp_additional_image_sizes[$_size])) { + $sizes[$_size] = array( + 'width' => $_wp_additional_image_sizes[$_size]['width'], + 'height' => $_wp_additional_image_sizes[$_size]['height'], + 'crop' => $_wp_additional_image_sizes[$_size]['crop'], + ); + } + } + + $sizes['full'] = array(); + $sizes['custom'] = array(); + + return (array) apply_filters('novelist/widget/book/image-sizes', $sizes); + } + + /** + * Get Image Alignments + * + * @access public + * @since 1.0.0 + * @return array + */ + public function get_alignments(): array + { + $alignments = array( + 'alignleft' => __('Left', 'novelist'), + 'aligncenter' => __('Center', 'novelist'), + 'alignright' => __('Right', 'novelist') + ); + + return (array) apply_filters('novelist/widget/book/image-alignments', $alignments); + } + + /** + * Get Allowed HTML + * + * (For the link text) + * + * @access public + * @since 1.0.0 + * @return array + */ + public function get_allowed_html(): array + { + $allowed_html = array( + 'span' => array() + ); + + return (array) apply_filters('novelist/widget/book/allowed-link-text-html', $allowed_html); + } + + /** + * Get Chosen Image Size + * + * Returns the chosen image size, to be used in wp_get_attachment_image() + * Will either be a string value of one of the pre-defined sizes, or + * an array of width and height values (in that order). + * + * @param array $instance + * + * @access public + * @since 1.0.0 + * @return array|string + */ + public function get_chosen_size($instance) + { + $size = $instance['book_cover_size']; + + if ($size != 'custom') { + return strip_tags($size); + } + + $width = $instance['book_cover_width']; + $height = $instance['book_cover_height']; + + return array($width, $height); + } + +} diff --git a/src/Widgets/BooksBySeriesWidget.php b/src/Widgets/BooksBySeriesWidget.php new file mode 100644 index 0000000..1c4d1b3 --- /dev/null +++ b/src/Widgets/BooksBySeriesWidget.php @@ -0,0 +1,236 @@ + __('List all of the books in a series.', 'novelist')) + ); + } + + /** + * Front-end display of the widget. + * + * @see WP_Widget::widget() + * + * @param array $args Widget arguments + * @param array $instance Saved widget settings + * + * @access public + * @since 1.0.0 + * @return void + */ + public function widget($args, $instance) + { + echo $args['before_widget']; + + if (! empty($instance['title'])) { + echo $args['before_title'].apply_filters('widget_title', $instance['title']).$args['after_title']; + } + + do_action('novelist/widget/books-by-series/before-widget', $args, $instance); + + if (! array_key_exists('series', $instance) || $instance['series'] === '') { + $instance['series'] = 0; + } + + if ($instance['text_before']) { + echo wpautop($instance['text_before']); + } + + if ($instance['series'] || $instance['series'] === 0) { + + // Build the query args. + $query_args = array( + 'post_type' => 'book', + 'posts_per_page' => -1, + 'nopaging' => true, + 'orderby' => 'meta_value_num', + 'meta_key' => 'novelist_series', + 'order' => 'ASC' + ); + + if (is_numeric($instance['series']) && $instance['series'] > 0) { + $query_args['tax_query'] = array( + array( + 'taxonomy' => 'novelist-series', + 'field' => 'id', + 'terms' => array($instance['series']) + ) + ); + } elseif ((int) $instance['series'] === -1) { + $all_series = get_terms('novelist-series', array('fields' => 'ids')); + + $query_args['tax_query'] = array( + array( + 'taxonomy' => 'novelist-series', + 'field' => 'id', + 'terms' => $all_series, + 'operator' => 'NOT IN' + ) + ); + } + + // Query for books. + $books = new WP_Query(apply_filters('novelist/widget/books-by-series/query-args', $query_args)); + + if ($books->have_posts()) { + ?> +
    + + have_posts()) : $books->the_post(); + + $book_obj = new Novelist_Book(get_the_ID()); + $book_title = $book_obj->get_title(); + $series_name = novelist_get_formatted_series(get_the_ID()); + + // Append the series name. + if ($series_name) { + $book_title = sprintf('%s (%s)', $book_title, $series_name); + } + + $final_html = '
  • '.$book_title.'
  • '; + + echo apply_filters( + 'novelist/widget/books-by-series/book-entry', + $final_html, + $book_obj, + $book_title, + $series_name + ); + + endwhile; + wp_reset_postdata(); + ?> +
+ +

+ + +

+ +

+ + __('All Series', 'novelist'), + 'show_option_none' => __('Standalones', 'novelist'), + 'orderby' => 'NAME', + 'hide_empty' => false, + 'name' => $this->get_field_name('series'), + 'id' => $this->get_field_id('series'), + 'class' => 'widefat', + 'selected' => $series, + 'taxonomy' => 'novelist-series', + ); + wp_dropdown_categories($args); + ?> +

+ +
+ +

+ +

+ + +

+ +

+ + +

+ __('Keep track of your progress towards a word count goal.', 'novelist')) + ); + } + + /** + * Front-end display of the widget. + * + * @see WP_Widget::widget() + * + * @param array $args Widget arguments + * @param array $instance Saved widget settings + * + * @access public + * @since 1.0.0 + * @return void + */ + public function widget($args, $instance) + { + echo $args['before_widget']; + + if (! empty($instance['title'])) { + echo $args['before_title'].apply_filters('widget_title', $instance['title']).$args['after_title']; + } + + do_action('novelist/widget/word-count/before-widget'); + + $template = novelist_get_template_part('widgets/widget', 'word-count', false); + + if ($template) { + include $template; + } + + do_action('novelist/widget/word-count/after-widget'); + + echo $args['after_widget']; + } + + /** + * Back-end widget form. + * + * @see WP_Widget::form() + * + * @param array $instance Previously saved values from database. + * + * @access public + * @since 1.0.0 + * @return void + */ + public function form($instance) + { + $instance = wp_parse_args((array) $instance, array( + 'title' => __('Word Count', 'novelist'), + 'goal' => '', + 'current' => '', + 'color' => '#333333', + 'label' => '[current] / [goal]', + 'text_before' => '', + 'text_after' => '' + )); + + $title = ! empty($instance['title']) ? $instance['title'] : ''; + $goal = (! empty($instance['goal']) && is_numeric($instance['goal'])) ? $instance['goal'] : ''; + $current = (! empty($instance['current']) && is_numeric($instance['current'])) ? $instance['current'] : 0; + $color = ! empty($instance['color']) ? $instance['color'] : '#333333'; + $label = ! empty($instance['label']) ? $instance['label'] : ''; + $text_before = ! empty($instance['text_before']) ? $instance['text_before'] : ''; + $text_after = ! empty($instance['text_after']) ? $instance['text_after'] : ''; + + do_action('novelist/widget/word-count/before-form'); + + ?> + + +

+ + +

+ +

+ + +

+ +

+ + +

+ +

+ +
+ +

+ +
+ +

+
+ [current] as a placeholder for your current word count, and [goal] as a placeholder for your goal.', + 'novelist' + ), + ['mark' => []] + ); + ?> +
+ +

+ +
+ +

+ +

+ + +

+ +

+ + +

+ sanitize_hex_color($new_instance['color']) : '#333333'; + $instance['label'] = (! empty($new_instance['label'])) ? strip_tags($new_instance['label']) : ''; + $instance['text_before'] = (! empty($new_instance['text_before'])) ? wp_kses_post($new_instance['text_before']) : ''; + $instance['text_after'] = (! empty($new_instance['text_after'])) ? wp_kses_post($new_instance['text_after']) : ''; + + do_action('novelist/widget/word-count/update', $instance, $new_instance, $old_instance); + + return $instance; + } + + /** + * Sanitize Hex Color + * + * @param string $color + * + * @access public + * @since 1.0.0 + * @return string + */ + public function sanitize_hex_color($color): string + { + if (! empty($color) && preg_match('|^#([A-Fa-f0-9]{3}){1,2}$|', $color)) { + return $color; + } + + return ''; + } + +}