From 01fbea72606f7c7b68e0620db55c251a2a3e8719 Mon Sep 17 00:00:00 2001 From: Brede Basualdo Date: Thu, 20 Mar 2025 15:26:47 -0300 Subject: [PATCH 01/11] - Added support to ask yes or no and returns a string to be used directly in stubs - Better code for CPT, so now generates automatically the default name, plural, slug based on name - Added a function to avoid create a post_type name larger than 20 characters (as described here: https://developer.wordpress.org/reference/functions/register_post_type/ ) - Added support to set showInRest and showUI directly supports in CPT - Added a default icon for the stub of CPT, so we avoid the empty space --- src/Console/bin/bones | 113 +++++++++++++++++++++++++++++++++++-- src/Console/stubs/cpt.stub | 10 +++- 2 files changed, 114 insertions(+), 9 deletions(-) diff --git a/src/Console/bin/bones b/src/Console/bin/bones index 109da76..794f747 100644 --- a/src/Console/bin/bones +++ b/src/Console/bin/bones @@ -589,6 +589,33 @@ namespace Bones\Traits { return $line ?: $default; } + /** + * Get input from console + * + * @param string $str The question to ask + * @param string|null $default The default value + */ + protected function askYesNo(string $str, bool $default = true): string + { + + $str = WPBONES_COLOR_GREEN . + "❓ $str (".($default?"Y/n":"y/N").")" . + " (default: ".($default?"Yes":"No")."): " . + WPBONES_COLOR_RESET; + + // Use readline to get the user input + $line = readline($str); + + // Trim the input to remove extra spaces or newlines + $line = strtoupper(trim($line)); + if($line=="Y"||$line=="YES"){ + return "true"; + }else if($line=="N"||$line=="NO"){ + return "false"; + }else{ + return $default?"true":"false"; + } + } } } @@ -2508,6 +2535,47 @@ namespace Bones { $this->line(' Created plugin/Console/Kernel.php'); } } + protected function simplePluralize($word) { + $strLength = strlen($word); + $last = strtolower($word[$strLength - 1]); + $secondLast = $strLength > 1 ? strtolower($word[$strLength - 2]) : ''; + + // Special cases (irregulars) + $irregular = [ + 'child' => 'children', + 'man' => 'men', + 'woman' => 'women', + 'tooth' => 'teeth', + 'foot' => 'feet', + 'mouse' => 'mice', + 'goose' => 'geese', + 'person' => 'people', + ]; + + if (array_key_exists(strtolower($word), $irregular)) { + return $irregular[strtolower($word)]; + } + + // common words + if ($last === 's' || $last === 'x' || $last === 'z' || ($secondLast . $last) === 'ch' || ($secondLast . $last) === 'sh') { + return $word . 'es'; + } + + if ($last === 'y' && !in_array($secondLast, ['a', 'e', 'i', 'o', 'u'])) { + return substr($word, 0, $strLength - 1) . 'ies'; + } + + if ($last === 'f' || ($secondLast . $last) === 'fe') { + $base = substr($word, 0, $strLength - ($last === 'f' ? 1 : 2)); + return $base . 'ves'; + } + + if ($last === 'o' && in_array($secondLast, ['a', 'e', 'i', 'o', 'u']) === false) { + return $word . 'es'; + } + + return $word . 's'; + } /** * Create a Custom Post Type controller @@ -2526,16 +2594,27 @@ namespace Bones { $className = $this->askClassNameIfEmpty($className); $filename = sprintf('%s.php', $className); - // current plugin name and namespace [$pluginName, $namespace] = $this->getPluginNameAndNamespace(); + $className = ucfirst($this->sanitize($className)); + $name = ($className); + $plural = ucfirst($this->simplePluralize($className)); + $slug = $this->slugify($namespace,$plural); + + $id = $this->ask('Enter a ID', $slug); + $name = $this->ask('Enter the name',$name); + $plural = $this->ask('Enter the plural name',$plural); - $slug = str_replace('-', '_', $this->sanitize($pluginName)); + $showInRest = true; + $showInRest = $this->askYesNo('Show in Rest?',$showInRest); - $id = $this->ask('Enter a ID', $slug); - $name = $this->ask('Enter the name'); - $plural = $this->ask('Enter the plural name'); + $showUI = true; + $showUI = $this->askYesNo('Show in admin sidebar?',$showUI); + $supports = 'title, editor, thumbnail, excerpt'; + $supports = $this->ask('Features available for the CPT',$supports); + $itemsSupport = $this->stringToArray($supports); + if (empty($id)) { $id = $slug; } @@ -2547,6 +2626,9 @@ namespace Bones { '{ID}' => $id, '{Name}' => $name, '{Plural}' => $plural, + '{ShowInRest}' => $showInRest, + '{showUI}' => $showUI, + '{Supports}' => "'".join("','",$itemsSupport)."'", ]); // Create the folder if it doesn't exist @@ -2559,9 +2641,28 @@ namespace Bones { $this->line(" Created plugin/CustomPostTypes/{$filename}"); $this->info( - "Remember to add {$className} in the config/plugin.php array in the 'custom_post_types' key." + "Remember to add {$namespace}\CustomPostTypes\\{$className} in the config/plugin.php array in the 'custom_post_types' key." ); } + private function stringToArray($content){ + $itemsSupport=[] ; + foreach(explode(",",$content) as $item) + { + if(!empty($item)) + $itemsSupport[]=$item; + } + return $itemsSupport; + } + private function slugify($namespace,$name,$remove=0){ + + $subNamespace = substr($namespace,0,20-$remove-strlen($name)-1); + $slug = strtolower($subNamespace."_".str_replace('-', '_', $name)); + $length = strlen($slug); + if($length>20){ + return $this->slugify($namespace,$name,$length-20); + } + return $slug; + } /** * Create a Shortcode controller diff --git a/src/Console/stubs/cpt.stub b/src/Console/stubs/cpt.stub index 7e26f03..0812131 100644 --- a/src/Console/stubs/cpt.stub +++ b/src/Console/stubs/cpt.stub @@ -11,9 +11,13 @@ use {Namespace}\WPBones\Foundation\WordPressCustomPostTypeServiceProvider as Ser class {ClassName} extends ServiceProvider { - protected $id = '{ID}'; - protected $name = '{Name}'; - protected $plural = '{Plural}'; + protected $id = '{ID}'; + protected $name = '{Name}'; + protected $plural = '{Plural}'; + protected $showInRest = {ShowInRest}; + protected $showUI = {showUI}; + protected $supports = [{Supports}]; + protected $menuIcon = "dashicons-admin-post"; /** * You may override this method in order to register your own actions and filters. From a6ba2fd243c86afdbf6190428adc36e0fca7e574 Mon Sep 17 00:00:00 2001 From: Brede Basualdo Date: Thu, 20 Mar 2025 15:35:10 -0300 Subject: [PATCH 02/11] fix to avoid loop the menu -> items when is empty --- src/Routing/AdminMenuProvider.php | 127 +++++++++++++++--------------- 1 file changed, 64 insertions(+), 63 deletions(-) diff --git a/src/Routing/AdminMenuProvider.php b/src/Routing/AdminMenuProvider.php index b7b7668..7f37964 100644 --- a/src/Routing/AdminMenuProvider.php +++ b/src/Routing/AdminMenuProvider.php @@ -64,69 +64,70 @@ public function register() } else { $firstMenu = false; } - - foreach ($menu['items'] as $key => $subMenu) { - if (is_null($subMenu)) { - continue; - } - - // index 0 - if (empty($key)) { - $key = '0'; - } - - // sanitize array keys - $subMenu['capability'] = isset($subMenu['capability']) ? $subMenu['capability'] : $menu['capability']; - $subMenu['page_title'] = isset($subMenu['page_title']) ? $subMenu['page_title'] : $subMenu['menu_title']; - - // key could be a number - $key = str_replace('-', '_', sanitize_title($key)); - - $array = explode('\\', __NAMESPACE__); - $namespace = sanitize_title($array[0]); - - // submenu slug - $submenuSlug = "{$namespace}_{$key}"; - - if ($firstMenu) { - $firstMenu = false; - $submenuSlug = $topLevelSlug; - } - - // get hook - $hook = $this->plugin->getCallableHook($subMenu['route']); - - $subMenuHook = add_submenu_page( - $topLevelSlug, - $subMenu['page_title'], - $subMenu['menu_title'], - $subMenu['capability'], - $submenuSlug, - $hook - ); - - if (isset($subMenu['route']['load'])) { - [$controller, $method] = Str::parseCallback($subMenu['route']['load']); - - add_action("load-{$subMenuHook}", function () use ($controller, $method) { - $className = "WPKirk\\Http\\Controllers\\{$controller}"; - $instance = new $className(); - - return $instance->{$method}(); - }); - } - - if (isset($subMenu['route']['resource'])) { - $controller = $subMenu['route']['resource']; - - add_action("load-{$subMenuHook}", function () use ($controller) { - $className = "WPKirk\\Http\\Controllers\\{$controller}"; - $instance = new $className(); - if (method_exists($instance, 'load')) { - return $instance->load(); - } - }); - } + if(!empty($menu['items'])){ + foreach ($menu['items'] as $key => $subMenu) { + if (is_null($subMenu)) { + continue; + } + + // index 0 + if (empty($key)) { + $key = '0'; + } + + // sanitize array keys + $subMenu['capability'] = isset($subMenu['capability']) ? $subMenu['capability'] : $menu['capability']; + $subMenu['page_title'] = isset($subMenu['page_title']) ? $subMenu['page_title'] : $subMenu['menu_title']; + + // key could be a number + $key = str_replace('-', '_', sanitize_title($key)); + + $array = explode('\\', __NAMESPACE__); + $namespace = sanitize_title($array[0]); + + // submenu slug + $submenuSlug = "{$namespace}_{$key}"; + + if ($firstMenu) { + $firstMenu = false; + $submenuSlug = $topLevelSlug; + } + + // get hook + $hook = $this->plugin->getCallableHook($subMenu['route']); + + $subMenuHook = add_submenu_page( + $topLevelSlug, + $subMenu['page_title'], + $subMenu['menu_title'], + $subMenu['capability'], + $submenuSlug, + $hook + ); + + if (isset($subMenu['route']['load'])) { + [$controller, $method] = Str::parseCallback($subMenu['route']['load']); + + add_action("load-{$subMenuHook}", function () use ($controller, $method) { + $className = "WPKirk\\Http\\Controllers\\{$controller}"; + $instance = new $className(); + + return $instance->{$method}(); + }); + } + + if (isset($subMenu['route']['resource'])) { + $controller = $subMenu['route']['resource']; + + add_action("load-{$subMenuHook}", function () use ($controller) { + $className = "WPKirk\\Http\\Controllers\\{$controller}"; + $instance = new $className(); + if (method_exists($instance, 'load')) { + return $instance->load(); + } + }); + } + } } } } From ed6a8e0bfe1dd9eb80a7cb7ffe772fd03a9c8f63 Mon Sep 17 00:00:00 2001 From: Brede Basualdo Date: Thu, 20 Mar 2025 17:12:06 -0300 Subject: [PATCH 03/11] - Added support to show an example on ask method - Added support to pre-fill the data on CTT - Allow multiple Post Type support to CTT - Added support to set showui by default on CTT --- src/Console/bin/bones | 89 ++++++++++++++++++++------------------ src/Console/stubs/ctt.stub | 3 +- 2 files changed, 49 insertions(+), 43 deletions(-) diff --git a/src/Console/bin/bones b/src/Console/bin/bones index 794f747..219c0f6 100644 --- a/src/Console/bin/bones +++ b/src/Console/bin/bones @@ -573,11 +573,12 @@ namespace Bones\Traits { * @param string $str The question to ask * @param string|null $default The default value */ - protected function ask(string $str, ?string $default = ''): string + protected function ask(string $str, ?string $default = '',?string $example = ''): string { $str = WPBONES_COLOR_GREEN . "❓ $str" . + (empty($example) ? '' : " ie: {$example}") . (empty($default) ? ': ' : " (default: {$default}): ") . WPBONES_COLOR_RESET; @@ -2591,28 +2592,26 @@ namespace Bones { } // ask className if empty - $className = $this->askClassNameIfEmpty($className); - - $filename = sprintf('%s.php', $className); + $className = $this->askClassNameIfEmpty($className); + $filename = sprintf('%s.php', $className); // current plugin name and namespace [$pluginName, $namespace] = $this->getPluginNameAndNamespace(); - $className = ucfirst($this->sanitize($className)); - $name = ($className); - $plural = ucfirst($this->simplePluralize($className)); - $slug = $this->slugify($namespace,$plural); - - $id = $this->ask('Enter a ID', $slug); - $name = $this->ask('Enter the name',$name); - $plural = $this->ask('Enter the plural name',$plural); + $className = ucfirst($this->sanitize($className)); + $name = ($className); + $plural = ucfirst($this->simplePluralize($className)); + $slug = $this->slugify($namespace,$plural); + $id = $this->ask('Enter a ID', $slug); + $name = $this->ask('Enter the name',$name); + $plural = $this->ask('Enter the plural name',$plural); $showInRest = true; $showInRest = $this->askYesNo('Show in Rest?',$showInRest); - $showUI = true; - $showUI = $this->askYesNo('Show in admin sidebar?',$showUI); + $showUI = true; + $showUI = $this->askYesNo('Show in admin sidebar?',$showUI); - $supports = 'title, editor, thumbnail, excerpt'; - $supports = $this->ask('Features available for the CPT',$supports); + $supports = 'title, editor, thumbnail, excerpt'; + $supports = $this->ask('Features available for the CPT',$supports); $itemsSupport = $this->stringToArray($supports); if (empty($id)) { @@ -2621,14 +2620,14 @@ namespace Bones { // stubbing $content = $this->prepareStub('cpt', [ - '{Namespace}' => $namespace, - '{ClassName}' => $className, - '{ID}' => $id, - '{Name}' => $name, - '{Plural}' => $plural, - '{ShowInRest}' => $showInRest, - '{showUI}' => $showUI, - '{Supports}' => "'".join("','",$itemsSupport)."'", + '{Namespace}' => $namespace, + '{ClassName}' => $className, + '{ID}' => $id, + '{Name}' => $name, + '{Plural}' => $plural, + '{ShowInRest}' => $showInRest, + '{showUI}' => $showUI, + '{Supports}' => "'".join("','",$itemsSupport)."'", ]); // Create the folder if it doesn't exist @@ -2641,7 +2640,7 @@ namespace Bones { $this->line(" Created plugin/CustomPostTypes/{$filename}"); $this->info( - "Remember to add {$namespace}\CustomPostTypes\\{$className} in the config/plugin.php array in the 'custom_post_types' key." + "Remember to add \\{$namespace}\CustomPostTypes\\{$className} in the config/plugin.php array in the 'custom_post_types' key." ); } private function stringToArray($content){ @@ -2656,7 +2655,7 @@ namespace Bones { private function slugify($namespace,$name,$remove=0){ $subNamespace = substr($namespace,0,20-$remove-strlen($name)-1); - $slug = strtolower($subNamespace."_".str_replace('-', '_', $name)); + $slug = strtolower($subNamespace.(empty($namespace)?"":"_").str_replace('-', '_', $name)); $length = strlen($slug); if($length>20){ return $this->slugify($namespace,$name,$length-20); @@ -2840,25 +2839,30 @@ namespace Bones { } // ask className if empty - $className = $this->askClassNameIfEmpty($className); - - $filename = sprintf('%s.php', $className); + $className = $this->askClassNameIfEmpty($className); + $filename = sprintf('%s.php', $className); // current plugin name and namespace - $namespace = $this->getNamespace(); - - $slug = $this->getPluginId(); - - $id = $this->ask('Enter a ID', $slug); - $name = $this->ask('Enter the name'); - $plural = $this->ask('Enter the plural name'); - + $namespace = $this->getNamespace(); + // current plugin name and namespace + $className = ucfirst($this->sanitize($className)); + $name = $className; + $plural = ucfirst($this->simplePluralize($className)); + $slug = $this->slugify("",$plural); + $id = $this->ask('Enter a ID', $slug); + $name = $this->ask('Enter the name',$name); + $plural = $this->ask('Enter the plural name',$plural); + $showInRest = true; + $showInRest = $this->askYesNo('Show in Rest?',$showInRest); + $this->line( 'The object type below refers to the id of your previous Custom Post Type' ); - - $objectType = $this->ask('Enter the object type to bound'); - + $objectType ="post"; + $example ="post, your_custom_post_type, etc"; + $objectType = $this->ask('Enter the object type to bound',$objectType,$example); + $objectType = $this->stringToArray($objectType); + if (empty($id)) { $id = $slug; } @@ -2869,8 +2873,9 @@ namespace Bones { '{ClassName}' => $className, '{ID}' => $id, '{Name}' => $name, + '{ShowInRest}' => $showInRest, '{Plural}' => $plural, - '{ObjectType}' => $objectType, + '{ObjectType}' => "'".join("','",$objectType)."'", ]); // Create the folder if it doesn't exist @@ -2883,7 +2888,7 @@ namespace Bones { $this->line(" Created plugin/CustomTaxonomyTypes/{$filename}"); $this->info( - "Remember to add {$className} in the config/plugin.php array in the 'custom_taxonomy_types' key." + "Remember to add \\{$namespace}\CustomTaxonomyTypes\\{$className} in the config/plugin.php array in the 'custom_taxonomy_types' key." ); } diff --git a/src/Console/stubs/ctt.stub b/src/Console/stubs/ctt.stub index 9432842..45ea2ba 100644 --- a/src/Console/stubs/ctt.stub +++ b/src/Console/stubs/ctt.stub @@ -14,7 +14,8 @@ class {ClassName} extends ServiceProvider protected $id = '{ID}'; protected $name = '{Name}'; protected $plural = '{Plural}'; - protected $objectType = '{ObjectType}'; + protected $showInRest = {ShowInRest}; + protected $objectType = [{ObjectType}]; /** * You may override this method in order to register your own actions and filters. From ac55a8602841fd501021a754e43756f64f127fdb Mon Sep 17 00:00:00 2001 From: Brede Basualdo Date: Thu, 20 Mar 2025 18:00:34 -0300 Subject: [PATCH 04/11] Added support to manage CPT trash event. --- .../WordPressCustomPostTypeServiceProvider.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Foundation/WordPressCustomPostTypeServiceProvider.php b/src/Foundation/WordPressCustomPostTypeServiceProvider.php index 9fc0ddb..f50a247 100644 --- a/src/Foundation/WordPressCustomPostTypeServiceProvider.php +++ b/src/Foundation/WordPressCustomPostTypeServiceProvider.php @@ -422,6 +422,7 @@ public function register() $this->boot(); // Register custom post type + register_post_type($this->id, $this->optionalArgs()); $this->initHooks(); @@ -619,6 +620,7 @@ protected function initHooks() // Hook save post add_action('save_post_' . $this->id, [$this, 'save_post'], 10, 2); + add_action('trash_' . $this->id, [$this, 'trash_post'], 10, 3); // Manage columns @since 1.9.0 add_filter("manage_{$this->id}_posts_columns", [$this, '_manage_posts_columns']); @@ -917,6 +919,18 @@ public function save_post($post_id, $post = null) } } + /** + * Override this method when your post was trashed. + * This method is called by hook action trashed_{post_type}` + * + * @param int|string $post_id Post ID + * @param object $post Optional. Post object + * + */ + public function trash_post($post_id, $post, $old_status ){ + + } + /** * Override this method to save/update your custom data. * This method is called by hook action save_post_{post_type}` From e47f394b8805c9fd886bf2b3808e542576da54be Mon Sep 17 00:00:00 2001 From: Brede Basualdo Date: Mon, 24 Mar 2025 20:47:26 -0300 Subject: [PATCH 05/11] add support for columns on Custom Taxonomy Type --- ...PressCustomTaxonomyTypeServiceProvider.php | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/src/Foundation/WordPressCustomTaxonomyTypeServiceProvider.php b/src/Foundation/WordPressCustomTaxonomyTypeServiceProvider.php index 7ed6e13..eef29ae 100644 --- a/src/Foundation/WordPressCustomTaxonomyTypeServiceProvider.php +++ b/src/Foundation/WordPressCustomTaxonomyTypeServiceProvider.php @@ -355,6 +355,7 @@ public function register() // Register custom taxonomy register_taxonomy($this->id, $this->objectType, $this->optionalArgs()); + $this->initHooks(); } /** @@ -525,4 +526,58 @@ protected function rewrite(): array return $this->mapPropertiesToArray($mapProperties); } + /** + * Initialize hooks + */ + protected function initHooks() + { + add_action('admin_init', [$this, 'addTaxonomyColumns']); + } + public function addTaxonomyColumns() + { + + add_filter('manage_' . $this->id . '_custom_column', [$this, 'columnContent'], 15, 3); + add_filter('manage_edit-' . $this->id . '_columns', [$this, '_manage_taxonomy_columns']); + } + /** + * Manage columns + * + * @param array $columns + * + * @since 1.9.0 + * @return array + */ + + public function _manage_taxonomy_columns($columns) + { + $newColumns = $this->registerColumns(); + $cb = []; + $cnt = []; + if (isset($columns["cb"])) { + $cb = ["cb"=>$columns["cb"]]; + unset($columns["cb"]); + } + if (isset($columns["posts"])) { + $cnt = ["posts"=>$columns["posts"]]; + unset($columns["posts"]); + } + return array_merge($cb, $columns, $newColumns,$cnt); + } + + + /** + * Return the column content + * + * @param string $string + * @param string $column_name + * @param int $term_id + * + * @since 1.9.0 + * @return string + */ + public function columnContent($string, $column_name, $term_id) + { + // You may override this method + return $string; + } } From 5e6dc105a7288c31ff5e3e3d8fdc19468f34b6aa Mon Sep 17 00:00:00 2001 From: Brede Basualdo Date: Mon, 24 Mar 2025 21:17:39 -0300 Subject: [PATCH 06/11] - fix to use the ClassName in ucfirst as filename - explanation of CTT fix --- src/Console/bin/bones | 2 +- src/Foundation/WordPressCustomTaxonomyTypeServiceProvider.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Console/bin/bones b/src/Console/bin/bones index 219c0f6..0ea6d5d 100644 --- a/src/Console/bin/bones +++ b/src/Console/bin/bones @@ -2593,10 +2593,10 @@ namespace Bones { // ask className if empty $className = $this->askClassNameIfEmpty($className); - $filename = sprintf('%s.php', $className); // current plugin name and namespace [$pluginName, $namespace] = $this->getPluginNameAndNamespace(); $className = ucfirst($this->sanitize($className)); + $filename = sprintf('%s.php', $className); $name = ($className); $plural = ucfirst($this->simplePluralize($className)); $slug = $this->slugify($namespace,$plural); diff --git a/src/Foundation/WordPressCustomTaxonomyTypeServiceProvider.php b/src/Foundation/WordPressCustomTaxonomyTypeServiceProvider.php index eef29ae..dcdf6be 100644 --- a/src/Foundation/WordPressCustomTaxonomyTypeServiceProvider.php +++ b/src/Foundation/WordPressCustomTaxonomyTypeServiceProvider.php @@ -553,6 +553,7 @@ public function _manage_taxonomy_columns($columns) $newColumns = $this->registerColumns(); $cb = []; $cnt = []; + //With this, we keep the cb at first item, and the post count at last. if (isset($columns["cb"])) { $cb = ["cb"=>$columns["cb"]]; unset($columns["cb"]); From 927e08fd9b439ce78495c1fbe9281c8192b2cb85 Mon Sep 17 00:00:00 2001 From: Brede Basualdo Date: Mon, 24 Mar 2025 21:30:18 -0300 Subject: [PATCH 07/11] add try/catch in case of missing classes on src/Foundation/Plugin.php --- src/Foundation/Plugin.php | 50 +++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 15 deletions(-) diff --git a/src/Foundation/Plugin.php b/src/Foundation/Plugin.php index 01a3eda..8f1f3df 100644 --- a/src/Foundation/Plugin.php +++ b/src/Foundation/Plugin.php @@ -265,43 +265,63 @@ public function _init() // Custom post types Service Provider $custom_post_types = $this->config('plugin.custom_post_types', []); foreach ($custom_post_types as $className) { - $object = new $className($this); - $object->register(); - $this->provides[$className] = $object; + try{ + $object = new $className($this); + $object->register(); + $this->provides[$className] = $object; + }catch(\Throwable $e){ + error_log("missing class ".$className); + } } // Custom taxonomy type Service Provider $custom_taxonomy_types = $this->config('plugin.custom_taxonomy_types', []); foreach ($custom_taxonomy_types as $className) { - $object = new $className($this); - $object->register(); - $this->provides[$className] = $object; + try{ + $object = new $className($this); + $object->register(); + $this->provides[$className] = $object; + }catch(\Throwable $e){ + error_log("missing class ".$className); + } } // Shortcodes Service Provider $shortcodes = $this->config('plugin.shortcodes', []); foreach ($shortcodes as $className) { - $object = new $className($this); - $object->register(); - $this->provides[$className] = $object; + try{ + $object = new $className($this); + $object->register(); + $this->provides[$className] = $object; + }catch(\Throwable $e){ + error_log("missing class ".$className); + } } // Ajax Service Provider if ($this->isAjax()) { $ajax = $this->config('plugin.ajax', []); foreach ($ajax as $className) { - $object = new $className($this); - $object->register(); - $this->provides[$className] = $object; + try{ + $object = new $className($this); + $object->register(); + $this->provides[$className] = $object; + }catch(\Throwable $e){ + error_log("missing class ".$className); + } } } // Custom service provider $providers = $this->config('plugin.providers', []); foreach ($providers as $className) { - $object = new $className($this); - $object->register(); - $this->provides[$className] = $object; + try{ + $object = new $className($this); + $object->register(); + $this->provides[$className] = $object; + }catch(\Throwable $e){ + error_log("missing class ".$className); + } } } From f84eef6ba9bdcc04150c0fa35de787cf0d18d00b Mon Sep 17 00:00:00 2001 From: Brede Basualdo Date: Mon, 24 Mar 2025 21:43:21 -0300 Subject: [PATCH 08/11] fix on stringToArray to remove empty spaces --- src/Console/bin/bones | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/bin/bones b/src/Console/bin/bones index 0ea6d5d..a979562 100644 --- a/src/Console/bin/bones +++ b/src/Console/bin/bones @@ -2648,7 +2648,7 @@ namespace Bones { foreach(explode(",",$content) as $item) { if(!empty($item)) - $itemsSupport[]=$item; + $itemsSupport[]=trim($item); } return $itemsSupport; } From 4282bad5ee7acad9ec0b1db34bc1924d67a6cf6d Mon Sep 17 00:00:00 2001 From: Brede Basualdo Date: Tue, 25 Mar 2025 13:53:45 -0300 Subject: [PATCH 09/11] Self Generated Menus from CTT and extended use of WPBONES_TEXTDOMAIN WIP: Add support for WPBONES_TEXTDOMAIN - Added a Menu Relations to self generated menus from CTT - Added support to make use of WPBONES_TEXTDOMAIN on Admin Menu Provider Important, this could lead to a break on your code, you *need* to add at least ``` defined("WPBONES_TEXTDOMAIN","your_text_domain"); ``` in `config.php` or update your code based on https://github.com/wpbones/WPKirk/pull/32 --- src/Foundation/Plugin.php | 5 + src/Routing/AdminMenuProvider.php | 262 ++++++++++++++++++------------ 2 files changed, 162 insertions(+), 105 deletions(-) diff --git a/src/Foundation/Plugin.php b/src/Foundation/Plugin.php index 8f1f3df..dc79769 100644 --- a/src/Foundation/Plugin.php +++ b/src/Foundation/Plugin.php @@ -43,6 +43,11 @@ class Plugin extends Container implements PluginContract * @var string */ public $slug = ''; + /** + * Array of Menu Relations generated by CTT + * @var array + */ + public $menuRelations= []; /** * Build in __FILE__ relative plugin. diff --git a/src/Routing/AdminMenuProvider.php b/src/Routing/AdminMenuProvider.php index 7f37964..aca6295 100644 --- a/src/Routing/AdminMenuProvider.php +++ b/src/Routing/AdminMenuProvider.php @@ -20,116 +20,168 @@ class AdminMenuProvider extends ServiceProvider { - // register - public function register() - { - $menus = include_once "{$this->plugin->basePath}/config/menus.php"; - - if (!empty($menus) && is_array($menus)) { - foreach ($menus as $topLevelSlug => $menu) { - // sanitize array keys - $menu['position'] = isset($menu['position']) ? $menu['position'] : null; - $menu['capability'] = isset($menu['capability']) ? $menu['capability'] : 'read'; - $menu['icon'] = isset($menu['icon']) ? $menu['icon'] : ''; - $page_title = isset($menu['page_title']) ? $menu['page_title'] : $menu['menu_title']; - $menu['page_title'] = sanitize_title($page_title); - - // icon - $icon = $menu['icon']; - $hasImage = false; - - if (isset($menu['icon']) && !empty($menu['icon']) && 'dashicons' != substr($menu['icon'], 0, 9) && 'data:' != substr($menu['icon'], 0, 5)) { - $icon = $this->plugin->images . '/' . $menu['icon']; - $hasImage = true; - } - - $firstMenu = true; - - if (substr($topLevelSlug, 0, 8) !== 'edit.php') { - $suffix = add_menu_page( - $menu['page_title'], - $menu['menu_title'], - $menu['capability'], - $topLevelSlug, - '', - $icon, - $menu['position'] - ); - - if ($hasImage) { - add_action('admin_head', function () use ($suffix) { - echo ''; - }); - } - } else { - $firstMenu = false; - } - if(!empty($menu['items'])){ - foreach ($menu['items'] as $key => $subMenu) { - if (is_null($subMenu)) { - continue; - } - - // index 0 - if (empty($key)) { - $key = '0'; - } - - // sanitize array keys - $subMenu['capability'] = isset($subMenu['capability']) ? $subMenu['capability'] : $menu['capability']; - $subMenu['page_title'] = isset($subMenu['page_title']) ? $subMenu['page_title'] : $subMenu['menu_title']; - - // key could be a number - $key = str_replace('-', '_', sanitize_title($key)); - - $array = explode('\\', __NAMESPACE__); - $namespace = sanitize_title($array[0]); - - // submenu slug - $submenuSlug = "{$namespace}_{$key}"; - - if ($firstMenu) { - $firstMenu = false; - $submenuSlug = $topLevelSlug; - } - - // get hook - $hook = $this->plugin->getCallableHook($subMenu['route']); - - $subMenuHook = add_submenu_page( - $topLevelSlug, - $subMenu['page_title'], - $subMenu['menu_title'], - $subMenu['capability'], - $submenuSlug, - $hook - ); - - if (isset($subMenu['route']['load'])) { - [$controller, $method] = Str::parseCallback($subMenu['route']['load']); - - add_action("load-{$subMenuHook}", function () use ($controller, $method) { - $className = "WPKirk\\Http\\Controllers\\{$controller}"; - $instance = new $className(); + // this will help us to detect if we move some auto-assigned menu from CTT + /** + * Helper to know if the auto-assigned menu is in the current URL Path + * @var bool + */ + private bool $fromLink = false; + /** + * The Currently Top Level Slug + * @var string + */ + private string $topLevelSlug = ""; + // register + public function register() + { + $menus = include_once "{$this->plugin->basePath}/config/menus.php"; + if (! empty($menus) && is_array($menus)) { + + foreach ($menus as $topLevelSlug => $menu) { + $this->topLevelSlug = $topLevelSlug; + // sanitize array keys + $menu['position'] = isset($menu['position']) ? $menu['position'] : null; + $menu['capability'] = isset($menu['capability']) ? $menu['capability'] : 'read'; + $menu['icon'] = isset($menu['icon']) ? $menu['icon'] : ''; + $page_title = isset($menu['page_title']) ? $menu['page_title'] : $menu['menu_title']; + $menu['page_title'] = sanitize_title($page_title); + + // icon + $icon = $menu['icon']; + $hasImage = false; + + if (isset($menu['icon']) && ! empty($menu['icon']) && 'dashicons' != substr($menu['icon'], 0, 9) && 'data:' != substr($menu['icon'], 0, 5)) { + $icon = $this->plugin->images . '/' . $menu['icon']; + $hasImage = true; + } - return $instance->{$method}(); - }); - } + $firstMenu = true; + + if (substr($this->topLevelSlug, 0, 8) !== 'edit.php') { + $suffix = add_menu_page( + __($menu['page_title'], WPBONES_TEXTDOMAIN), + __($menu['menu_title'], WPBONES_TEXTDOMAIN), + $menu['capability'], + $this->topLevelSlug, + isset($menu["callback"]) ? $menu["callback"] : '', + $icon, + $menu['position'] + ); + + if ($hasImage) { + add_action('admin_head', function () use ($suffix) { + echo ''; + }); + } + } else { + $firstMenu = false; + } - if (isset($subMenu['route']['resource'])) { - $controller = $subMenu['route']['resource']; + if (! empty($menu['items'])) { + foreach ($menu['items'] as $key => $subMenu) { + if (is_null($subMenu)) { + continue; + } + + // index 0 + if (empty($key)) { + $key = '0'; + } + + // sanitize array keys + $subMenu['capability'] = isset($subMenu['capability']) ? $subMenu['capability'] : $menu['capability']; + $subMenu['page_title'] = isset($subMenu['page_title']) ? $subMenu['page_title'] : $subMenu['menu_title']; + + // key could be a number + $key = str_replace('-', '_', sanitize_title($key)); + + $array = explode('\\', __NAMESPACE__); + $namespace = sanitize_title($array[0]); + + // submenu slug + $submenuSlug = "{$namespace}_{$key}"; + + if ($firstMenu) { + $firstMenu = false; + $submenuSlug = $this->topLevelSlug; + } + + // get hook + if (isset($subMenu["route"])) { + $hook = $this->plugin->getCallableHook($subMenu['route']); + } elseif (isset($subMenu["url"])) { + //if the submenu is an url, we remove the hook, and use the submenu url as target url + $submenuSlug = $subMenu["url"]; + $hook = ""; + + } + + $subMenuHook = add_submenu_page( + $this->topLevelSlug, + __($subMenu['page_title'], WPBONES_TEXTDOMAIN), + __($subMenu['menu_title'], WPBONES_TEXTDOMAIN), + $subMenu['capability'], + $submenuSlug, + $hook + ); + + if (isset($subMenu['route']['load'])) { + [$controller, $method] = Str::parseCallback($subMenu['route']['load']); + + add_action("load-{$subMenuHook}", function () use ($controller, $method) { + $className = "WPKirk\\Http\\Controllers\\{$controller}"; + $instance = new $className(); + + return $instance->{$method}(); + }); + } + + if (isset($subMenu['route']['resource'])) { + $controller = $subMenu['route']['resource']; + + add_action("load-{$subMenuHook}", function () use ($controller) { + $className = "WPKirk\\Http\\Controllers\\{$controller}"; + $instance = new $className(); + if (method_exists($instance, 'load')) { + return $instance->load(); + } + }); + } + } + } + // if we had a parent menu with the same name as TopLevelSlug we will add the admin sub menu taken from CTT + if (isset($this->plugin->menuRelations[$this->topLevelSlug]) && ! empty($this->plugin->menuRelations[$this->topLevelSlug])) { + foreach ($this->plugin->menuRelations[$this->topLevelSlug] as $menuInternal) { + if (str_contains($_SERVER['REQUEST_URI'], $menuInternal["url"])) { + $this->fromLink = true; + } + add_submenu_page( + $this->topLevelSlug, + __($menuInternal['name'], WPBONES_TEXTDOMAIN), + __($menuInternal['name'], WPBONES_TEXTDOMAIN), + "read", + $menuInternal["url"], + "" + ); + remove_submenu_page("edit.php", $menuInternal["url"]); + } + + add_filter("parent_file", [$this, "renameParentFile"], 10, 1); - add_action("load-{$subMenuHook}", function () use ($controller) { - $className = "WPKirk\\Http\\Controllers\\{$controller}"; - $instance = new $className(); - if (method_exists($instance, 'load')) { - return $instance->load(); } - }); - } + //if our callback is # we remove the submenu page, with this we avoid the duplicate menu and submenu with the same name + if (isset($menu["callback"]) && $menu["callback"] == "#") { + remove_submenu_page($this->topLevelSlug, $this->topLevelSlug); + } } } - } } - } + public function renameParentFile($parent_file) + { + if ($this->fromLink) { + return $this->topLevelSlug; + } + return $parent_file; + } + } From 43d710ffebf85a5bb3e32f35e50803fbe84c4d67 Mon Sep 17 00:00:00 2001 From: Brede Basualdo Date: Tue, 25 Mar 2025 14:21:36 -0300 Subject: [PATCH 10/11] added missing auto menu generator --- ...PressCustomTaxonomyTypeServiceProvider.php | 1141 +++++++++-------- 1 file changed, 577 insertions(+), 564 deletions(-) diff --git a/src/Foundation/WordPressCustomTaxonomyTypeServiceProvider.php b/src/Foundation/WordPressCustomTaxonomyTypeServiceProvider.php index dcdf6be..a5304a5 100644 --- a/src/Foundation/WordPressCustomTaxonomyTypeServiceProvider.php +++ b/src/Foundation/WordPressCustomTaxonomyTypeServiceProvider.php @@ -5,580 +5,593 @@ use WPKirk\WPBones\Support\ServiceProvider; if (!defined('ABSPATH')) { - exit(); + exit(); } abstract class WordPressCustomTaxonomyTypeServiceProvider extends ServiceProvider { - /** - * Taxonomy key. Must not exceed 32 characters and may only contain - * lowercase alphanumeric characters, dashes, and underscores. See sanitize_key(). - * - * It's the `$taxonomy` parameter used in - * `register_taxonomy( $taxonomy, $object_type, $args = array() )` - * - * @var string - */ - protected $id; - - /** - * Object type or array of object types with which the taxonomy should be associated. - * - * It's the `$object_type` parameter used in - * `register_taxonomy( $taxonomy, $object_type, $args = array() )` - * - * @var string - */ - protected $objectType; - - /** - * Name for one object of this taxonomy. Default 'Tag'/'Category'. - * - * @var string - */ - protected $name; - - /** - * General name for the taxonomy, usually plural. - * The same as and overridden by `$tax->label`. Default 'Tags'/'Categories'. - * - * @var string - */ - protected $plural; - - /** - * Taxonomy labels object. The first default value is for non-hierarchical taxonomies - * (like tags) and the second one is for hierarchical taxonomies (like categories). - * - * @type string $name General name for the taxonomy, usually plural. The same - * as and overridden by `$tax->label`. Default 'Tags'/'Categories'. - * @type string $singular_name Name for one object of this taxonomy. Default 'Tag'/'Category'. - * @type string $search_items Default 'Search Tags'/'Search Categories'. - * @type string $popular_items This label is only used for non-hierarchical taxonomies. - * Default 'Popular Tags'. - * @type string $all_items Default 'All Tags'/'All Categories'. - * @type string $parent_item This label is only used for hierarchical taxonomies. Default - * 'Parent Category'. - * @type string $parent_item_colon The same as `parent_item`, but with colon `:` in the end. - * @type string $name_field_description Description for the Name field on Edit Tags screen. - * Default 'The name is how it appears on your site'. - * @type string $slug_field_description Description for the Slug field on Edit Tags screen. - * Default 'The “slug” is the URL-friendly version - * of the name. It is usually all lowercase and contains - * only letters, numbers, and hyphens'. - * @type string $parent_field_description Description for the Parent field on Edit Tags screen. - * Default 'Assign a parent term to create a hierarchy. - * The term Jazz, for example, would be the parent - * of Bebop and Big Band'. - * @type string $desc_field_description Description for the Description field on Edit Tags screen. - * Default 'The description is not prominent by default; - * however, some themes may show it'. - * @type string $edit_item Default 'Edit Tag'/'Edit Category'. - * @type string $view_item Default 'View Tag'/'View Category'. - * @type string $update_item Default 'Update Tag'/'Update Category'. - * @type string $add_new_item Default 'Add New Tag'/'Add New Category'. - * @type string $new_item_name Default 'New Tag Name'/'New Category Name'. - * @type string $separate_items_with_commas This label is only used for non-hierarchical taxonomies. Default - * 'Separate tags with commas', used in the meta box. - * @type string $add_or_remove_items This label is only used for non-hierarchical taxonomies. Default - * 'Add or remove tags', used in the meta box when JavaScript - * is disabled. - * @type string $choose_from_most_used This label is only used on non-hierarchical taxonomies. Default - * 'Choose from the most used tags', used in the meta box. - * @type string $not_found Default 'No tags found'/'No categories found', used in - * the meta box and taxonomy list table. - * @type string $no_terms Default 'No tags'/'No categories', used in the posts and media - * list tables. - * @type string $filter_by_item This label is only used for hierarchical taxonomies. Default - * 'Filter by category', used in the posts list table. - * @type string $items_list_navigation Label for the table pagination hidden heading. - * @type string $items_list Label for the table hidden heading. - * @type string $most_used Title for the Most Used tab. Default 'Most Used'. - * @type string $back_to_items Label displayed after a term has been updated. - * @type string $item_link Used in the block editor. Title for a navigation link block variation. - * Default 'Tag Link'/'Category Link'. - * @type string $item_link_description Used in the block editor. Description for a navigation link block - * variation. Default 'A link to a tag'/'A link to a category'. - * - * @var string[] - */ - protected $labels = []; - - /** - * A short descriptive summary of what the taxonomy is for. Defaults to blank. - * - * @var string - */ - protected $description = ''; - - /** - * If the taxonomy should be publicly queryable; //@TODO not implemented. - * Defaults to true. - * - * @var bool - */ - protected $public; - - /** - * Whether the taxonomy is publicly queryable. - * If not set, the default is inherited from `$public`. - * - * @var bool - */ - protected $publiclyQueryable; - - /** - * Whether the taxonomy is hierarchical (e.g. category). - * Defaults to false. - * - * @var bool - */ - protected $hierarchical; - - /** - * Whether to generate a default UI for managing this taxonomy in the admin. - * If not set, the default is inherited from public. - * - * @var bool - */ - protected $showUI; - - /** - * Whether to show the taxonomy in the admin menu. - * If true, the taxonomy is shown as a submenu of the object type menu. - * If false, no menu is shown. - * show_ui must be true. - * If not set, the default is inherited from show_ui. - * - * @var bool - */ - protected $showInMenu; - - /** - * Makes this taxonomy available for selection in navigation menus. - * If not set, the default is inherited from public. - * - * @var bool - */ - protected $showInNavMenus; - - /** - * Whether to include the taxonomy in the REST API. - * Set this to true for the taxonomy to be available in the block editor. - * - * @var bool - */ - protected $showInRest; - - /** - * Whether to list the taxonomy in the Tag Cloud Widget. - * If not set, the default is inherited from show_ui. - * - * @var bool - */ - protected $showTagcloud; - - /** - * To change the base url of REST API route. Default is $taxonomy. - * - * @var string - */ - protected $restBase; - - /** - * To change the namespace of REST API route. Default is wp/v2. - * - * @var string - */ - protected $restNamespace; - - /** - * REST API Controller class name. Default is 'WP_REST_Terms_Controller'. - * - * @var string - */ - protected $restControllerClass; - - /** - * Whether to list the taxonomy in the Tag Cloud Widget controls. - * If not set, the default is inherited from `$show_ui` (default true). - * - * @var bool - */ - protected $showTagCloud; - - /** - * Whether to show the taxonomy in the quick/bulk edit panel. - * It not set, the default is inherited from show_ui. - * - * @var bool - */ - protected $showInQuickEdit; - - /** - * Whether to display a column for the taxonomy on its post type listing screens. - * - * @var bool - */ - protected $showAdminColumn; - - /** - * Provide a callback function for the meta box display. - * If not set, defaults to post_categories_meta_box for hierarchical taxonomies and post_tags_meta_box for - * non-hierarchical. If false, no meta box is shown. - * - * @var null - */ - protected $metaBoxCb; - - /** - * Callback function for sanitizing taxonomy data saved from a meta box. - * If no callback is defined, an appropriate one is determined - * based on the value of `$metaBoxCb`. - * - * @var null - */ - protected $metaBoxSanitizeCb; - - /** - * Array of capabilities for this taxonomy. - * - * @type string $manage_terms Default 'manage_categories'. - * @type string $edit_terms Default 'manage_categories'. - * @type string $delete_terms Default 'manage_categories'. - * @type string $assign_terms Default 'edit_posts'. - * - * @var array - */ - protected $capabilities = []; - - /** - * Triggers the handling of rewrites for this taxonomy. Defaults to true, using $taxonomy as slug. - * To prevent rewrite, set to false. - * To specify rewrite rules, an array can be passed with any of these keys: - * - * @type string $slug Customize the permastruct slug. Default `$taxonomy` key. - * @type bool $with_front Should the permastruct be prepended with WP_Rewrite::$front. Default true. - * @type bool $hierarchical Either hierarchical rewrite tag or not. Default false. - * @type int $ep_mask Assign an endpoint mask. Default `EP_NONE`. - * - * @var array - */ - protected $rewrite = []; - - /** - * Customize the permastruct slug. Defaults to $taxonomy key - * - * @var string - */ - protected $slug; - - /** - * Should the permastruct be prepended with WP_Rewrite::$front. Defaults to true. - * - * @var bool - */ - protected $withFront; - - /** - * Either hierarchical rewrite tag or not. Defaults to false. - * - * @var bool - */ - protected $rewriteHierarchical; - - /** - * Assign an endpoint mask. - * - * @var int - */ - protected $epMask = EP_NONE; - - /** - * Sets the query_var key for this taxonomy. Defaults to $taxonomy key - * If false, a taxonomy cannot be loaded at ?{query_var}={term_slug} - * If specified as a string, the query ?{query_var_string}={term_slug} will be valid. - * - * @var string - */ - protected $queryVar; - - /** - * Works much like a hook, in that it will be called when the count is updated. - * Defaults to _update_post_term_count() for taxonomies attached to post types, which then confirms that the objects - * are published before counting them. - * Defaults to _update_generic_term_count() for taxonomies attached to other - * object types, such as links. - * - * @var string - */ - protected $updateCountCallback; - - /** - * Default term to be used for the taxonomy. - * - * @type string $name Name of default term. - * @type string $slug Slug for default term. Default empty. - * @type string $description Description for default term. Default empty. - * - * @var string|array - */ - protected $defaultTerm; - - /** - * Whether terms in this taxonomy should be sorted in the order they are - * provided to `wp_set_object_terms()`. Default null which equates to false. - * - * @var bool - */ - protected $sort; - - /** - * Array of arguments to automatically use inside `wp_get_object_terms()` - * for this taxonomy. - * - * @var array - */ - protected $args = []; - - /** - * true if this taxonomy is a native or "built-in" taxonomy. THIS IS FOR INTERNAL USE ONLY! - * - * @var bool - */ - //private $_builtin = true; - - public function register() - { - // you can override this method to set the properties - $this->boot(); - - // Register custom taxonomy - register_taxonomy($this->id, $this->objectType, $this->optionalArgs()); - $this->initHooks(); - } + /** + * Taxonomy key. Must not exceed 32 characters and may only contain + * lowercase alphanumeric characters, dashes, and underscores. See sanitize_key(). + * + * It's the `$taxonomy` parameter used in + * `register_taxonomy( $taxonomy, $object_type, $args = array() )` + * + * @var string + */ + protected $id; + + /** + * Object type or array of object types with which the taxonomy should be associated. + * + * It's the `$object_type` parameter used in + * `register_taxonomy( $taxonomy, $object_type, $args = array() )` + * + * @var string + */ + protected $objectType; + + /** + * Name for one object of this taxonomy. Default 'Tag'/'Category'. + * + * @var string + */ + protected $name; + + /** + * General name for the taxonomy, usually plural. + * The same as and overridden by `$tax->label`. Default 'Tags'/'Categories'. + * + * @var string + */ + protected $plural; + + /** + * Taxonomy labels object. The first default value is for non-hierarchical taxonomies + * (like tags) and the second one is for hierarchical taxonomies (like categories). + * + * @type string $name General name for the taxonomy, usually plural. The same + * as and overridden by `$tax->label`. Default 'Tags'/'Categories'. + * @type string $singular_name Name for one object of this taxonomy. Default 'Tag'/'Category'. + * @type string $search_items Default 'Search Tags'/'Search Categories'. + * @type string $popular_items This label is only used for non-hierarchical taxonomies. + * Default 'Popular Tags'. + * @type string $all_items Default 'All Tags'/'All Categories'. + * @type string $parent_item This label is only used for hierarchical taxonomies. Default + * 'Parent Category'. + * @type string $parent_item_colon The same as `parent_item`, but with colon `:` in the end. + * @type string $name_field_description Description for the Name field on Edit Tags screen. + * Default 'The name is how it appears on your site'. + * @type string $slug_field_description Description for the Slug field on Edit Tags screen. + * Default 'The “slug” is the URL-friendly version + * of the name. It is usually all lowercase and contains + * only letters, numbers, and hyphens'. + * @type string $parent_field_description Description for the Parent field on Edit Tags screen. + * Default 'Assign a parent term to create a hierarchy. + * The term Jazz, for example, would be the parent + * of Bebop and Big Band'. + * @type string $desc_field_description Description for the Description field on Edit Tags screen. + * Default 'The description is not prominent by default; + * however, some themes may show it'. + * @type string $edit_item Default 'Edit Tag'/'Edit Category'. + * @type string $view_item Default 'View Tag'/'View Category'. + * @type string $update_item Default 'Update Tag'/'Update Category'. + * @type string $add_new_item Default 'Add New Tag'/'Add New Category'. + * @type string $new_item_name Default 'New Tag Name'/'New Category Name'. + * @type string $separate_items_with_commas This label is only used for non-hierarchical taxonomies. Default + * 'Separate tags with commas', used in the meta box. + * @type string $add_or_remove_items This label is only used for non-hierarchical taxonomies. Default + * 'Add or remove tags', used in the meta box when JavaScript + * is disabled. + * @type string $choose_from_most_used This label is only used on non-hierarchical taxonomies. Default + * 'Choose from the most used tags', used in the meta box. + * @type string $not_found Default 'No tags found'/'No categories found', used in + * the meta box and taxonomy list table. + * @type string $no_terms Default 'No tags'/'No categories', used in the posts and media + * list tables. + * @type string $filter_by_item This label is only used for hierarchical taxonomies. Default + * 'Filter by category', used in the posts list table. + * @type string $items_list_navigation Label for the table pagination hidden heading. + * @type string $items_list Label for the table hidden heading. + * @type string $most_used Title for the Most Used tab. Default 'Most Used'. + * @type string $back_to_items Label displayed after a term has been updated. + * @type string $item_link Used in the block editor. Title for a navigation link block variation. + * Default 'Tag Link'/'Category Link'. + * @type string $item_link_description Used in the block editor. Description for a navigation link block + * variation. Default 'A link to a tag'/'A link to a category'. + * + * @var string[] + */ + protected $labels = []; + + /** + * A short descriptive summary of what the taxonomy is for. Defaults to blank. + * + * @var string + */ + protected $description = ''; + + /** + * If the taxonomy should be publicly queryable; //@TODO not implemented. + * Defaults to true. + * + * @var bool + */ + protected $public; + + /** + * Whether the taxonomy is publicly queryable. + * If not set, the default is inherited from `$public`. + * + * @var bool + */ + protected $publiclyQueryable; + + /** + * Whether the taxonomy is hierarchical (e.g. category). + * Defaults to false. + * + * @var bool + */ + protected $hierarchical; + + /** + * Whether to generate a default UI for managing this taxonomy in the admin. + * If not set, the default is inherited from public. + * + * @var bool + */ + protected $showUI; + + /** + * Whether to show the taxonomy in the admin menu. + * If true, the taxonomy is shown as a submenu of the object type menu. + * If false, no menu is shown. + * show_ui must be true. + * If not set, the default is inherited from show_ui. + * + * @var bool + */ + protected $showInMenu; + + /** + * Makes this taxonomy available for selection in navigation menus. + * If not set, the default is inherited from public. + * + * @var bool + */ + protected $showInNavMenus; + + /** + * Whether to include the taxonomy in the REST API. + * Set this to true for the taxonomy to be available in the block editor. + * + * @var bool + */ + protected $showInRest; + + /** + * Whether to list the taxonomy in the Tag Cloud Widget. + * If not set, the default is inherited from show_ui. + * + * @var bool + */ + protected $showTagcloud; + + /** + * To change the base url of REST API route. Default is $taxonomy. + * + * @var string + */ + protected $restBase; + + /** + * To change the namespace of REST API route. Default is wp/v2. + * + * @var string + */ + protected $restNamespace; + + /** + * REST API Controller class name. Default is 'WP_REST_Terms_Controller'. + * + * @var string + */ + protected $restControllerClass; + + /** + * Whether to list the taxonomy in the Tag Cloud Widget controls. + * If not set, the default is inherited from `$show_ui` (default true). + * + * @var bool + */ + protected $showTagCloud; + + /** + * Whether to show the taxonomy in the quick/bulk edit panel. + * It not set, the default is inherited from show_ui. + * + * @var bool + */ + protected $showInQuickEdit; + + /** + * Whether to display a column for the taxonomy on its post type listing screens. + * + * @var bool + */ + protected $showAdminColumn; + + /** + * Provide a callback function for the meta box display. + * If not set, defaults to post_categories_meta_box for hierarchical taxonomies and post_tags_meta_box for + * non-hierarchical. If false, no meta box is shown. + * + * @var null + */ + protected $metaBoxCb; - /** - * You may override this method in order to register your own actions and filters. - */ - public function boot() - { - // You may override this method + /** + * Callback function for sanitizing taxonomy data saved from a meta box. + * If no callback is defined, an appropriate one is determined + * based on the value of `$metaBoxCb`. + * + * @var null + */ + protected $metaBoxSanitizeCb; + + /** + * Array of capabilities for this taxonomy. + * + * @type string $manage_terms Default 'manage_categories'. + * @type string $edit_terms Default 'manage_categories'. + * @type string $delete_terms Default 'manage_categories'. + * @type string $assign_terms Default 'edit_posts'. + * + * @var array + */ + protected $capabilities = []; + + /** + * Array of the columns that should be added to the post type table. + * + * @since 1.9.0 + */ + private $columns = []; + + /** + * Triggers the handling of rewrites for this taxonomy. Defaults to true, using $taxonomy as slug. + * To prevent rewrite, set to false. + * To specify rewrite rules, an array can be passed with any of these keys: + * + * @type string $slug Customize the permastruct slug. Default `$taxonomy` key. + * @type bool $with_front Should the permastruct be prepended with WP_Rewrite::$front. Default true. + * @type bool $hierarchical Either hierarchical rewrite tag or not. Default false. + * @type int $ep_mask Assign an endpoint mask. Default `EP_NONE`. + * + * @var array + */ + protected $rewrite = []; + + /** + * Customize the permastruct slug. Defaults to $taxonomy key + * + * @var string + */ + protected $slug; + + /** + * Should the permastruct be prepended with WP_Rewrite::$front. Defaults to true. + * + * @var bool + */ + protected $withFront; + + /** + * Either hierarchical rewrite tag or not. Defaults to false. + * + * @var bool + */ + protected $rewriteHierarchical; + + /** + * Assign an endpoint mask. + * + * @var int + */ + protected $epMask = EP_NONE; + + /** + * Sets the query_var key for this taxonomy. Defaults to $taxonomy key + * If false, a taxonomy cannot be loaded at ?{query_var}={term_slug} + * If specified as a string, the query ?{query_var_string}={term_slug} will be valid. + * + * @var string + */ + protected $queryVar; + + /** + * Works much like a hook, in that it will be called when the count is updated. + * Defaults to _update_post_term_count() for taxonomies attached to post types, which then confirms that the objects + * are published before counting them. + * Defaults to _update_generic_term_count() for taxonomies attached to other + * object types, such as links. + * + * @var string + */ + protected $updateCountCallback; + + /** + * Default term to be used for the taxonomy. + * + * @type string $name Name of default term. + * @type string $slug Slug for default term. Default empty. + * @type string $description Description for default term. Default empty. + * + * @var string|array + */ + protected $defaultTerm; + + /** + * Whether terms in this taxonomy should be sorted in the order they are + * provided to `wp_set_object_terms()`. Default null which equates to false. + * + * @var bool + */ + protected $sort; + + /** + * Array of arguments to automatically use inside `wp_get_object_terms()` + * for this taxonomy. + * + * @var array + */ + protected $args = []; + + /** + * true if this taxonomy is a native or "built-in" taxonomy. THIS IS FOR INTERNAL USE ONLY! + * + * @var bool + */ + //private $_builtin = true; + + public function register() + { + // you can override this method to set the properties + $this->boot(); + + // Register custom taxonomy + register_taxonomy($this->id, $this->objectType, $this->optionalArgs()); + if (isset($this->optionalArgs()["show_in_menu"]) && is_string($this->optionalArgs()["show_in_menu"])) { + $parent = $this->optionalArgs()["show_in_menu"]; + if (! isset($this->plugin->menuRelations[$parent])) { + $this->plugin->menuRelations[$parent] = []; + } + $this->plugin->menuRelations[$parent][] = ["url" => "edit-tags.php?taxonomy=" . $this->id, "name" => $this->optionalArgs()["labels"]["name"], "capabilities" => $this->optionalArgs()["capabilities"]]; } + $this->initHooks(); + } + + /** + * You may override this method in order to register your own actions and filters. + */ + public function boot() + { + // You may override this method + } + + /** + * Return the default args. + * + * @return array + */ + protected function optionalArgs(): array + { + + $mapProperties = [ + 'labels' => 'labels', + 'description' => 'description', + 'public' => 'public', + 'publicly_queryable' => 'publiclyQueryable', + 'hierarchical' => 'hierarchical', + 'show_ui' => 'showUI', + 'show_in_menu' => 'showInMenu', + 'show_in_nav_menus' => 'showInNavMenus', + 'show_in_rest' => 'showInRest', + 'rest_base' => 'restBase', + 'rest_namespace' => 'restNamespace', + 'rest_controller_class' => 'restControllerClass', + 'show_tagcloud' => 'showTagcloud', + 'show_in_quick_edit' => 'showInQuickEdit', + 'show_admin_column' => 'showAdminColumn', + 'meta_box_cb' => 'metaBoxCb', + 'meta_box_sanitize_cb' => 'metaBoxSanitizeCb', + 'capabilities' => 'capabilities', + 'rewrite' => 'rewrite', + 'query_var' => 'queryVar', + 'update_count_callback' => 'updateCountCallback', + 'default_term' => 'defaultTerm', + 'sort' => 'sort', + 'args' => 'args', + ]; + + return $this->mapPropertiesToArray($mapProperties); + } - /** - * Return the default args. - * - * @return array - */ - protected function optionalArgs(): array - { - - $mapProperties = [ - 'labels' => 'labels', - 'description' => 'description', - 'public' => 'public', - 'publicly_queryable' => 'publiclyQueryable', - 'hierarchical' => 'hierarchical', - 'show_ui' => 'showUI', - 'show_in_menu' => 'showInMenu', - 'show_in_nav_menus' => 'showInNavMenus', - 'show_in_rest' => 'showInRest', - 'rest_base' => 'restBase', - 'rest_namespace' => 'restNamespace', - 'rest_controller_class' => 'restControllerClass', - 'show_tagcloud' => 'showTagcloud', - 'show_in_quick_edit' => 'showInQuickEdit', - 'show_admin_column' => 'showAdminColumn', - 'meta_box_cb' => 'metaBoxCb', - 'meta_box_sanitize_cb' => 'metaBoxSanitizeCb', - 'capabilities' => 'capabilities', - 'rewrite' => 'rewrite', - 'query_var' => 'queryVar', - 'update_count_callback' => 'updateCountCallback', - 'default_term' => 'defaultTerm', - 'sort' => 'sort', - 'args' => 'args', - ]; - - return $this->mapPropertiesToArray($mapProperties); + /** + * Return defaults labels. + * + * @type string $name General name for the taxonomy, usually plural. The same + * as and overridden by `$tax->label`. Default 'Tags'/'Categories'. + * @type string $singular_name Name for one object of this taxonomy. Default 'Tag'/'Category'. + * @type string $search_items Default 'Search Tags'/'Search Categories'. + * @type string $popular_items This label is only used for non-hierarchical taxonomies. + * Default 'Popular Tags'. + * @type string $all_items Default 'All Tags'/'All Categories'. + * @type string $parent_item This label is only used for hierarchical taxonomies. Default + * 'Parent Category'. + * @type string $parent_item_colon The same as `parent_item`, but with colon `:` in the end. + * @type string $name_field_description Description for the Name field on Edit Tags screen. + * Default 'The name is how it appears on your site'. + * @type string $slug_field_description Description for the Slug field on Edit Tags screen. + * Default 'The “slug” is the URL-friendly version + * of the name. It is usually all lowercase and contains + * only letters, numbers, and hyphens'. + * @type string $parent_field_description Description for the Parent field on Edit Tags screen. + * Default 'Assign a parent term to create a hierarchy. + * The term Jazz, for example, would be the parent + * of Bebop and Big Band'. + * @type string $desc_field_description Description for the Description field on Edit Tags screen. + * Default 'The description is not prominent by default; + * however, some themes may show it'. + * @type string $edit_item Default 'Edit Tag'/'Edit Category'. + * @type string $view_item Default 'View Tag'/'View Category'. + * @type string $update_item Default 'Update Tag'/'Update Category'. + * @type string $add_new_item Default 'Add New Tag'/'Add New Category'. + * @type string $new_item_name Default 'New Tag Name'/'New Category Name'. + * @type string $separate_items_with_commas This label is only used for non-hierarchical taxonomies. Default + * 'Separate tags with commas', used in the meta box. + * @type string $add_or_remove_items This label is only used for non-hierarchical taxonomies. Default + * 'Add or remove tags', used in the meta box when JavaScript + * is disabled. + * @type string $choose_from_most_used This label is only used on non-hierarchical taxonomies. Default + * 'Choose from the most used tags', used in the meta box. + * @type string $not_found Default 'No tags found'/'No categories found', used in + * the meta box and taxonomy list table. + * @type string $no_terms Default 'No tags'/'No categories', used in the posts and media + * list tables. + * @type string $filter_by_item This label is only used for hierarchical taxonomies. Default + * 'Filter by category', used in the posts list table. + * @type string $items_list_navigation Label for the table pagination hidden heading. + * @type string $items_list Label for the table hidden heading. + * @type string $most_used Title for the Most Used tab. Default 'Most Used'. + * @type string $back_to_items Label displayed after a term has been updated. + * @type string $item_link Used in the block editor. Title for a navigation link block variation. + * Default 'Tag Link'/'Category Link'. + * @type string $item_link_description Used in the block editor. Description for a navigation link block + * variation. Default 'A link to a tag'/'A link to a category'. + * + * @return array + */ + protected function labels(): array + { + $defaults = [ + 'name' => $this->plural, + 'singular_name' => "{$this->name} category", + 'search_items' => "Search {$this->name} categories", + 'popular_items' => "Popular {$this->name} categories", + 'all_items' => "All {$this->name} categories", + 'parent_item' => "Parent {$this->name} category", + 'parent_item_colon' => "Parent {$this->name} category:", + 'slug_field_description' => "The “slug”", + 'parent_field_description' => "Assign a parent {$this->name} category to create a hierarchy.", + 'desc_field_description' => "The description is not prominent by default", + 'menu_name' => "{$this->name} categories", + 'edit_item' => "Edit {$this->name} category", + 'view_item' => "View {$this->name} category", + 'update_item' => "Updated {$this->name} category", + 'add_new_item' => "Add new {$this->name} category", + 'new_item_name' => "New {$this->name} category name", + 'separate_items_with_commas' => "Separate {$this->name} categories with comas", + 'add_or_remove_items' => "Add or remove {$this->name} categories", + 'choose_from_most_used' => "Choose from the most used {$this->name} categories", + 'not_found' => "No {$this->name} categories found", + 'no_terms' => "No {$this->name} categories", + 'filter_by_item' => "Filter by {$this->name} category", + 'items_list_navigation' => "{$this->name} categories list navigation", + 'items_list' => "{$this->name} categories list", + 'most_used' => "Most used {$this->name} categories", + 'back_to_items' => "Back to {$this->name} categories", + 'item_link' => "{$this->name} category link", + 'item_link_description' => "A link to a {$this->name} category", + ]; + + if (empty($this->labels)) { + return $defaults; } - /** - * Return defaults labels. - * - * @type string $name General name for the taxonomy, usually plural. The same - * as and overridden by `$tax->label`. Default 'Tags'/'Categories'. - * @type string $singular_name Name for one object of this taxonomy. Default 'Tag'/'Category'. - * @type string $search_items Default 'Search Tags'/'Search Categories'. - * @type string $popular_items This label is only used for non-hierarchical taxonomies. - * Default 'Popular Tags'. - * @type string $all_items Default 'All Tags'/'All Categories'. - * @type string $parent_item This label is only used for hierarchical taxonomies. Default - * 'Parent Category'. - * @type string $parent_item_colon The same as `parent_item`, but with colon `:` in the end. - * @type string $name_field_description Description for the Name field on Edit Tags screen. - * Default 'The name is how it appears on your site'. - * @type string $slug_field_description Description for the Slug field on Edit Tags screen. - * Default 'The “slug” is the URL-friendly version - * of the name. It is usually all lowercase and contains - * only letters, numbers, and hyphens'. - * @type string $parent_field_description Description for the Parent field on Edit Tags screen. - * Default 'Assign a parent term to create a hierarchy. - * The term Jazz, for example, would be the parent - * of Bebop and Big Band'. - * @type string $desc_field_description Description for the Description field on Edit Tags screen. - * Default 'The description is not prominent by default; - * however, some themes may show it'. - * @type string $edit_item Default 'Edit Tag'/'Edit Category'. - * @type string $view_item Default 'View Tag'/'View Category'. - * @type string $update_item Default 'Update Tag'/'Update Category'. - * @type string $add_new_item Default 'Add New Tag'/'Add New Category'. - * @type string $new_item_name Default 'New Tag Name'/'New Category Name'. - * @type string $separate_items_with_commas This label is only used for non-hierarchical taxonomies. Default - * 'Separate tags with commas', used in the meta box. - * @type string $add_or_remove_items This label is only used for non-hierarchical taxonomies. Default - * 'Add or remove tags', used in the meta box when JavaScript - * is disabled. - * @type string $choose_from_most_used This label is only used on non-hierarchical taxonomies. Default - * 'Choose from the most used tags', used in the meta box. - * @type string $not_found Default 'No tags found'/'No categories found', used in - * the meta box and taxonomy list table. - * @type string $no_terms Default 'No tags'/'No categories', used in the posts and media - * list tables. - * @type string $filter_by_item This label is only used for hierarchical taxonomies. Default - * 'Filter by category', used in the posts list table. - * @type string $items_list_navigation Label for the table pagination hidden heading. - * @type string $items_list Label for the table hidden heading. - * @type string $most_used Title for the Most Used tab. Default 'Most Used'. - * @type string $back_to_items Label displayed after a term has been updated. - * @type string $item_link Used in the block editor. Title for a navigation link block variation. - * Default 'Tag Link'/'Category Link'. - * @type string $item_link_description Used in the block editor. Description for a navigation link block - * variation. Default 'A link to a tag'/'A link to a category'. - * - * @return array - */ - protected function labels(): array - { - $defaults = [ - 'name' => $this->plural, - 'singular_name' => "{$this->name} category", - 'search_items' => "Search {$this->name} categories", - 'popular_items' => "Popular {$this->name} categories", - 'all_items' => "All {$this->name} categories", - 'parent_item' => "Parent {$this->name} category", - 'parent_item_colon' => "Parent {$this->name} category:", - 'slug_field_description' => "The “slug”", - 'parent_field_description' => "Assign a parent {$this->name} category to create a hierarchy.", - 'desc_field_description' => "The description is not prominent by default", - 'menu_name' => "{$this->name} categories", - 'edit_item' => "Edit {$this->name} category", - 'view_item' => "View {$this->name} category", - 'update_item' => "Updated {$this->name} category", - 'add_new_item' => "Add new {$this->name} category", - 'new_item_name' => "New {$this->name} category name", - 'separate_items_with_commas' => "Separate {$this->name} categories with comas", - 'add_or_remove_items' => "Add or remove {$this->name} categories", - 'choose_from_most_used' => "Choose from the most used {$this->name} categories", - 'not_found' => "No {$this->name} categories found", - 'no_terms' => "No {$this->name} categories", - 'filter_by_item' => "Filter by {$this->name} category", - 'items_list_navigation' => "{$this->name} categories list navigation", - 'items_list' => "{$this->name} categories list", - 'most_used' => "Most used {$this->name} categories", - 'back_to_items' => "Back to {$this->name} categories", - 'item_link' => "{$this->name} category link", - 'item_link_description' => "A link to a {$this->name} category", - ]; - - if (empty($this->labels)) { - return $defaults; - } - - return array_merge($defaults, $this->labels); + return array_merge($defaults, $this->labels); + } + + /** + * Return defaults rewrite. + * + * 'slug' => string Customize the permastruct slug. Defaults to $taxonomy key + * 'with_front' => bool Should the permastruct be prepended with WP_Rewrite::$front. Defaults to true. + * 'hierarchical' => bool Either hierarchical rewrite tag or not. Defaults to false. + * 'ep_mask' => const Assign an endpoint mask. + * If not specified, defaults to EP_NONE. + * + * @return array + */ + protected function rewrite(): array + { + + if (! empty($this->rewrite)) { + return $this->rewrite; } - /** - * Return defaults rewrite. - * - * 'slug' => string Customize the permastruct slug. Defaults to $taxonomy key - * 'with_front' => bool Should the permastruct be prepended with WP_Rewrite::$front. Defaults to true. - * 'hierarchical' => bool Either hierarchical rewrite tag or not. Defaults to false. - * 'ep_mask' => const Assign an endpoint mask. - * If not specified, defaults to EP_NONE. - * - * @return array - */ - protected function rewrite(): array - { - - if (!empty($this->rewrite)) { - return $this->rewrite; - } - - $mapProperties = [ - 'slug' => 'id', - 'with_front' => 'withFront', - 'hierarchical' => 'rewriteHierarchical', - 'ep_mask' => 'epMask', - ]; - - return $this->mapPropertiesToArray($mapProperties); + $mapProperties = [ + 'slug' => 'id', + 'with_front' => 'withFront', + 'hierarchical' => 'rewriteHierarchical', + 'ep_mask' => 'epMask', + ]; + + return $this->mapPropertiesToArray($mapProperties); + } + /** + * Initialize hooks + */ + protected function initHooks() + { + add_action('admin_init', [$this, 'addTaxonomyColumns']); + } + public function addTaxonomyColumns() + { + + add_filter('manage_' . $this->id . '_custom_column', [$this, 'columnContent'], 15, 3); + add_filter('manage_edit-' . $this->id . '_columns', [$this, '_manage_taxonomy_columns']); + } + /** + * Manage columns + * + * @param array $columns + * + * @since 1.9.0 + * @return array + */ + + public function _manage_taxonomy_columns($columns) + { + $newColumns = $this->registerColumns(); + $cb = []; + $cnt = []; + if (isset($columns["cb"])) { + $cb = ["cb" => $columns["cb"]]; + unset($columns["cb"]); + } + if (isset($columns["posts"])) { + $cnt = ["posts" => $columns["posts"]]; + unset($columns["posts"]); } - /** - * Initialize hooks - */ - protected function initHooks() - { - add_action('admin_init', [$this, 'addTaxonomyColumns']); - } - public function addTaxonomyColumns() - { - - add_filter('manage_' . $this->id . '_custom_column', [$this, 'columnContent'], 15, 3); - add_filter('manage_edit-' . $this->id . '_columns', [$this, '_manage_taxonomy_columns']); - } - /** - * Manage columns - * - * @param array $columns - * - * @since 1.9.0 - * @return array - */ - - public function _manage_taxonomy_columns($columns) - { - $newColumns = $this->registerColumns(); - $cb = []; - $cnt = []; - //With this, we keep the cb at first item, and the post count at last. - if (isset($columns["cb"])) { - $cb = ["cb"=>$columns["cb"]]; - unset($columns["cb"]); - } - if (isset($columns["posts"])) { - $cnt = ["posts"=>$columns["posts"]]; - unset($columns["posts"]); - } - return array_merge($cb, $columns, $newColumns,$cnt); - } - - - /** - * Return the column content - * - * @param string $string - * @param string $column_name - * @param int $term_id - * - * @since 1.9.0 - * @return string - */ - public function columnContent($string, $column_name, $term_id) - { - // You may override this method - return $string; - } + return array_merge($cb, $columns, $newColumns, $cnt); + } + + /** + * Return the column content + * + * @param string $string + * @param string $column_name + * @param int $term_id + * + * @since 1.9.0 + * @return string + */ + public function columnContent($string, $column_name, $term_id) + { + // You may override this method + return $string; + } + } From b8ae22a5f119bce6584dd5d750cb803d8e2aa719 Mon Sep 17 00:00:00 2001 From: Brede Basualdo <62568995+bredecl@users.noreply.github.com> Date: Tue, 1 Apr 2025 15:46:52 -0300 Subject: [PATCH 11/11] Update bones fix closes the askYesNo method --- src/Console/bin/bones | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Console/bin/bones b/src/Console/bin/bones index 18dde5f..e738f3b 100644 --- a/src/Console/bin/bones +++ b/src/Console/bin/bones @@ -619,7 +619,8 @@ namespace Bones\Traits { }else{ return $default?"true":"false"; - + } + } /** * Get an option value from the command line arguments *