{"generator":"Code Snippets v3.9.5","date_created":"2026-04-12 09:55","snippets":[{"id":7,"name":"MA Admin Quick Nav","desc":"<p>Quick Navigation in Admin Bar<\/p>\n<p>Version 1.8.2, 2026-04-12<br \/>\u00a9 2024-2025, Matthias Altmann<\/p>\n<p>Info: <br \/>en: <a href=\"https:\/\/www.altmann.de\/en\/blog-en\/code-snippet-admin-quick-nav\/\" target=\"_blank\" rel=\"noopener\">https:\/\/www.altmann.de\/en\/blog-en\/code-snippet-admin-quick-nav\/<\/a><br \/>de: <a href=\"https:\/\/www.altmann.de\/blog\/code-snippet-admin-schnellnavigation\/\" target=\"_blank\" rel=\"noopener\">https:\/\/www.altmann.de\/blog\/code-snippet-admin-schnellnavigation\/<\/a><\/p>","code":"\/*\nPlugin Name:       MA Admin Quick Nav\nDescription:       Admin Bar Quick Navigation for Post Types\nAuthor:            <a href=\"https:\/\/www.altmann.de\/\">Matthias Altmann<\/a>\nProject:           Code Snippet: MA Admin Quick Nav\nVersion:           1.8.2\nRequires at least: 5.8\nRequires PHP:      7.4\nPlugin URI:        https:\/\/www.altmann.de\/en\/blog-en\/code-snippet-admin-quick-nav\/\nDescription:       en: https:\/\/www.altmann.de\/en\/blog-en\/code-snippet-admin-quick-nav\/\n                   de: https:\/\/www.altmann.de\/blog\/code-snippet-admin-schnellnavigation\/\nCopyright:         \u00a9 2024-2026, Matthias Altmann\n\nTESTED WITH:\nProduct\t\tVersions\n--------------------------------------------------------------------------------------------------------------\nPHP \t\t8.1 ... 8.4\nWordPress\t6.4.2 ... 6.9.4\nBricks\t\t1.9.5 ... 2.3.2\nOxygen\t\t4.8.1 ... 4.9.6\n--------------------------------------------------------------------------------------------------------------\n\nVERSION HISTORY:\nDate\t\tVersion\t\tDescription\n--------------------------------------------------------------------------------------------------------------\n2026-04-12\t1.8.2 \t\tNew Features:\n\t\t\t\t\t\t- Support of custom menu_icon image URL (e.g. icon from media library for ACF CPT)\n\t\t\t\t\t\t  (Thanks to Aidan Gartland for reporting)\n\t\t\t\t\t\tCode Improvements:\n\t\t\t\t\t\t- Changed direct access to constants (e.g. CT_VERSION), to constant() calls\n\t\t\t\t\t\t- Changed direct function calls (e.g. ct_get_post_builder_link()), to call_user_func() calls\n2025-12-06\t1.8.1\t\tNew Features:\n\t\t\t\t\t\t- Added support for localization. \n\t\t\t\t\t\t  Initially for German (de_DE), Italian (it_IT), French (fr_FR), and Spanish (es_ES).\n\t\t\t\t\t\t  More to follow on request.\n\t\t\t\t\t\tImprovements:\n\t\t\t\t\t\t- Don't embed CSS, SVG, and JS for anonymous requests or if Admin Bar is not shown\n\t\t\t\t\t\tTested:\n\t\t\t\t\t\t- WordPress 6.9\n\t\t\t\t\t\t- Bricks 2.1.4\n2025-11-10\t1.8.0\t\tNew Features:\n\t\t\t\t\t\t- Custom Links: Create your own custom links to be shown in Admin Bar\n\t\t\t\t\t\t- Support for WP Gutenberg Patterns (post type \"wp_block\")\n\t\t\t\t\t\t\t- Detection if Gutenberg is enabled for any of the registered post types\n\t\t\t\t\t\t\t- Setting to enable Patterns in Admin Quick Nav\n\t\t\t\t\t\t\t- Setting to enable Patterns in Admin Menu\n\t\t\t\t\t\t\t- Disable \"View\" action (not available for Patterns)\n\t\t\t\t\t\tChanges:\n\t\t\t\t\t\t- For post types with undefined menu icon, show dashicons-marker\n2025-10-12\t1.7.5\t\tImprovements: \n\t\t\t\t\t\t- Consistent escaping. \n\t\t\t\t\t\tTested:\n\t\t\t\t\t\t- Bricks 2.1.2.\n2025-06-06\t1.7.4 \t\tTested:\n\t\t\t\t\t\t- Bricks 2.0-beta, backwards compatible to Bricks 1.12.4\n2025-04-24\tunreleased\tFixes:\n\t\t\t\t\t\t- Fix for Bricks 2.0-alpha: \n\t\t\t\t\t\t  CSS adaptions for top and height of #bricks-panel, #bricks-preview, #bricks-structure\n\t\t\t\t\t\t  to use new --builder-toolbar-height (=48px, default 40px for older Bricks versions) \n\t\t\t\t\t\t  instead of hardcoded 40px\n\t\t\t\t\t\t- Ignore configured post type \"bricks_template\" if Bricks has been deactivated\/uninstalled\n2024-11-13\t1.7.3\t\tCompatibility WordPress 6.7: \n\t\t\t\t\t\t- Changed permission check based on cap instead of capability_type (which is now array)\n\t\t\t\t\t\tFixes:\n\t\t\t\t\t\t- CSS adaptions for Advanced Themer 2.9 on Bricks sites: \n\t\t\t\t\t\t\t- Padding for search field on AT settings page\n2024-10-28\t1.7.2\t\tFixes:\n\t\t\t\t\t\t- Fix for quick search in Builder and frontend\n2024-10-11\t1.7.1\t\tFixes:\n\t\t\t\t\t\t- CSS adaptions for Advanced Themer 2.9 on Bricks sites: \n\t\t\t\t\t\t  - Additional spacing at top for full screen modes with Admin bar enabled\n\t\t\t\t\t\t  - Padding for search field\n2024-10-06\t1.7.0\t\tNew Features:\n\t\t\t\t\t\t- Added \"Add new ...\" link \n\t\t\t\t\t\t  (Requested by Jennifer Cisneros)\n2024-08-23\t1.6.3\t\tFixes:\n\t\t\t\t\t\t- CSS adaptions for Admin Bar in Oxygen 4.9 Builder\n2024-07-28\t1.6.2\t\tFixes:\n\t\t\t\t\t\t- When building menu sections\/items, skip unregistered post types. \n\t\t\t\t\t\t  This situation can occur if a post type was configured in Quick Nav settings, \n\t\t\t\t\t\t  but related plugin has been deactivated.\n2024-07-22\t1.6.1\t\tFixes:\n\t\t\t\t\t\t- Prevent fatal errors if no HappyFiles post types have been configured\n\t\t\t\t\t\t (Thanks to Pascal D\u00f6rflinger for reporting)\n2024-07-17\t1.6.0\t\tNew Features:\n\t\t\t\t\t\t- Support for HappyFiles folders\n\t\t\t\t\t\t  (Requested by Sebastian Berger)\n2024-07-07\t1.5.0\t\tNew Features:\n\t\t\t\t\t\t- Quick Search\n\t\t\t\t\t\t  (Requested by John Kirker)\n\t\t\t\t\t\tChanges:\n\t\t\t\t\t\t- Optimized post limit detection (now checking all selected states)\n2024-05-23\t1.4.0\t\tNew Features:\n\t\t\t\t\t\t- Optional display of Polylang language in Quick Nav lists\n2024-05-21\t1.3.1\t\tChanges:\n\t\t\t\t\t\t- Compatibility to Oxygen 4.8.3 with changed post meta keys\n2024-04-09\t1.3.0\t\tNew Features:\n\t\t\t\t\t\t- Support for selecting status of posts to be listed\n2024-04-05\t1.2.0\t\tChanges:\n\t\t\t\t\t\t- Disabled Polylang language filter to show all languages, also in frontend\n\t\t\t\t\t\t- Changed some direct Bricks function calls to call_user_func() to avoid warnings in \n\t\t\t\t\t\t  code editor on non-Bricks setups\n2024-02-07\t1.1.0\t\tNew Features:\n\t\t\t\t\t\t- New action \"View\" (except for Oxygen\/Bricks templates)\n2024-02-03\t1.0.2\t\tFixes:\n\t\t\t\t\t\t- Corrected semantical issue by moving svg symbols from head to footer\n\t\t\t\t\t\t- Corrected action icon width.\n2024-01-30\t1.0.1\t\tFixes:\n\t\t\t\t\t\t- Added additional permission check for Bricks templates\n\t\t\t\t\t\tTested:\n2024-01-28\t1.0.0\t\tChanges:\n\t\t\t\t\t\t- Stretch sub menu item links\n\t\t\t\t\t\t- Improved translation\n\t\t\t\t\t\t- Empty limit now means unlimited\n\t\t\t\t\t\tInspired by Michael Pucher:\n\t\t\t\t\t\t- Sortable post types\n\t\t\t\t\t\t- Collapsed or individual menus for post types\n\t\t\t\t\t\t- Allow sorting by menu_order\n\t\t\t\t\t\t- Removed title attribute from top menu because that tooltip is distracting\n2024-01-26\t0.0.1\t\tInternal test version supporting Oxygen and Bricks\n2024-01-20\t0.0.0\t\tDevelopment start\n--------------------------------------------------------------------------------------------------------------\n\nIcon Arrow:\thttps:\/\/css.gg\/arrow-top-right-o\nIcon Eye:\thttps:\/\/css.gg\/eye\n*\/\n\nif (!class_exists('MA_Admin_Quick_Nav')) :\n\nclass MA_Admin_Quick_Nav {\n\tconst TITLE\t\t\t\t\t\t\t= 'Quick Nav';\n\tconst SLUG\t\t\t\t\t\t\t= 'ma-admin-quick-nav';\n\tconst SHRT \t\t\t\t\t\t\t= 'maqn';\n\tconst VERSION\t\t\t\t\t\t= '1.8.2';\n\n\t\/\/ ===== CONFIGURATION ==============================================================================================\n\tpublic static $timing\t\t\t\t= false; \t\/\/ Write timing info to wordpress debug.log if WP_DEBUG also enabled. \n\t\t\t\t\t\t\t\t\t\t\t\t\t\/\/ false\/0:\tDisabled, true\/1: Enabled, 2: Extended.\n\t\t\t\t\t\t\t\t\t\t\t\t\t\/\/ The snippet also provides timing for Query Monitor plugin.\n\tpublic static $expert_mode\t\t\t= false;\t\/\/ enables some additional settings for the experts, like menu_position\n\tpublic static $custom_link_remove_site_url = false;\t\/\/ when true, the site base URL is automatically removed from \n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\/\/ custom link URLs on settings page\n\n\n\t\/\/ ===== INTERNAL ===================================================================================================\n\tprivate const DEFAULT_MENU_POSITION\t= 80; \n\tprivate const DEFAULT_POST_TYPES \t= ['page'=>[],'post'=>[]];\n\tprivate const DEFAULT_LIMIT\t\t\t= 20;\n\tprivate const DEFAULT_ORDERBY\t\t= 'post_date';\n\tprivate const DEFAULT_ORDER\t\t\t= 'DESC';\n\tprivate const DEFAULT_STATUS\t\t= ['publish'];\n\tprivate const VALID_ORDERBY\t\t\t= ['post_date','post_title','menu_order'];\n\tprivate const VALID_ORDER\t\t\t= ['ASC','DESC'];\n\tprivate const MINIMIZE_CSS\t\t\t= true;\n\tprivate const MINIMIZE_SVG\t\t\t= true;\n\tprivate const MINIMIZE_JS\t\t\t= true;\n\n\tpublic static $total_runtime\t\t= 0;\n\tprivate $custom_translations\t\t= [];  \/\/ Array to hold custom translations\n\tprivate $settings \t\t\t\t\t= null;\n\n\t\/\/-------------------------------------------------------------------------------------------------------------------\n\tfunction __construct() {\n\t\tdo_action('qm\/start', __METHOD__); \/\/ Query Monitor Profiling\n\t\t$st = microtime(true);\n\n\t\t\/\/ read settings from db and merge to default settings\n\t\t$this->settings = array_merge([\n\t\t\t'menu_position'\t=> self::DEFAULT_MENU_POSITION,\n\t\t\t'post_types'\t=> [],\n\t\t\t'status'\t\t=> self::DEFAULT_STATUS,\n\t\t], get_option(self::SLUG, ['post_types'=>self::DEFAULT_POST_TYPES]));\n\n\n\t\t\/\/ emit CSS for backend and frontend \n\t\tadd_action('admin_enqueue_scripts',\t[$this, 'css']);\n\t\tadd_action('wp_enqueue_scripts', \t[$this, 'css']);\n\n\t\t\/\/ emit SVG for backend and frontend \n\t\tadd_action('admin_footer',\t\t\t[$this, 'svg']);\n\t\tadd_action('wp_footer', \t\t\t[$this, 'svg']);\n\t\tadd_action('wp_footer', \t\t\t[$this, 'debug_info']);\n\n\t\t\/\/ emit search script\n\t\tif ($this->settings['quick_search']??null) {\n\t\t\tadd_action('wp_footer',[$this, 'search_script']);\n\t\t\tadd_action('admin_footer',[$this, 'search_script']);\n\t\t}\n\n\t\tadd_action('admin_enqueue_scripts',\t[$this, 'admin_scripts']);\n\t\t\n\t\t\/\/ Initialize custom translations\n\t\tadd_action('after_setup_theme', [$this, 'l10n_init']);\n\n\t\t\/\/ display menu only for wp-admin area\n\t\tif (is_admin()) {\n\t\t\t\/\/ register admin menus (must be registered with hook, otherwise appears on top of menu)\n\t\t\tadd_action('admin_menu', [$this, 'admin_menu'], 100 );\n\t\t\t\/\/ register settings\n\t\t\tadd_action('admin_init', [$this, 'settings_register'] );\n\n\t\t} \n\t\t\n\n\n\t\t\/\/ builder admin bar?\n\t\tif ($this->settings['builder_admin_bar']??false) {\n\t\t\t\/\/ must hook before init\n\t\t\tadd_action('after_setup_theme',[$this,'builder_admin_bar']);\n\t\t}\n\n\t\t\/\/ register admin bar menu items\n\t\tadd_action('admin_bar_menu',[$this, 'admin_bar_items'], intval($this->settings['menu_position']??self::DEFAULT_MENU_POSITION));\n\n\t\t\/\/ add a handler for logging total runtime\n\t\tadd_action('shutdown', [$this,'total_runtime']);\n\n\t\t$et = microtime(true);\n\t\tif (WP_DEBUG && self::$timing) {error_log(sprintf('%s%s::%s() Timing: %.5f sec.', '', __CLASS__, __FUNCTION__, $et-$st));}\n\t\tself::$total_runtime += $et-$st;\n\t\tdo_action('qm\/stop', __METHOD__); \/\/ Query Monitor Profiling\n\t}\n\n\t\/\/-------------------------------------------------------------------------------------------------------------------\n\t\/**\n\t * Logs total timing.\n\t *\/\n\tpublic static function total_runtime(){\n\t\tif (WP_DEBUG && self::$timing) {error_log(sprintf('%s%s::%s() Timing: %.5f sec.', '', __CLASS__, __FUNCTION__, self::$total_runtime));}\n\t}\n\n\t\/\/-------------------------------------------------------------------------------------------------------------------\n\t\/**\n\t * Initialize custom translations.\n\t *\/\n\tpublic function l10n_init() {\n\t\tif (!is_admin_bar_showing()) return;\n\t\tif (!is_user_logged_in()) return;\n\n\t\tdo_action('qm\/start', __METHOD__); \/\/ Query Monitor Profiling\n\t\t$st = microtime(true);\n\n\t\t\/\/ Definition of custom translations\n\t\t\/\/ Format: 'original text' => ['locale' => 'translated text']\n\t\t$this->custom_translations = [\n\t\t\t\/\/ General settings\n\t\t\t'Collapse all lists into one menu \"%s\"' => [\n\t\t\t\t'de_DE' => 'Alle Listen in ein Men\u00fc \"%s\" zusammenfassen',\n\t\t\t\t'it_IT' => 'Comprimi tutte le liste in un unico menu \"%s\"',\n\t\t\t\t'fr_FR' => 'R\u00e9duire toutes les listes en un seul menu \"%s\"',\n\t\t\t\t'es_ES' => 'Colapsar todas las listas en un solo men\u00fa \"%s\"',\n\t\t\t],\n\t\t\t'Enable quick search bar' => [\n\t\t\t\t'de_DE' => 'Schnellsuchleiste aktivieren',\n\t\t\t\t'it_IT' => 'Abilita barra di ricerca rapida',\n\t\t\t\t'fr_FR' => 'Activer la barre de recherche rapide',\n\t\t\t\t'es_ES' => 'Activar la barra de b\u00fasqueda r\u00e1pida',\n\t\t\t],\n\t\t\t'Enable \"Add {element}...\" for each post type' => [\n\t\t\t\t'de_DE' => '\"{Element} hinzuf\u00fcgen...\" f\u00fcr jeden Beitragstyp aktivieren',\n\t\t\t\t'it_IT' => 'Abilita \"Aggiungi {elemento}...\" per ogni tipo di post',\n\t\t\t\t'fr_FR' => 'Activer \"Ajouter {\u00e9l\u00e9ment}...\" pour chaque type de publication',\n\t\t\t\t'es_ES' => 'Activar \"Agregar {elemento}...\" para cada tipo de publicaci\u00f3n',\n\t\t\t],\n\t\t\t'Show language for items if defined' => [\n\t\t\t\t'de_DE' => 'Sprache f\u00fcr Elemente anzeigen, falls definiert',\n\t\t\t\t'it_IT' => 'Mostra la lingua per gli elementi se definita',\n\t\t\t\t'fr_FR' => 'Afficher la langue pour les \u00e9l\u00e9ments si d\u00e9finie',\n\t\t\t\t'es_ES' => 'Mostrar el idioma para los elementos si est\u00e1 definido',\n\t\t\t],\n\t\t\t'Enable admin bar in %s builder' => [\n\t\t\t\t'de_DE' => 'Admin Bar im %s Builder aktivieren',\n\t\t\t\t'it_IT' => 'Abilita la barra di amministrazione nel builder %s',\n\t\t\t\t'fr_FR' => 'Activer la barre d\\'administration dans le constructeur %s',\n\t\t\t\t'es_ES' => 'Activar la barra de administraci\u00f3n en el constructor %s',\n\t\t\t],\n\t\t\t\/\/ Post Types table\n\t\t\t'Enable Admin Bar Menu for Post Types. Drag the handle to reorder.' => [\n\t\t\t\t'de_DE' => 'Admin Bar Men\u00fc f\u00fcr Beitragstypen aktivieren. Symbol ziehen, um die Reihenfolge zu \u00e4ndern.',\n\t\t\t\t'it_IT' => 'Abilita il menu della barra di amministrazione per i tipi di post. Trascina la maniglia per riordinare.',\n\t\t\t\t'fr_FR' => 'Activer le menu de la barre d\\'administration pour les types de publication. Faites glisser la poign\u00e9e pour r\u00e9organiser.',\n\t\t\t\t'es_ES' => 'Activar el men\u00fa de la barra de administraci\u00f3n para los tipos de publicaciones. Arrastra el controlador para reordenar.',\n\t\t\t],\n\t\t\t'Show hierarchy by indenting list' => [\n\t\t\t\t'de_DE' => 'Hierarchie durch Einr\u00fcckung der Liste anzeigen',\n\t\t\t\t'it_IT' => 'Mostra la gerarchia rientrando l\\'elenco',\n\t\t\t\t'fr_FR' => 'Afficher la hi\u00e9rarchie en indentant la liste',\n\t\t\t\t'es_ES' => 'Mostrar la jerarqu\u00eda indentando la lista',\n\t\t\t],\n\t\t\t'Group by HappyFiles folders' => [\n\t\t\t\t'de_DE' => 'Nach HappyFiles-Ordnern gruppieren',\n\t\t\t\t'it_IT' => 'Raggruppa per cartelle HappyFiles',\n\t\t\t\t'fr_FR' => 'Grouper par dossiers HappyFiles',\n\t\t\t\t'es_ES' => 'Agrupar por carpetas de HappyFiles',\n\t\t\t],\n\t\t\t'Show icon to allow editing in %s' => [\n\t\t\t\t'de_DE' => 'Symbol anzeigen, um die Bearbeitung in %s zu erm\u00f6glichen',\n\t\t\t\t'it_IT' => 'Mostra l\\'icona per consentire la modifica in %s',\n\t\t\t\t'fr_FR' => 'Afficher l\\'ic\u00f4ne pour permettre la modification dans %s',\n\t\t\t\t'es_ES' => 'Mostrar el icono para permitir la edici\u00f3n en %s',\n\t\t\t],\n\t\t\t'Show in Admin Menu' => [\n\t\t\t\t'de_DE' => 'Im Admin Men\u00fc anzeigen',\n\t\t\t\t'it_IT' => 'Mostra nel menu di amministrazione',\n\t\t\t\t'fr_FR' => 'Afficher dans le menu d\\'administration',\n\t\t\t\t'es_ES' => 'Mostrar en el men\u00fa de administraci\u00f3n',\n\t\t\t],\n\t\t\t'List items with status &quot;%s&quot;' => [\n\t\t\t\t'de_DE' => 'Elemente mit dem Status &quot;%s&quot; auflisten',\n\t\t\t\t'it_IT' => 'Elenca gli elementi con stato &quot;%s&quot;',\n\t\t\t\t'fr_FR' => 'Lister les \u00e9l\u00e9ments avec le statut &quot;%s&quot;',\n\t\t\t\t'es_ES' => 'Listar elementos con estado &quot;%s&quot;',\n\t\t\t],\n\t\t\t\/\/ Custom Links table\n\t\t\t'Manage custom links. Drag the handle to reorder.' => [\n\t\t\t\t'de_DE' => 'Verwaltung f\u00fcr individuelle Links. Symbol ziehen, um die Reihenfolge zu \u00e4ndern.',\n\t\t\t\t'it_IT' => 'Gestisci i link personalizzati. Trascina la maniglia per riordinare.',\n\t\t\t\t'fr_FR' => 'G\u00e9rer les liens personnalis\u00e9s. Faites glisser la poign\u00e9e pour r\u00e9organiser.',\n\t\t\t\t'es_ES' => 'Gestionar enlaces personalizados. Arrastra el controlador para reordenar.',\n\t\t\t],\n\t\t];\n\n\t\t\/\/ Hook into WordPress translation filter\n\t\tadd_filter('gettext', [$this, 'l10n_translate'], 10, 3);\n\n\t\t$et = microtime(true);\n\t\tWP_DEBUG && self::$timing and error_log(sprintf('%s%s::%s() Timing: %.5f sec.', '', __CLASS__, __FUNCTION__, $et-$st));\n\t\tself::$total_runtime += $et-$st;\n\t\tdo_action('qm\/stop', __METHOD__); \/\/ Query Monitor Profiling\n\t}\n\n\t\/\/-------------------------------------------------------------------------------------------------------------------\n\t\/**\n\t * Custom translation filter.\n\t * \n\t * @param string $translation Translated text.\n\t * @param string $text Original text to translate.\n\t * @param string $domain Text domain.\n\t * @return string The translated text or original if no custom translation exists.\n\t *\/\n\tpublic function l10n_translate($translation, $text, $domain) {\n\t\tif ($domain !== 'maqn') {return $translation;} \/\/ Only handle our own text domain\n\t\t$retval = $translation;\n\t\t\/\/ Get current user locale\n\t\t$locale = get_user_locale();\n\t\t\/\/ simplify locale for formal\/informal variants\n\t\t$locale = str_replace('_formal', '', $locale);\n\t\t\/\/ Check if we have a custom translation for this text and locale\n\t\tif (isset($this->custom_translations[$text][$locale])) {\n\t\t\t$retval = $this->custom_translations[$text][$locale];\n\t\t}\n\t\treturn $retval;\n\t}\n\n\t\/\/===================================================================================================================\n\t\/\/ SETTINGS\n\t\/\/-------------------------------------------------------------------------------------------------------------------\n\t\/\/-------------------------------------------------------------------------------------------------------------------\n\t\/**\n\t * Creates the Settings menu.\n\t *\/\n\tpublic function admin_menu(){\n\t\tadd_options_page(\t\n\t\t\tself::TITLE.' '.__('Settings'), \/\/ page title\n\t\t\tself::TITLE,\t\t\t\t\t\/\/ menu title\n\t\t\t'manage_options', \t\t\t\t\/\/ capabilitiy\n\t\t\tself::SLUG, \t\t\t\t\t\/\/ menu slug\n\t\t\t[$this, 'settings'] \t\t\t\/\/ function\n\t\t);\n\n\t\t\/\/ @since 1.8.0: Enable Admin Menu for post types with setting \"admin-menu\", like Patterns (wp_block)\n\t\tforeach ($this->settings['post_types'] as $ptn => $pts) {\n\t\t\tif (!($pts['admin-menu']??false)) continue;\n\t\t\t$reg_post_type = get_post_type_object($ptn);\n\t\t\tif (!$reg_post_type) continue;\n\t\t\tif (!current_user_can($reg_post_type->cap->edit_posts)) continue;\n\t\t\t$menu_icon = $reg_post_type->menu_icon;\n\t\t\tif (!$menu_icon) {\n\t\t\t\tswitch ($ptn) {\n\t\t\t\t\tcase 'wp_block': \t\/\/ SVG \"blocks\" icon encoded as base64 data URI\n\t\t\t\t\t\t\t\t\t\t$menu_icon = 'data:image\/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My'.\n\t\t\t\t\t\t\t\t\t\t\t\t\t'5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZmlsbD0'.\n\t\t\t\t\t\t\t\t\t\t\t\t\t'iY3VycmVudENvbG9yIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0i'.\n\t\t\t\t\t\t\t\t\t\t\t\t\t'ZXZlbm9kZCIgZD0iTTIxLjMgMTAuOGwtNS42LTUuNmMtLjctLjctMS44LS43L'.\n\t\t\t\t\t\t\t\t\t\t\t\t\t'TIuNSAwbC01LjYgNS42Yy0uNy43LS43IDEuOCAwIDIuNWw1LjYgNS42Yy4zLj'.\n\t\t\t\t\t\t\t\t\t\t\t\t\t'MuOC41IDEuMi41cy45LS4yIDEuMi0uNWw1LjYtNS42Yy44LS43LjgtMS45LjE'.\n\t\t\t\t\t\t\t\t\t\t\t\t\t'tMi41em0tMSAxLjRsLTUuNiA1LjZjLS4xLjEtLjMuMS0uNCAwbC01LjYtNS42'.\n\t\t\t\t\t\t\t\t\t\t\t\t\t'Yy0uMS0uMS0uMS0uMyAwLS40bDUuNi01LjZzLjEtMS4xLjItMS4xLjEgMCAuM'.\n\t\t\t\t\t\t\t\t\t\t\t\t\t'i4xbDUuNiA1LjZjLjEgMS4xLS4xIDEuMy0uMiAxLjR6bS0xNi42LS40TDEwID'.\n\t\t\t\t\t\t\t\t\t\t\t\t\t'UuNWwtMS0xLTYuMyA2LjNjLS43LjctLjcgMS44IDAgMi41TDkgMTkuNWwxLjE'.\n\t\t\t\t\t\t\t\t\t\t\t\t\t'tMS4xLTYuMy02LjNjLS4yIDAtLjItLjItLjEtLjN6Ii8+PC9zdmc+';break;\n\t\t\t\t\tdefault:\t\t\t$menu_icon = 'dashicons-marker';\n\t\t\t\t}\n\t\t\t}\n\t\t\t$menu_position = null;\n\t\t\tswitch ($ptn) {\n\t\t\t\tcase 'wp_block':\t$menu_position = 22; break;\n\t\t\t\tdefault:\t\t\t$menu_position = null; \/\/ at the end\n\t\t\t}\n\t\t\tadd_menu_page(\n\t\t\t\t$reg_post_type->labels->menu_name,\n\t\t\t\t$reg_post_type->labels->menu_name,\n\t\t\t\t$reg_post_type->cap->edit_posts,\n\t\t\t\t'edit.php?post_type='.$ptn,\n\t\t\t\t'',\n\t\t\t\t$menu_icon,\n\t\t\t\t$menu_position\n\t\t\t);\n\t\t}\n\n\t}\n\t\/\/-------------------------------------------------------------------------------------------------------------------\n\t\/**\n\t * Register the settings and validation method.\n\t *\/\n\tpublic function settings_register() {\n\t\tregister_setting(\t\n\t\t\tself::SLUG, \t\t\t\t\t\/\/ option group\n\t\t\tself::SLUG, \t\t\t\t\t\/\/ option name\n\t\t\t[\n\t\t\t\t'type'\t\t\t\t=> 'array',\n\t\t\t\t'sanitize_callback'\t=> [$this, 'settings_validate'],\n\t\t\t]\n\t\t);\n\t}\n\t\/\/-------------------------------------------------------------------------------------------------------------------\n\t\/**\n\t * Outputs the settings page.\n\t *\/\n\tpublic function settings() {\n\t\t\/\/ check user capabilities\n\t\tif (!current_user_can('manage_options')) return;\n\n\t\t\/\/ show error\/update messages\n\t\tsettings_errors(self::SLUG . '_messages');\n\n\t\t\/\/ get the registered post types\n\t\t$registered_post_types = get_post_types([],'OBJECT');\n\n\t\t\/\/ get the registered post statuses\n\t\t$post_statuses = get_post_statuses();\n\n\t\t\/\/ @since 1.8.0: detect if Gutenberg is enabled for any of the known post types\n\t\t\/\/ we only offer the patterns settings if at least one post type supports Gutenberg editor\n\t\t$gutenberg_enabled_post_types = [];\n\t\tforeach ($registered_post_types as $ptn => $pt) {\n\t\t\tif (post_type_supports($ptn,'editor')) {\n\t\t\t\t$gutenberg_enabled_post_types[] = $ptn;\n\t\t\t}\n\t\t}\n\n\t\t?>\n\t\t<div class=\"wrap maqn <?php echo self::SLUG;?>-settings\" id=\"<?php echo self::SLUG;?>-settings\">\n\n\t\t\t<h1><?php echo esc_html(get_admin_page_title()); ?><\/h1>\n\t\t\t<style>\n\t\t\t\t.maqn h2.fancy {padding:.2rem .5rem; background-image:linear-gradient(to right, lightgray, transparent); border-radius:3px;}\n\t\t\t\t.maqn h3 {font-size: 1.1em;}\n\t\t\t\t.maqn .maqn-spaced {margin-top:3rem;}\n\t\t\t\t.maqn table {max-width:100%; border-radius:3px; overflow:hidden; border-collapse:collapse;}\n\t\t\t\t.maqn table thead {background-color:#dddddd;}\n\t\t\t\t.maqn table tr {border-bottom:1px solid lightgray;}\n\t\t\t\t.maqn table :where(th,td) {border:1px solid #ddd; text-align:left; vertical-align:top; padding:5px;}\n\t\t\t\t.maqn table :where(th,td).sort {width:20px; text-align:center; padding:5px;}\n\t\t\t\t.maqn table :where(th,td).sort svg.sort-handle {color:gray; width:1em; height:1em; padding-top:5px;}\n\t\t\t\t.maqn table td.sort svg.sort-handle {cursor:ns-resize;}\n\t\t\t\tDISABLED <?php echo '.'.self::SLUG;?>-settings input#submit {position:fixed;top:60px;right:50px;}\n\t\t\t<\/style>\n\t\t\t\n\t\t\t<form class=\"maqn-form\" method=\"post\" action=\"options.php\">\n\n\t\t\t\t<?php settings_fields(self::SLUG); ?>\n\n\n\t\t\t\t<div class=\"maqn-general\">\n\t\t\t\t\t<h2><?php _e('General'); ?><\/h2>\n\n\t\t\t\t\t<?php if (self::$expert_mode):  ?>\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<?php $hint = sprintf('Set the position for the %s menu(s)', self::TITLE); ?>\n\t\t\t\t\t\t<label for=\"menu_position\" title=\"<?php echo esc_attr($hint);?>\">\n\t\t\t\t\t\t\t<input type=\"number\" name=\"menu_position\" id=\"menu_position\" value=\"<?php echo esc_attr($this->settings['menu_position']);?>\">\n\t\t\t\t\t\t\t<?php printf(__('Menu Location'), self::TITLE);?>\n\t\t\t\t\t\t<\/label><br\/>\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<?php endif; ?>\n\t\t\t\t\t\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<?php $hint = sprintf(__('Collapse all lists into one menu \"%s\"','maqn'), self::TITLE); ?>\n\t\t\t\t\t\t<label title=\"<?php echo esc_attr($hint);?>\">\n\t\t\t\t\t\t\t<input type=\"checkbox\" name=\"collapsed_menu\" id=\"collapsed_menu\" value=\"1\" <?php checked($this->settings['collapsed_menu']??false,true);?>>\n\t\t\t\t\t\t\t<?php printf(__('Collapsed Menu'), self::TITLE);?>\n\t\t\t\t\t\t<\/label>\n\t\t\t\t\t<\/div>\n\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<?php $hint = sprintf(__('Enable quick search bar','maqn')); ?>\n\t\t\t\t\t\t<label title=\"<?php echo esc_attr($hint);?>\">\n\t\t\t\t\t\t\t<input type=\"checkbox\" name=\"quick_search\" id=\"quick_search\" value=\"1\" <?php checked($this->settings['quick_search']??false,true);?>>\n\t\t\t\t\t\t\t<?php printf(_x('Activate %s','plugin'), esc_html__('Quick Search'));?>\n\t\t\t\t\t\t<\/label>\n\t\t\t\t\t<\/div>\n\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<?php $hint = esc_html(__('Enable \"Add {element}...\" for each post type','maqn')); ?>\n\t\t\t\t\t\t<label title=\"<?php echo esc_attr($hint);?>\">\n\t\t\t\t\t\t\t<input type=\"checkbox\" name=\"add_new\" id=\"add_new\" value=\"1\" <?php checked($this->settings['add_new']??false,true);?>>\n\t\t\t\t\t\t\t<?php printf(_x('Activate %s','plugin'), __('Add Items'))?>\n\t\t\t\t\t\t<\/label>\n\t\t\t\t\t<\/div>\n\n\t\t\t\t\t<?php if (defined('POLYLANG_VERSION')): ?>\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<?php $hint = sprintf(__('Show language for items if defined','maqn')); ?>\n\t\t\t\t\t\t<label title=\"<?php echo esc_attr($hint);?>\">\n\t\t\t\t\t\t\t<input type=\"checkbox\" name=\"show_language\" id=\"show_language\" value=\"1\" <?php checked($this->settings['show_language']??false,true);?>>\n\t\t\t\t\t\t\t<?php printf(_x('Activate %s','plugin'), __('Language'));?>\n\t\t\t\t\t\t<\/label>\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<?php endif; ?>\n\t\t\t\t\t\n\t\t\t\t\t<?php if ($this->builder()): ?>\n\t\t\t\t\t<div>\n\t\t\t\t\t\t<?php $hint = sprintf(__('Enable admin bar in %s builder','maqn'), esc_html($this->builder())); ?>\n\t\t\t\t\t\t<label title=\"<?php echo esc_attr($hint);?>\">\n\t\t\t\t\t\t\t<input type=\"checkbox\" name=\"builder_admin_bar\" id=\"builder_admin_bar\" value=\"1\" <?php checked($this->settings['builder_admin_bar']??false,true);?>>\n\t\t\t\t\t\t\t<?php printf(_x('Activate %s','plugin'), sprintf(__('Admin Bar in %s Builder'), esc_html($this->builder())));?>\n\t\t\t\t\t\t<\/label>\n\t\t\t\t\t<\/div>\n\t\t\t\t\t<?php endif; ?>\n\t\t\t\t<\/div>\n\n\t\t\t\t<div class=\"maqn-post-types maqn-spaced\">\n\t\t\t\t\t<style>\n\t\t\t\t\t.maqn-post-types .ptl input[type='number'] {width:10ch;}\n\t\t\t\t\t.maqn-post-types .ptl .details * {display:none;}\n\t\t\t\t\t.maqn-post-types .ptl .active .details * {display:inline-block;}\n\t\t\t\t\t<\/style>\n\t\t\t\t\t<h2><?php _e('Post Types'); ?><\/h2>\n\t\t\t\t\t<p class=\"description\"><?php _e('Enable Admin Bar Menu for Post Types. Drag the handle to reorder.','maqn'); ?><\/p>\n\t\t\t\t\t<table class=\"ptl\" role=\"presentation\" aria-label=\"Post Types List\">\n\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t<th class=\"sort\" title=\"<?php _e('Reorder');?>\"><svg class=\"sort-handle\"><use xlink:href=\"#icon-sort\"><\/use><\/svg><\/th>\n\t\t\t\t\t\t\t\t<th><?php _e('Post Type'); ?><\/th>\n\t\t\t\t\t\t\t\t<th><?php _e('Sort Order:'); ?><\/th>\n\t\t\t\t\t\t\t\t<th><?php _e('Order'); ?><\/th>\n\t\t\t\t\t\t\t\t<th><?php _ex('Count','Number\/count of items'); ?><\/th>\n\t\t\t\t\t\t\t\t<th><?php _e('Advanced Options'); ?><\/th>\n\t\t\t\t\t\t\t<\/tr>\n\t\t\t\t\t\t<\/thead>\n\n\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t<?php \n\t\t\t\t\t\t\/\/ get post types from settings plus other registered post types\n\t\t\t\t\t\t$post_types = array_unique(array_merge(array_keys($this->settings['post_types']), array_keys(wp_list_pluck($registered_post_types,'name'))));\n\t\t\t\t\t\tforeach ($post_types as $post_type):\n\t\t\t\t\t\t\t\/\/ check if configured post type is still a registered post type\n\t\t\t\t\t\t\tif (!$reg_post_type = $registered_post_types[$post_type]) continue;\n\t\t\t\t\t\t\t\/\/ skip post types that are not configured to be listed in nav menus\n\t\t\t\t\t\t\t\/\/ @since 1.8.0: ... but allow Patterns (wp_block) if Gutenberg is enabled for any of the known post types\n\t\t\t\t\t\t\t$override_nav_menu_property = ($post_type==='wp_block') && !empty($gutenberg_enabled_post_types);\n\t\t\t\t\t\t\tif (!($reg_post_type->show_in_nav_menus??false) && !($override_nav_menu_property)) continue;\n\n\t\t\t\t\t\t\t\/\/ loop valid post types\n\t\t\t\t\t\t\t$ptn = $reg_post_type->name;\n\t\t\t\t\t\t\t$ptl = $reg_post_type->label;\n\t\t\t\t\t\t\t$pts = $this->settings['post_types']??[];\n\t\t\t\t\t\t\t$pto = array_merge([\n\t\t\t\t\t\t\t\t'orderby'\t\t=> self::DEFAULT_ORDERBY,\n\t\t\t\t\t\t\t\t'order'\t\t\t=> self::DEFAULT_ORDER,\n\t\t\t\t\t\t\t\t'limit'\t\t\t=> self::DEFAULT_LIMIT,\n\t\t\t\t\t\t\t\t'hierarchical'\t=> false,\n\t\t\t\t\t\t\t\t'happyfiles'\t=> false,\n\t\t\t\t\t\t\t\t'builder' \t\t=> false, \n\t\t\t\t\t\t\t],$pts[$ptn] ?? []);\n\n\t\t\t\t\t\t\t$label = esc_html($ptl) . ' (<code>'.$ptn.'<\/code>)'; \n\t\t\t\t\t\t\t\/\/ label adaption for special cases\n\t\t\t\t\t\t\tif ($ptn == 'wp_block') {$label = 'Gutenberg '.$label;}\n\t\t\t\t\t\t\tif ($ptn == 'ct_template') {$label = 'Oxygen '.$label;}\n\t\t\t\t\t\t\t?>\n\t\t\t\t\t\t\t<tr class=\"post_type_row <?php echo in_array($ptn,array_keys($pts)) ? 'active' : '';?>\">\n\t\t\t\t\t\t\t\t<td class=\"sort\" title=\"<?php _e('Reorder');?>\">\n\t\t\t\t\t\t\t\t\t<svg class=\"sort-handle\"><use xlink:href=\"#icon-sort\"><\/use><\/svg>\n\t\t\t\t\t\t\t\t<\/td>\n\t\t\t\t\t\t\t\t<td>\n\t\t\t\t\t\t\t\t\t<label>\n\t\t\t\t\t\t\t\t\t\t<input type=\"checkbox\" name=\"post_types[]\" id=\"post_type_<?php echo esc_attr($ptn);?>\" value=\"<?php echo esc_attr($ptn);?>\" <?php checked(in_array($ptn,array_keys($pts)), true);?>>\n\t\t\t\t\t\t\t\t\t\t<?php echo $label;?>\n\t\t\t\t\t\t\t\t\t<\/label>\n\t\t\t\t\t\t\t\t<\/td>\n\t\t\t\t\t\t\t\t<td class=\"details\">\n\t\t\t\t\t\t\t\t\t<select name=\"<?php echo esc_attr($ptn);?>-orderby\" id=\"<?php echo esc_attr($ptn);?>-orderby\">\n\t\t\t\t\t\t\t\t\t\t<option value=\"post_date\" <?php selected($pto['orderby'], 'post_date'); ?>><?php _e('Created');?><\/option>\n\t\t\t\t\t\t\t\t\t\t<option value=\"post_title\" <?php selected($pto['orderby'], 'post_title'); ?>><?php _e('Title');?><\/option>\n\t\t\t\t\t\t\t\t\t\t<option value=\"menu_order\" <?php selected($pto['orderby'], 'menu_order'); ?>><?php _e('Menu Order');?><\/option>\n\t\t\t\t\t\t\t\t\t<\/select> \n\t\t\t\t\t\t\t\t<\/td>\n\t\t\t\t\t\t\t\t<td class=\"details\">\n\t\t\t\t\t\t\t\t\t<select name=\"<?php echo esc_attr($ptn);?>-order\" id=\"<?php echo esc_attr($ptn);?>-order\">\n\t\t\t\t\t\t\t\t\t\t<option value=\"ASC\" <?php selected($pto['order'], 'ASC'); ?>><?php _e('Ascending');?><\/option>\n\t\t\t\t\t\t\t\t\t\t<option value=\"DESC\" <?php selected($pto['order'], 'DESC'); ?>><?php _e('Descending');?><\/option>\n\t\t\t\t\t\t\t\t\t<\/select> \n\t\t\t\t\t\t\t\t<\/td>\n\t\t\t\t\t\t\t\t<td class=\"details\">\n\t\t\t\t\t\t\t\t\t<input type=\"number\" name=\"<?php echo esc_attr($ptn);?>-limit\" id=\"<?php echo esc_attr($ptn);?>-limit\" value=\"<?php echo esc_attr($pto['limit']);?>\">\n\t\t\t\t\t\t\t\t<\/td>\n\t\t\t\t\t\t\t\t<td class=\"details\">\n\t\t\t\t\t\t\t\t\t<?php if ($reg_post_type->hierarchical === true): \n\t\t\t\t\t\t\t\t\t\t$hint = __('Show hierarchy by indenting list','maqn'); ?>\n\t\t\t\t\t\t\t\t\t\t<label title=\"<?php echo esc_attr($hint);?>\">\n\t\t\t\t\t\t\t\t\t\t\t<input type=\"checkbox\" name=\"<?php echo esc_attr($ptn);?>-hierarchical\" id=\"<?php echo esc_attr($ptn);?>-hierarchical\" value=\"1\" <?php checked($pto['hierarchical'],true);?>>\n\t\t\t\t\t\t\t\t\t\t\t<?php _e('Show hierarchy');?>\n\t\t\t\t\t\t\t\t\t\t<\/label>\n\t\t\t\t\t\t\t\t\t\t<br>\n\t\t\t\t\t\t\t\t\t<?php endif; ?>\n\t\t\t\t\t\t\t\t\t<?php if (defined('HAPPYFILES_VERSION') && in_array($ptn, $this->get_happyfiles_post_types())):\n\t\t\t\t\t\t\t\t\t\t$hint = __('Group by HappyFiles folders','maqn'); ?>\n\t\t\t\t\t\t\t\t\t\t<label title=\"<?php echo esc_attr($hint);?>\">\n\t\t\t\t\t\t\t\t\t\t\t<input type=\"checkbox\" name=\"<?php echo esc_attr($ptn);?>-happyfiles\" id=\"<?php echo esc_attr($ptn);?>-happyfiles\" value=\"1\" <?php checked($pto['happyfiles'],true);?>>\n\t\t\t\t\t\t\t\t\t\t\t<?php echo 'HappyFiles '.esc_html__( 'Folders', 'happyfiles' );?>\n\t\t\t\t\t\t\t\t\t\t<\/label>\n\t\t\t\t\t\t\t\t\t\t<br>\n\t\t\t\t\t\t\t\t\t<?php endif; ?>\n\t\t\t\t\t\t\t\t\t<?php if ($this->builder() && $this->builder_post_type($ptn)): \n\t\t\t\t\t\t\t\t\t\t$hint = sprintf(__('Show icon to allow editing in %s','maqn'), $this->builder()); ?>\n\t\t\t\t\t\t\t\t\t\t<label title=\"<?php echo esc_attr($hint);?>\">\n\t\t\t\t\t\t\t\t\t\t\t<input type=\"checkbox\" name=\"<?php echo esc_attr($ptn);?>-builder\" id=\"<?php echo esc_attr($ptn);?>-builder\" value=\"1\" <?php checked($pto['builder']??false,true);?>>\n\t\t\t\t\t\t\t\t\t\t\t<?php printf(__('Edit').' in %s Builder',$this->builder());?>\n\t\t\t\t\t\t\t\t\t\t<\/label>\n\t\t\t\t\t\t\t\t\t\t<br>\n\t\t\t\t\t\t\t\t\t<?php endif; ?>\n\t\t\t\t\t\t\t\t\t<?php if ($ptn === 'wp_block'): \/* Settings for Patterns *\/\n\t\t\t\t\t\t\t\t\t\t$hint = __('Show in Admin Menu','maqn'); ?>\n\t\t\t\t\t\t\t\t\t\t<label title=\"<?php echo esc_attr($hint);?>\">\n\t\t\t\t\t\t\t\t\t\t\t<input type=\"checkbox\" name=\"<?php echo esc_attr($ptn);?>-admin-menu\" id=\"<?php echo esc_attr($ptn);?>-admin-menu\" value=\"1\" <?php checked($pto['admin-menu']??false,true);?>>\n\t\t\t\t\t\t\t\t\t\t\t<?php echo (__('Admin').' '.__('Menu Item'));?>\n\t\t\t\t\t\t\t\t\t\t<\/label>\n\t\t\t\t\t\t\t\t\t\t<br>\n\t\t\t\t\t\t\t\t\t<?php endif; ?>\n\t\t\t\t\t\t\t\t<\/td>\n\t\t\t\t\t\t\t<\/tr>\n\t\t\t\t\t\t\t<?php\n\t\t\t\t\t\tendforeach;\n\t\t\t\t\t\t?>\n\t\t\t\t\t\t<\/tbody>\n\t\t\t\t\t<\/table>\n\t\t\t\t\t<script>\n\t\t\t\t\tdocument.querySelectorAll('input[name^=\"post_types[]\"]').forEach($c=>{\n\t\t\t\t\t\t$c.addEventListener('change',($e)=>{\n\t\t\t\t\t\t\tlet $r = $e.currentTarget.closest('.post_type_row');\n\t\t\t\t\t\t\tif ($e.currentTarget.checked) {$r.classList.add('active');} \n\t\t\t\t\t\t\telse {$r.classList.remove('active');}\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\t\t\t\t\tjQuery(document).ready(function(){\n\t\t\t\t\t\tjQuery('table.ptl tbody').sortable({\n\t\t\t\t\t\t\thandle: '.sort-handle',\n\t\t\t\t\t\t\tcursor: 'ns-resize',\n\t\t\t\t\t\t\taxis:   'y',\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\t\t\t\t\t<\/script>\n\n\n\t\t\t\t\t<div class=\"maqn-post-status\">\n\t\t\t\t\t\t<h3>Status<\/h3>\n\t\t\t\t\t\t<?php foreach ($post_statuses as $status => $status_label): \n\t\t\t\t\t\t\t$hint = sprintf(__('List items with status &quot;%s&quot;','maqn'), $status_label); ?>\n\t\t\t\t\t\t\t<label title=\"<?php echo esc_attr($hint);?>\">\n\t\t\t\t\t\t\t\t<input type=\"checkbox\" name=\"status[]\" id=\"status-<?php echo esc_attr($status);?>\" value=\"<?php echo esc_attr($status);?>\" <?php checked(in_array($status,$this->settings['status']),true);?>> \n\t\t\t\t\t\t\t\t<?php echo esc_html($status_label);?>\n\t\t\t\t\t\t\t<\/label><br\/>\n\t\t\t\t\t\t<?php endforeach;?>\n\t\t\t\t\t<\/div>\n\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"maqn-custom-links maqn-spaced\">\n\t\t\t\t\t<?php \/* @since 1.8.0: Custom Links *\/ ?>\n\t\t\t\t\t<h2><?php _e('Custom Links'); ?><\/h2>\n\t\t\t\t\t<?php\n\t\t\t\t\t\/\/ Custom Links manager (Title \/ URL) - sortable, add, delete\n\t\t\t\t\t$custom_links = $this->settings['custom-links'] ?? [];\n\t\t\t\t\t?>\n\t\t\t\t\t<style>\n\t\t\t\t\t.maqn-custom-links th.title {min-width:300px;}\n\t\t\t\t\t.maqn-custom-links th.url {min-width:400px;}\n\t\t\t\t\t.maqn-custom-links .actions {white-space:nowrap;}\n\t\t\t\t\t.maqn-custom-links input[type=\"text\"] {width:100%;}\n\t\t\t\t\t.maqn-custom-links input[type=\"text\"]::placeholder {color:lightgray;}\n\t\t\t\t\t.maqn-custom-links .add-btn {margin-top:.5em;}\n\t\t\t\t\t<\/style>\n\n\t\t\t\t\t<div id=\"maqn-custom-links\">\n\t\t\t\t\t\t<p class=\"description\"><?php _e('Manage custom links. Drag the handle to reorder.','maqn'); ?>\n\t\t\t\t\t\t\t<?php if (self::$custom_link_remove_site_url): ?>\n\t\t\t\t\t\t\t<br><i>Base URL <code><?php echo esc_html(site_url()); ?><\/code> will be removed from URLs automatically.<\/i>\n\t\t\t\t\t\t\t<?php endif; ?>\n\t\t\t\t\t\t<\/p>\n\t\t\t\t\t\t<table class=\"cll\" role=\"presentation\" aria-label=\"Custom Links\">\n\t\t\t\t\t\t\t<thead>\n\t\t\t\t\t\t\t\t<tr>\n\t\t\t\t\t\t\t\t\t<th class=\"sort\" title=\"<?php _e('Reorder');?>\"><svg class=\"sort-handle\"><use xlink:href=\"#icon-sort\"><\/use><\/svg><\/th>\n\t\t\t\t\t\t\t\t\t<th class=\"title\"><?php _e('Title'); ?><\/th>\n\t\t\t\t\t\t\t\t\t<th class=\"url\"><?php _e('URL'); ?><\/th>\n\t\t\t\t\t\t\t\t\t<th class=\"actions\"><?php _e('Actions'); ?><\/th>\n\t\t\t\t\t\t\t\t<\/tr>\n\t\t\t\t\t\t\t<\/thead>\n\t\t\t\t\t\t\t<tbody id=\"maqn-custom-links-body\">\n\t\t\t\t\t\t\t\t<?php foreach ($custom_links as $idx => $entry): \n\t\t\t\t\t\t\t\t\t$title = esc_attr($entry['title'] ?? '');\n\t\t\t\t\t\t\t\t\t$url   = esc_attr($entry['url'] ?? '');\n\t\t\t\t\t\t\t\t?>\n\t\t\t\t\t\t\t\t<tr class=\"maqn-custom-link-row\">\n\t\t\t\t\t\t\t\t\t<td class=\"sort\" title=\"<?php _e('Reorder'); ?>\">\n\t\t\t\t\t\t\t\t\t\t<svg class=\"sort-handle\"><use xlink:href=\"#icon-sort\"><\/use><\/svg>\n\t\t\t\t\t\t\t\t\t<\/td>\n\t\t\t\t\t\t\t\t\t<td class=\"title\">\n\t\t\t\t\t\t\t\t\t\t<input type=\"text\" class=\"custom-link-title\" value=\"<?php echo $title;?>\" placeholder=\"<?php _e('Title');?>\">\n\t\t\t\t\t\t\t\t\t<\/td>\n\t\t\t\t\t\t\t\t\t<td class=\"url\">\n\t\t\t\t\t\t\t\t\t<input type=\"text\" class=\"custom-link-url\" value=\"<?php echo $url;?>\" placeholder=\"<?php _e('URL');?>\">\n\t\t\t\t\t\t\t\t\t<\/td>\n\t\t\t\t\t\t\t\t\t<td class=\"actions\">\n\t\t\t\t\t\t\t\t\t\t<button type=\"button\" class=\"button maqn-custom-link-remove\"><?php _e('Delete'); ?><\/button>\n\t\t\t\t\t\t\t\t\t<\/td>\n\t\t\t\t\t\t\t\t<\/tr>\n\t\t\t\t\t\t\t\t<?php endforeach; ?>\n\t\t\t\t\t\t\t<\/tbody>\n\t\t\t\t\t\t<\/table>\n\n\t\t\t\t\t\t<button type=\"button\" class=\"button add-btn\" id=\"maqn-custom-link-add\"><?php _e('Add Link'); ?><\/button>\n\n\t\t\t\t\t\t<!-- Template row (hidden) -->\n\t\t\t\t\t\t<table style=\"display:none;\">\n\t\t\t\t\t\t\t<tbody>\n\t\t\t\t\t\t\t\t<tr id=\"maqn-custom-link-template\" class=\"maqn-custom-link-row\">\n\t\t\t\t\t\t\t\t\t<td class=\"sort\" title=\"<?php _e('Reorder'); ?>\">\n\t\t\t\t\t\t\t\t\t\t<svg class=\"sort-handle\"><use xlink:href=\"#icon-sort\"><\/use><\/svg>\n\t\t\t\t\t\t\t\t\t<\/td>\n\t\t\t\t\t\t\t\t\t<td><input type=\"text\" class=\"custom-link-title\" value=\"\" placeholder=\"<?php _e('Title');?>\"><\/td>\n\t\t\t\t\t\t\t\t\t<td><input type=\"text\" class=\"custom-link-url\" value=\"\" placeholder=\"<?php _e('URL');?>\"><\/td>\n\t\t\t\t\t\t\t\t\t<td class=\"actions\"><button type=\"button\" class=\"button maqn-custom-link-remove\"><?php _e('Delete'); ?><\/button><\/td>\n\t\t\t\t\t\t\t\t<\/tr>\n\t\t\t\t\t\t\t<\/tbody>\n\t\t\t\t\t\t<\/table>\n\n\t\t\t\t\t\t<!-- Hidden field that will hold the serialized list on submit -->\n\t\t\t\t\t\t<input type=\"hidden\" name=\"custom-links-serialized\" id=\"maqn-custom-links-serialized\" value=\"\">\n\t\t\t\t\t<\/div>\n\n\t\t\t\t\t<script>\n\t\t\t\t\t(function($){\n\t\t\t\t\t\t$(function(){\n\t\t\t\t\t\t\tconst $base_url = '<?php echo esc_url(site_url()); ?>';\n\t\t\t\t\t\t\tvar $body = $('#maqn-custom-links-body');\n\t\t\t\t\t\t\tvar $template = $('#maqn-custom-link-template');\n\n\t\t\t\t\t\t\tjQuery(document).ready(function(){\n\t\t\t\t\t\t\t\tjQuery('table.cll tbody').sortable({\n\t\t\t\t\t\t\t\t\thandle: '.sort-handle',\n\t\t\t\t\t\t\t\t\tcursor: 'ns-resize',\n\t\t\t\t\t\t\t\t\taxis:   'y',\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t\/\/ Add new row\n\t\t\t\t\t\t\t$('#maqn-custom-link-add').on('click', function(e){\n\t\t\t\t\t\t\t\tvar $n = $template.clone().removeAttr('id').show();\n\t\t\t\t\t\t\t\t$body.append($n);\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t\/\/ Remove row (delegated)\n\t\t\t\t\t\t\t$body.on('click', '.maqn-custom-link-remove', function(e){\n\t\t\t\t\t\t\t\t$(this).closest('tr').remove();\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t<?php if (self::$custom_link_remove_site_url): ?>\n\t\t\t\t\t\t\t\/\/ On URL field focus and blur, remove $base_url from the beginning of the string\n\t\t\t\t\t\t\t$body.on('focus blur', '.custom-link-url', function(e){\n\t\t\t\t\t\t\t\tvar $url_field = $(this);\n\t\t\t\t\t\t\t\tvar $url_value = $url_field.val();\n\t\t\t\t\t\t\t\tif ($url_value.indexOf($base_url) === 0) {\n\t\t\t\t\t\t\t\t\t$url_field.val($url_value.substring($base_url.length));\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t<?php endif; ?>\n\n\t\t\t\t\t\t\t\/\/ When the form is submitted, serialize rows into hidden input as JSON\n\t\t\t\t\t\t\t$('form').on('submit', function(){\n\t\t\t\t\t\t\t\tvar data = [];\n\t\t\t\t\t\t\t\t$body.find('tr.maqn-custom-link-row').each(function(){\n\t\t\t\t\t\t\t\t\tvar title = $(this).find('.custom-link-title').val() || '';\n\t\t\t\t\t\t\t\t\tvar url = $(this).find('.custom-link-url').val() || '';\n\t\t\t\t\t\t\t\t\t\/\/ trim\n\t\t\t\t\t\t\t\t\ttitle = $.trim(title);\n\t\t\t\t\t\t\t\t\turl = $.trim(url);\n\t\t\t\t\t\t\t\t\t\/\/ skip empty rows (both empty)\n\t\t\t\t\t\t\t\t\tif (title === '' && url === '') return;\n\t\t\t\t\t\t\t\t\tdata.push({title: title, url: url});\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t$('#maqn-custom-links-serialized').val(JSON.stringify(data));\n\t\t\t\t\t\t\t\t\/\/ allow submission to continue\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\t\/\/ ensure existing rows are valid inputs for accessibility (no automatic name; saved via serialized field)\n\t\t\t\t\t\t});\n\t\t\t\t\t})(jQuery);\n\t\t\t\t\t<\/script>\n\t\t\t\t<\/div>\n\n\t\t\t\t<?php submit_button(); ?>\n\t\t\t<\/form>\n\t\t<\/div>\n\t\t<div style=\"margin-top:3em;border-top:1px solid lightgray;font-size:70%;\">\n\t\t\t\u00a9 2024-2025, <a href=\"https:\/\/www.altmann.de\/\" target=\"_blank\">Matthias Altmann<\/a> \n\t\t\t\u2022 <?php echo basename(__FILE__) == 'ma-admin-quick-nav.php' ? 'Plugin' : 'Code Snippet';?> Version <?php echo esc_html(self::VERSION);?> \n\t\t\t\u2022 <a href=\"https:\/\/www.altmann.de\/en\/blog-en\/code-snippet-admin-quick-nav\/\" target=\"_blank\">Downloads &amp; Documentation<\/a>\n\t\t<\/div>\n\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" xmlns:xlink=\"http:\/\/www.w3.org\/1999\/xlink\" style=\"position: absolute; width: 0; height: 0; overflow: hidden;\" version=\"1.1\">\n\t\t\t<defs>\n\t\t\t\t<symbol id=\"icon-sort\" viewBox=\"0 0 500 500\">\n\t\t\t\t\t<title><?php _e('Reorder'); ?><\/title>\n\t\t\t\t\t<path fill=\"currentColor\" d=\"M285.714 303.571q0 7.254-5.301 12.556l-125 125q-5.301 5.301-12.556 \n\t\t\t\t\t5.301t-12.556-5.301l-125-125q-5.301-5.301-5.301-12.556t5.301-12.556 12.556-5.301h250q7.254 0 12.556 \n\t\t\t\t\t5.301t5.301 12.556zM285.714 196.429q0 7.254-5.301 12.556t-12.556 5.301h-250q-7.254 0-12.556-5.301t-5.301-12.556 \n\t\t\t\t\t5.301-12.556l125-125q5.301-5.301 12.556-5.301t12.556 5.301l125 125q5.301 5.301 5.301 12.556z\" \/>\n\t\t\t\t<\/symbol>\n\t\t\t<\/defs>\n\t\t<\/svg>\n\t\t<?php\n\t}\n\t\/\/-------------------------------------------------------------------------------------------------------------------\n\t\/**\n\t * Validates the submitted settings.\n\t *\/\n\tpublic function settings_validate() {\n\t\t$input = $_REQUEST;\n\t\t$valid = [];\n\n\t\t\/\/ general settings\n\t\tif ($menu_position = $input['menu_position'] ?? null) {\n\t\t\t$valid['menu_position'] = intval($menu_position);\n\t\t}\n\t\tif ($input['collapsed_menu']??'' == '1') {\n\t\t\t$valid['collapsed_menu'] = true;\n\t\t}\n\t\tif ($input['quick_search']??'' == '1') {\n\t\t\t$valid['quick_search'] = true;\n\t\t}\n\t\tif ($input['add_new']??'' == '1') {\n\t\t\t$valid['add_new'] = true;\n\t\t}\n\t\tif ($input['show_language']??'' == '1') {\n\t\t\t$valid['show_language'] = true;\n\t\t}\n\t\tif ($input['builder_admin_bar']??'' == '1') {\n\t\t\t$valid['builder_admin_bar'] = true;\n\t\t}\n\n\n\t\t\/\/ check selected post types\n\t\t$valid['post_types'] = [];\n\t\t$allowed_post_types = wp_list_pluck(get_post_types([],'OBJECT'),'name');\n\t\tforeach(($input['post_types']??[]) as $ptn) {\n\t\t\tif (in_array($ptn, $allowed_post_types)) {\n\t\t\t\t$pto = [];\n\t\t\t\tif (in_array($input[$ptn.'-orderby']??'',self::VALID_ORDERBY)) {\n\t\t\t\t\t$pto['orderby'] = $input[$ptn.'-orderby'];\n\t\t\t\t}\n\t\t\t\tif (in_array($input[$ptn.'-order']??'',self::VALID_ORDER)) {\n\t\t\t\t\t$pto['order'] = $input[$ptn.'-order'];\n\t\t\t\t}\n\t\t\t\t$limit = $input[$ptn.'-limit'] ?? null;\n\t\t\t\tif (isset($limit)) {\n\t\t\t\t\t$pto['limit'] = '';\n\t\t\t\t\tif ($limit !== '') {\n\t\t\t\t\t\t$limit = intval($limit);\n\t\t\t\t\t\tif ($limit !== 0) {\n\t\t\t\t\t\t\t$pto['limit'] = $limit;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif ($input[$ptn.'-hierarchical']??'') {\n\t\t\t\t\t$pto['hierarchical'] = true;\n\t\t\t\t}\n\t\t\t\tif ($input[$ptn.'-happyfiles']??'') {\n\t\t\t\t\t$pto['happyfiles'] = true;\n\t\t\t\t}\n\t\t\t\tif ($input[$ptn.'-builder']??'') {\n\t\t\t\t\t$pto['builder'] = true;\n\t\t\t\t}\n\t\t\t\tif ($input[$ptn.'-admin-menu']??'') {\n\t\t\t\t\t$pto['admin-menu'] = true;\n\t\t\t\t}\n\t\t\t\t$valid['post_types'][$ptn] = $pto;\n\t\t\t}\n\t\t}\n\t\tif ($input['status']??null) {\n\t\t\t$valid['status'] = $input['status'];\n\t\t}\n\n\t\t\/\/ @since 1.8.0: custom links\n\t\t$valid['custom-links'] = [];\n\t\tif (!empty($input['custom-links-serialized']??null)) {\n\t\t\t$custom_links = json_decode(stripslashes($input['custom-links-serialized']), true);\n\t\t\tif (is_array($custom_links)) {\n\t\t\t\tforeach ($custom_links as $entry) {\n\t\t\t\t\t$title = sanitize_text_field($entry['title'] ?? '');\n\t\t\t\t\t$url = esc_url_raw($entry['url'] ?? '');\n\t\t\t\t\tif ($title !== '' || $url !== '') {\n\t\t\t\t\t\t$valid['custom-links'][] = [\n\t\t\t\t\t\t\t'title'\t=> $title,\n\t\t\t\t\t\t\t'url'\t=> $url,\n\t\t\t\t\t\t];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn $valid;\n\t}\n\n\n\n\n\t\/\/===================================================================================================================\n\t\/\/ ADMIN BAR\n\t\/\/-------------------------------------------------------------------------------------------------------------------\n\t\/**\n\t * Enables admin bar in builders.\n\t *\/\n\tpublic function builder_admin_bar() {\n\t\tif ($this->builder('Oxygen')) {\n\t\t\t\/\/ disable Oxygen's admin bar removal\n\t\t\tremove_action('init','ct_hide_admin_bar');\n\t\t\t\/\/ adds Oxygen builder style adaptions if necessary\n\t\t\tadd_action('wp_enqueue_scripts', [$this,'builder_css_oxygen']);\n\t\t\tadd_action('init', function(){\n\t\t\t\t\/\/ don't show admin bar in builder iframe\n\t\t\t\tif (defined('OXYGEN_IFRAME')) {\n\t\t\t\t\tadd_filter('show_admin_bar','__return_false');\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t\tif ($this->builder('Bricks')) {\n\t\t\t\/\/ adds Bricks builder style adaptions if necessary\n\t\t\tadd_action('wp_head', [$this,'builder_css_bricks']);\n\t\t\t\/\/ show admin bar in Bricks\n\t\t\tadd_action('init', function () {\n\t\t\t\t\/\/ only if Bricks editor active\n\t\t\t\tif (function_exists('bricks_is_builder_main') && call_user_func('bricks_is_builder_main')) {\n\t\t\t\t\tadd_filter('show_admin_bar', '__return_true');\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\t\/\/-------------------------------------------------------------------------------------------------------------------\n\t\/**\n\t * Emits CSS to adapt Oxygen builder.\n\t *\/\n\tpublic function builder_css_oxygen() {\n\t\t\/\/ only for loading Oxygen UI, but not in preview iframe\n\t\tif (!defined('SHOW_CT_BUILDER')) return;\n\t\tif (defined('OXYGEN_IFRAME'))\treturn;\n\t\tif (!defined('CT_VERSION')) return;\t\n\n\t\t\/\/ Oxygen pre 4.9\n\t\tif (version_compare(constant('CT_VERSION'),'4.9','<')) {\n\t\t\t$builder_css = <<<STYLE_END\n\t\t\t\t\/* ma-admin-quick-nav for Oxygen .. 4.8.3 *\/\n\t\t\t\t\/* new top *\/\n\t\t\t\t#oxygen-ui .oxygen-add-section-library-flyout-panel,\n\t\t\t\t#oxygen-ui .ct-panel-elements-managers,\n\t\t\t\t#oxygen-ui .oxygen-global-settings {\n\t\t\t\t\ttop: calc(40px + var(--wp-admin--admin-bar--height,0px));\n\t\t\t\t}\n\t\t\t\t#ct-viewport-container #ct-viewport-ruller-wrap {\n\t\t\t\t\ttop: calc(100vh - 40px -  var(--wp-admin--admin-bar--height,0px));\n\t\t\t\t}\n\t\t\t\t\/* new height *\/\n\t\t\t\t#oxygen-ui .oxygen-add-section-library-flyout-panel,\n\t\t\t\t#oxygen-ui #oxygen-sidebar,\n\t\t\t\t#ct-viewport-container #ct-viewport-ruller-wrap {\n\t\t\t\t\theight: calc(100vh - 40px - var(--wp-admin--admin-bar--height,0px));\n\t\t\t\t}\n\t\t\t\t#ct-dialog-window .oxygen-data-dialog {\n\t\t\t\t\theight: 80%;\n\t\t\t\t}\n\t\t\t\t#oxygen-ui #ct-dom-tree-2 {\n\t\t\t\t\theight: calc(100vh - 119px - var(--wp-admin--admin-bar--height,0px));\n\t\t\t\t}\nSTYLE_END;\n\t\t}\n\t\tif (version_compare(constant('CT_VERSION'),'4.9','>=')) {\n\t\t\t$builder_css = <<<STYLE_END\n\t\t\t\t\/* ma-admin-quick-nav for Oxygen 4.9 .. *\/\n\t\t\t\t#oxygen-ui {\n\t\t\t\t\tposition: fixed;\n\t\t\t\t\twidth: 100%;\n\t\t\t\t}\n\t\t\t\t#oxygen-ui #oxygen-topbar .oxygen-zoom-control .oxygen-zoom-icon {\n\t\t\t\t\theight: 40px;\n\t\t\t\t}\n\t\t\t\t#oxygen-ui #oxygen-sidebar {\n\t\t\t\t\theight: calc(100vh - 36px - var(--wp-admin--admin-bar--height,0px));\n\t\t\t\t}\t\n\t\t\t\t#oxygen-ui .oxygen-add-section-library-flyout-panel {\n\t\t\t\t\ttop: calc(58px + var(--wp-admin--admin-bar--height,0px));\n\t\t\t\t\theight: calc(100vh - 58px - var(--wp-admin--admin-bar--height,0px));\n\t\t\t\t}\n\t\t\t\t#oxygen-ui .ct-panel-elements-managers,\n\t\t\t\t#oxygen-ui .oxygen-global-settings {\n\t\t\t\t\ttop: calc(40px + var(--wp-admin--admin-bar--height,0px));\n\t\t\t\t}\n\t\t\t\t#oxygen-ui .oxygen-add-sidebar {\n\t\t\t\t\theight: calc(100% - var(--wp-admin--admin-bar--height,0px));\n\t\t\t\t}\n\t\t\t\tbody.admin-bar #ct-viewport-container {\n\t\t\t\t\tposition: fixed;\n\t\t\t\t\tmargin-top: var(--wp-admin--admin-bar--height,0px);\n\t\t\t\t}\nSTYLE_END;\n\t\t}\n\n\t\techo '<style id=\"'.esc_attr(self::SLUG.'-css-oxygen').'\">'.wp_strip_all_tags($builder_css).'<\/style>';\n\t}\n\t\/\/-------------------------------------------------------------------------------------------------------------------\n\t\/**\n\t * Emits CSS to adapt Bricks builder.\n\t *\/\n\tpublic function builder_css_bricks() {\n\t\tif (!function_exists('bricks_is_builder_main') || !call_user_func('bricks_is_builder_main')) return;\n\t\t$slug = self::SLUG;\n\t\t$shrt = self::SHRT;\n\t\t\/\/ checked with Bricks 1.9.5, 2.0-alpha, 2.0-beta\n\t\t\/\/ Bricks pre 2.0 has toolbar height of 40px, 2.0 has --builder-toolbar-height = 48px\n\t\t$builder_css = <<<STYLE_END\n\t\t\t\/* ma-admin-quick-nav for Bricks *\/\n\t\t\tbody.admin-bar #bricks-toolbar {\n\t\t\t\ttop: var(--wp-admin--admin-bar--height,0px);\n\t\t\t}\n\t\t\tbody.admin-bar #bricks-panel, body.admin-bar #bricks-preview, body.admin-bar #bricks-structure {\n\t\t\t\ttop: calc( var(--wp-admin--admin-bar--height,0px) + var(--builder-toolbar-height,40px) );\n\t\t\t\theight: calc( 100vh - var(--wp-admin--admin-bar--height,0px) - var(--builder-toolbar-height,40px) );\n\t\t\t}\n\t\t\t\/* adaptions for Advanced Themer 2.9 *\/\n\t\t\thtml body[data-superpower-css=\"true\"] [data-controlkey].full-screen, html #advancedCSSUI__panel.full-screen {\n\t\t\t\ttop: var(--wp-admin--admin-bar--height,0px);\n\t\t\t}\n\t\t\t#wpadminbar .{$shrt}-post-list .{$shrt}-search input {padding: 5px !important;}\nSTYLE_END;\n\t\techo '<style id=\"'.self::SLUG.'-css-bricks\">'.$builder_css.'<\/style>';\n\t}\n\t\n\t\/\/-------------------------------------------------------------------------------------------------------------------\n\t\/**\n\t * Enqueues jQuery sortable for settings page. \n\t *\/\n\tpublic function admin_scripts(){\n\t\tif (function_exists('get_current_screen')) {\n\t\t\t$screen = get_current_screen();\n\t\t\tif ($screen->id == 'settings_page_'.self::SLUG) {\n\t\t\t\twp_enqueue_script('jquery-ui-sortable');\n\t\t\t}\n\t\t}\n\t}\n\t\/\/-------------------------------------------------------------------------------------------------------------------\n\t\/**\n\t * Emits script for search \n\t *\/\n\tpublic function search_script(){\n\t\tif (!is_admin_bar_showing()) return;\n\t\tif (!is_user_logged_in()) return;\n\n\t\tdo_action('qm\/start', __METHOD__); \/\/ Query Monitor Profiling\n\t\t$st = microtime(true);\n\t\t$slug = self::SLUG;\n\t\t$shrt = self::SHRT;\n\t\t$script = <<<SCRIPT_END\n\t\t\t<script id=\"{$slug}-script\">\n\t\t\t'use strict';\n\t\t\tdocument.addEventListener('DOMContentLoaded',(evt) => {\n\t\t\t\t{$shrt}_search_init();\n\t\t\t});\n\t\t\tfunction {$shrt}_search_init() {\n\t\t\t\tconst {$shrt}_menu_observer_callback = (mutationList, observer) => {\n\t\t\t\t\tfor (let mutation of mutationList) {\n\t\t\t\t\t\tif ((mutation.type==='attributes') && (mutation.attributeName==='class')) {\n\t\t\t\t\t\t\tif (mutation.target.classList.contains('hover')) {\n\t\t\t\t\t\t\t\tlet search = mutation.target.querySelector('input[name=search]');\n\t\t\t\t\t\t\t\tsetTimeout(s=>{s.focus();}, 100, search);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tconst {$shrt}_observer = new MutationObserver({$shrt}_menu_observer_callback);\n\t\t\t\tdocument.querySelectorAll('.{$shrt}-post-list').forEach( post_list_menu =>{\n\t\t\t\t\t{$shrt}_observer.observe(post_list_menu, {attributes:true});\n\t\t\t\t});\n\t\t\t\tdocument.querySelectorAll('.{$shrt}-post-list input[name=search]').forEach( search_field =>{\n\t\t\t\t\t\/* handle search input *\/\n\t\t\t\t\tsearch_field.addEventListener('keyup',function(evt){\n\t\t\t\t\t\tlet term = this.value.toLowerCase();\n\t\t\t\t\t\tlet plist = this.closest('ul.ab-submenu');\n\t\t\t\t\t\tif (!plist) return;\n\t\t\t\t\t\tplist.querySelectorAll('.{$shrt}-post a.ab-item').forEach( post_link =>{\n\t\t\t\t\t\t\tpost_link.closest('li').style.display = (post_link.innerText.toLowerCase().indexOf(term)==-1) ? 'none' : 'flex';\n\t\t\t\t\t\t});\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t}\n\t\t\t<\/script>\nSCRIPT_END;\n\t\tif (self::MINIMIZE_JS) {\n\t\t\t$script = preg_replace('\/\\\/\\*.*?\\*\\\/\/s','',$script);  \/\/ remove comments \n\t\t\t$script = preg_replace('\/\\r?\\n *\/','',$script); \/\/ remove line breaks\n\t\t\t$script = preg_replace('\/\\t\/','',$script); \/\/ remove tabs\n\n\t\t}\n\t\techo $script.PHP_EOL;\n\t\t$et = microtime(true);\n\t\tWP_DEBUG && self::$timing and error_log(sprintf('%s%s::%s() Timing: %.5f sec.', '', __CLASS__, __FUNCTION__, $et-$st));\n\t\tself::$total_runtime += $et-$st;\n\t\tdo_action('qm\/stop', __METHOD__); \/\/ Query Monitor Profiling\n\t}\n\n\t\/\/-------------------------------------------------------------------------------------------------------------------\n\t\/**\n\t * Emits CSS for menu styling.\n\t *\/\n\tpublic function css(){\n\t\tif (!is_admin_bar_showing()) return;\n\t\tif (!is_user_logged_in()) return;\n\n\t\tdo_action('qm\/start', __METHOD__); \/\/ Query Monitor Profiling\n\t\t$st = microtime(true);\n\n\t\t\/\/ Get user's color scheme to pick some colors\n\t\t$color_scheme \t= get_user_option('admin_color', get_current_user_id());\n\t\t$color_hover\t= $GLOBALS['_wp_admin_css_colors'][$color_scheme]->colors[0]??'';\n\t\t$color_line\t\t= $GLOBALS['_wp_admin_css_colors'][$color_scheme]->colors[2]??'';\n\t\t$css_props_language = 'font-family:monospace;font-size:80%;vertical-align:super;';\n\t\t$css_props_status = 'font-family:monospace;font-style:italic;';\n\t\t\/* Notes: \n\t\tColor schemes define the colors for the backend. \n\t\tColor scheme for frontend adminbar is always standard (dark).\n\t\tWhile there are proper definitions for text, highlight, icons, there's no color definition for input fields.\n\t\tSo we always use dark background and light text for search input field\n\t\t*\/\n\n\t\t\n\t\t$icon_size = '16px';\n\t\t\n\t\t$slug = self::SLUG;\n\t\t$shrt = self::SHRT;\n\t\t$style = <<<STYLE_END\n\t\t\t<style id=\"{$slug}-css\">\n\n\t\t\t\/* highlight row on hover *\/\n\t\t\t#wpadminbar .{$shrt} ul li:hover {background-color:{$color_hover}; }\n\n\t\t\t\/* flexbox for icon+label *\/\n\t\t\t#wpadminbar .{$shrt} .{$shrt}-flexer {display:flex; flex-direction:row; align-items:center;}\n\t\t\t#wpadminbar .{$shrt} ul ul .{$shrt}-flexer {justify-content:space-between;}\n\t\t\t#wpadminbar .{$shrt} .ab-item {width:100%;}\n\t\t\t#wpadminbar .{$shrt} > .ab-item {width:fit-content;}\n\n\t\t\t\/* search *\/\n\t\t\t#wpadminbar .{$shrt}-post-list .{$shrt}-search .ab-item {padding:0 5px;}\n\t\t\t#wpadminbar .{$shrt}-post-list .{$shrt}-search input {background-color:hsl(0,0%,10%); color:hsl(0,0%,70%); \n\t\t\t\twidth:-webkit-fill-available; width:-moz-available; width:fill; line-height:1; min-height:unset; \n\t\t\t\tborder: none; outline:1px dotted hsl(0,0%,70%); padding:5px !important;}\n\n\t\t\t\/* add new *\/\n\t\t\t#wpadminbar .{$shrt}-post-list .{$shrt}-add_new {border-bottom:1px dotted {$color_line};}\n\n\n\t\t\t\/* post-list *\/\n\t\t\t#wpadminbar .{$shrt}-post-list .ab-sub-wrapper ul {max-height:80vh; overflow:hidden auto; padding-bottom:10px;}\n\t\t\t\/* happyfiles *\/\n\t\t\t#wpadminbar .{$shrt}-post-list .{$shrt}-happyfiles-folder .ab-item {font-weight:700;background-color:dimgray;color:white;}\n\n\t\t\t\/* language *\/\n\t\t\t#wpadminbar .{$shrt}-post-list .ab-sub-wrapper ul li span.language {{$css_props_language}}\n\t\t\t\/* status *\/\n\t\t\t#wpadminbar .{$shrt}-post-list .ab-sub-wrapper ul li span.status {{$css_props_status}}\n\n\t\t\t\/* action links *\/\n\t\t\t#wpadminbar .{$shrt}-post-list .{$shrt}-action-links {display:flex; flex-direction:row; margin:0 1em;}\n\t\t\t#wpadminbar .{$shrt}-post-list .{$shrt}-action-links a {width:{$icon_size}; height:{$icon_size}; line-height:normal; padding:0 5px; opacity:.5;}\n\t\t\t#wpadminbar .{$shrt}-post-list .{$shrt}-action-links a.{$shrt}-has-content {opacity:1;}\n\n\t\t\t\/* icons & svg *\/\n\t\t\t#wpadminbar .{$shrt} .dashicons {font:{$icon_size} dashicons; line-height:normal; color:inherit;}\n\t\t\t#wpadminbar .{$shrt} svg {width:{$icon_size}; height:{$icon_size}; margin-right:6px;}\n\t\t\t#wpadminbar li.{$shrt} span.dashicon-before {color:inherit;}\n\t\t\t#wpadminbar li.{$shrt} span.dashicon-before::before {width:{$icon_size}; height:{$icon_size};  color:inherit;}\n\n\t\t\t\/* divider *\/\n\t\t\t#wpadminbar :is(.{$shrt}-divider,#incspec) {margin-top: .5em; border-top:1px dotted {$color_line}; height:26px;}\n\t\t\t<\/style>\nSTYLE_END;\n\t\tif (self::MINIMIZE_CSS) {\n\t\t\t$style = preg_replace('\/\\\/\\*.*?\\*\\\/\/', '', $style); \/\/ remove comments\n\t\t\t$style = preg_replace('\/\\r?\\n *\/', '', $style); \/\/ remove line breaks\n\t\t\t$style = preg_replace('\/\\t\/', '', $style); \/\/ remove tabs\n\t\t}\n\t\techo $style.PHP_EOL;\n\n\t\t$et = microtime(true);\n\t\tif (WP_DEBUG && self::$timing) {error_log(sprintf('  %s%s::%s() Timing: %.5f sec.', '', __CLASS__, __FUNCTION__, $et-$st));}\n\t\tself::$total_runtime += $et-$st;\n\t\tdo_action('qm\/stop', __METHOD__); \/\/ Query Monitor Profiling\n\t}\n\t\/\/-------------------------------------------------------------------------------------------------------------------\n\t\/**\n\t * Emits SVG for menu icons.\n\t *\/\n\tpublic function svg(){\n\t\tif (!is_admin_bar_showing()) return;\n\t\tif (!is_user_logged_in()) return;\n\n\t\tdo_action('qm\/start', __METHOD__); \/\/ Query Monitor Profiling\n\t\t$st = microtime(true);\n\n\t\t$slug = self::SLUG;\n\t\t$shrt = self::SHRT;\n\n\t\t$builder_logo = '';\n\t\tif ($this->builder('Oxygen')) {\n\t\t\t$builder_logo = <<<LOGO_END\n\t\t\t\t<symbol id=\"logo-oxygen\" viewBox=\"0 0 381 385\">\n\t\t\t\t\t<path fill=\"currentColor\" \n\t\t\t\t\t\td=\"M297.508,349.748 C275.443,349.748 257.556,331.86 257.556,309.796 C257.556,287.731 275.443,269.844 \n\t\t\t\t\t\t297.508,269.844 C319.573,269.844 337.46,287.731 337.46,309.796 C337.46,331.86 319.573,349.748 \n\t\t\t\t\t\t297.508,349.748 L297.508,349.748 Z M222.304,309.796 C222.304,312.039 222.447,314.247 222.639,316.441 \n\t\t\t\t\t\tC212.33,319.092 201.528,320.505 190.403,320.505 C119.01,320.505 60.929,262.423 60.929,191.031 \n\t\t\t\t\t\tC60.929,119.638 119.01,61.557 190.403,61.557 C261.794,61.557 319.877,119.638 319.877,191.031 \n\t\t\t\t\t\tC319.877,206.833 317.02,221.978 311.815,235.99 C307.179,235.097 302.404,234.592 297.508,234.592 \n\t\t\t\t\t\tC255.974,234.592 222.304,268.262 222.304,309.796 L222.304,309.796 Z M380.805,191.031 C380.805,86.042 \n\t\t\t\t\t\t295.392,0.628 190.403,0.628 C85.414,0.628 0,86.042 0,191.031 C0,296.02 85.414,381.433 190.403,381.433 \n\t\t\t\t\t\tC212.498,381.433 233.708,377.609 253.456,370.657 C265.845,379.641 281.034,385 297.508,385 C339.042,385 \n\t\t\t\t\t\t372.712,351.33 372.712,309.796 C372.712,296.092 368.988,283.283 362.584,272.219 C374.251,247.575 \n\t\t\t\t\t\t380.805,220.058 380.805,191.031 L380.805,191.031 Z\"\/>\n\t\t\t\t<\/symbol>\nLOGO_END;\n\t\t}\n\t\tif ($this->builder('Bricks')) {\n\t\t\t$builder_logo = <<<LOGO_END\n\t\t\t\t<symbol id=\"logo-bricks\"  viewBox=\"0 0 35 45\">\n\t\t\t\t\t<path fill=\"currentColor\" transform=\"translate(-16.000000, -11.000000)\"\n\t\t\t\t\t\td=\"M25.1875,11.34375 L25.9375,11.8125 L25.9375,24.84375 C28.5833466,23.0937413 31.5104006,22.21875 \n\t\t\t\t\t\t34.71875,22.21875 C39.3437731,22.21875 43.1770681,23.8333172 46.21875,27.0625 C49.218765,30.2916828 \n\t\t\t\t\t\t50.71875,34.2708097 50.71875,39 C50.71875,43.7500237 49.2083484,47.7291506 46.1875,50.9375 \n\t\t\t\t\t\tC43.1458181,54.1666828 39.3229397,55.78125 34.71875,55.78125 C30.6978966,55.78125 27.2604309,54.3437644 \n\t\t\t\t\t\t24.40625,51.46875 L24.40625,55 L16.03125,55 L16.03125,12.375 L25.1875,11.34375 Z M33.125,30.6875 \n\t\t\t\t\t\tC30.9166556,30.6875 29.0729241,31.4374925 27.59375,32.9375 C26.1145759,34.4791744 25.375,36.4999875 \n\t\t\t\t\t\t25.375,39 C25.375,41.5000125 26.1145759,43.5104091 27.59375,45.03125 C29.0520906,46.5520909 \n\t\t\t\t\t\t30.8958222,47.3125 33.125,47.3125 C35.4791784,47.3125 37.3854094,46.5208413 38.84375,44.9375 \n\t\t\t\t\t\tC40.2812572,43.3749922 41,41.3958453 41,39 C41,36.6041547 40.2708406,34.6145913 38.8125,33.03125 \n\t\t\t\t\t\tC37.3541594,31.4687422 35.458345,30.6875 33.125,30.6875 Z\"\/>\n\t\t\t\t<\/symbol>\nLOGO_END;\n\t\t}\n\n\t\t\n\t\t$svg = <<<SVG_END\n\t\t\t<svg id=\"{$slug}-svg\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" xmlns:xlink=\"http:\/\/www.w3.org\/1999\/xlink\" \n\t\t\t\tstyle=\"position: absolute; width: 0; height: 0; overflow: hidden;\" version=\"1.1\">\n\t\t\t\t<defs>\n\t\t\t\t\t<symbol id=\"logo-{$slug}\" viewBox=\"0 0 24 24\">\n\t\t\t\t\t\t<path fill=\"currentColor\"\n\t\t\t\t\t\t\td=\"M14 13.9633H16V7.96331H10V9.96331H12.5858L7.25623 15.2929L8.67044 16.7071L14 11.3775L14 13.9633Z\"\/>\n\t\t\t\t\t\t<path fill=\"currentColor\" fill-rule=\"evenodd\" clip-rule=\"evenodd\"\n\t\t\t\t\t\t\td=\"M1 12C1 5.92487 5.92487 1 12 1C18.0751 1 23 5.92487 23 12C23 18.0751 18.0751 23 12 23C5.92487 23 \n\t\t\t\t\t\t\t1 18.0751 1 12ZM3 12C3 7.02944 7.02944 3 12 3C16.9706 3 21 7.02944 21 12C21 16.9706 16.9706 21 12 \n\t\t\t\t\t\t\t21C7.02944 21 3 16.9706 3 12Z\"\/>\n\t\t\t\t\t<\/symbol>\n\t\t\t\t\t<symbol id=\"icon-{$slug}-eye\" viewBox=\"0 0 24 24\">\n\t\t\t\t\t\t<path fill=\"currentColor\" fill-rule=\"evenodd\" clip-rule=\"evenodd\"\n\t\t\t\t\t\t\td=\"M16 12C16 14.2091 14.2091 16 12 16C9.79086 16 8 14.2091 8 12C8 9.79086 9.79086 8 12 8C14.2091 8 16 \n\t\t\t\t\t\t\t9.79086 16 12ZM14 12C14 13.1046 13.1046 14 12 14C10.8954 14 10 13.1046 10 12C10 10.8954 10.8954 10 12 \n\t\t\t\t\t\t\t10C13.1046 10 14 10.8954 14 12Z\"\/>\n\t\t\t\t\t\t<path fill=\"currentColor\" fill-rule=\"evenodd\" clip-rule=\"evenodd\"\n\t\t\t\t\t\t\td=\"M12 3C17.5915 3 22.2898 6.82432 23.6219 12C22.2898 17.1757 17.5915 21 12 21C6.40848 21 1.71018 17.1757 \n\t\t\t\t\t\t\t0.378052 12C1.71018 6.82432 6.40848 3 12 3ZM12 19C7.52443 19 3.73132 16.0581 2.45723 12C3.73132 7.94186 \n\t\t\t\t\t\t\t7.52443 5 12 5C16.4756 5 20.2687 7.94186 21.5428 12C20.2687 16.0581 16.4756 19 12 19Z\"\/>\n\t\t\t\t\t<\/symbol>\n\t\t\t\t\t<symbol id=\"icon-{$slug}-pattern\" viewBox=\"0 0 24 24\">\n\t\t\t\t\t\t<path fill=\"currentColor\" fill-rule=\"evenodd\" clip-rule=\"evenodd\"\n\t\t\t\t\t\t\td=\"M21.3 10.8l-5.6-5.6c-.7-.7-1.8-.7-2.5 0l-5.6 5.6c-.7.7-.7 1.8 0 2.5l5.6 5.6c.3.3.8.5 1.2.5s.9-.2 \n\t\t\t\t\t\t\t1.2-.5l5.6-5.6c.8-.7.8-1.9.1-2.5zm-1 1.4l-5.6 5.6c-.1.1-.3.1-.4 0l-5.6-5.6c-.1-.1-.1-.3 \n\t\t\t\t\t\t\t0-.4l5.6-5.6s.1-.1.2-.1.1 0 .2.1l5.6 5.6c.1.1.1.3 0 .4zm-16.6-.4L10 5.5l-1-1-6.3 6.3c-.7.7-.7 \n\t\t\t\t\t\t\t1.8 0 2.5L9 19.5l1.1-1.1-6.3-6.3c-.2 0-.2-.2-.1-.3z\"\/>\n\t\t\t\t\t<\/symbol>\n\t\t\t\t\t{$builder_logo}\n\t\t\t\t<\/defs>\n\t\t\t<\/svg>\nSVG_END;\n\t\tif (self::MINIMIZE_SVG) {\n\t\t\t$svg = preg_replace('\/>.*?<\/s', '><', $svg); \/\/ remove formatting content (line breaks, tabs) between tags\n\t\t\t$svg = preg_replace('\/\\r?\\n\\t+\/', ' ', $svg); \/\/ remove remaining line breaks and tabs\n\t\t}\n\t\techo $svg.PHP_EOL;\n\n\t\t\n\t\t$et = microtime(true);\n\t\tif (WP_DEBUG && self::$timing) {error_log(sprintf('  %s%s::%s() Timing: %.5f sec.', '', __CLASS__, __FUNCTION__, $et-$st));}\n\t\tself::$total_runtime += $et-$st;\n\t\tdo_action('qm\/stop', __METHOD__); \/\/ Query Monitor Profiling\n\t}\n\t\/\/-------------------------------------------------------------------------------------------------------------------\n\t\/** @since 1.6.3 *\/\n\tpublic function debug_info(){\n\t\techo sprintf('<span id=\"%s-info\" data-nosnippet style=\"display:none\">%s %s %s<\/span>', self::SLUG, $this->get_script_details()->type, esc_html(self::SLUG), esc_html(self::VERSION)); \n\t}\n\t\/\/-------------------------------------------------------------------------------------------------------------------\n\t\/**\n\t * Adds items to the admin bar.\n\t *\/\n\tpublic function admin_bar_items (WP_Admin_Bar $wp_admin_bar) {\n\t\tdo_action('qm\/start', __METHOD__.' (total)'); \/\/ Query Monitor Profiling\n\t\t$st = microtime(true);\n\n\t\t\/\/ basic permission check\n\t\tif (!current_user_can('edit_posts')) return;\n\n\t\t\/\/ get the registered post types\n\t\t$registered_post_types = get_post_types([],'OBJECT');\n\n\t\t\/\/ get post statuses\n\t\t$post_statuses = get_post_statuses();\n\n\n\t\t$parent_menu_slug = null;\n\t\tif ($this->settings['collapsed_menu']??false) {\n\t\t\t$parent_menu_slug = self::SLUG;\n\t\t\t\/\/ create admin bar main item\n\t\t\t$label = 'Quick Nav';\n\t\t\t$icon = sprintf('<svg><use xlink:href=\"#logo-%1$s\"><\/use><\/svg>', self::SLUG);\n\t\t\t$wp_admin_bar->add_menu([\n\t\t\t\t'id'\t=> $parent_menu_slug, \n\t\t\t\t'title'\t=> sprintf('<span class=\"%s-flexer\">%s<\/span>', self::SHRT, $icon.$label),\n\t\t\t\t'meta'\t=> [\n\t\t\t\t\t'class' => self::SHRT,\n\t\t\t\t\t\/\/\t'title' => self::TITLE, \/\/ don't show because it's distracting\n\t\t\t\t\t]\n\t\t\t\t]);\n\t\t\t\t\n\t\t}\n\t\t\/\/ create sub menus for post types\n\t\tforeach ($this->settings['post_types']??[] as $ptn => $pto) {\n\n\t\t\t\/\/ skip if post type not registered. Could happen if it was configured in Quick Nav settings, but has deactivated since.\n\t\t\tif (!($registered_post_types[$ptn]??null)) continue;\n\n\t\t\t$stpt = microtime(true);\n\t\t\tdo_action('qm\/start', __METHOD__.' (post type \"'.$ptn.'\")'); \/\/ Query Monitor Profiling\n\t\t\tif (WP_DEBUG && self::$timing>1) {error_log(sprintf('    %s%s::%s() Post Type \"%s\" Loop Start', '', __CLASS__, __FUNCTION__, $ptn));}\n\t\t\t\n\t\t\t\/\/ get post object to evaluate capabilities and labels\n\t\t\t$ptobj = get_post_type_object($ptn); \n\n\t\t\t\/\/ permission check, either specific for this post type, or generic for posts\n\t\t\tif (!current_user_can($ptobj->cap->edit_posts??false)) goto DONE_POSTTYPE;\n\n\t\t\t\/\/ check permission to access Oxygen templates\n\t\t\tif ($this->builder('Oxygen') && ($ptn=='ct_template') && function_exists('oxygen_vsb_current_user_can_access') && !call_user_func('oxygen_vsb_current_user_can_access')) {\n\t\t\t\tif (WP_DEBUG && self::$timing>1) {error_log(sprintf('    %s%s::%s() User has no permission to access \"%s\"', '', __CLASS__, __FUNCTION__, $ptn));}\n\t\t\t\tgoto DONE_POSTTYPE;\n\t\t\t}\n\t\t\t\/\/ check permission to access Bricks templates\n\t\t\tif ($this->builder('Bricks') && ($ptn=='bricks_template') && method_exists('\\Bricks\\Capabilities','current_user_can_use_builder') && !call_user_func('\\Bricks\\Capabilities::current_user_can_use_builder')) {\n\t\t\t\tif (WP_DEBUG && self::$timing>1) {error_log(sprintf('    %s%s::%s() User has no permission to access \"%s\"', '', __CLASS__, __FUNCTION__, $ptn));}\n\t\t\t\tgoto DONE_POSTTYPE;\n\t\t\t}\n\n\t\t\t\/\/ prepare label, title, icon\n\t\t\t$label = $registered_post_types[$ptn]->label;\n\t\t\t$title = $registered_post_types[$ptn]->labels->all_items ?? $label;\n\t\t\t\/\/ @since 1.8.0: get post type icon, assign one if undefined\n\t\t\t$menu_icon = $registered_post_types[$ptn]->menu_icon ??null;\n\t\t\tif (!$menu_icon) {\n\t\t\t\tswitch ($ptn) {\n\t\t\t\t\tcase 'wp_block':\t$menu_icon = sprintf('<svg><use xlink:href=\"#icon-%s-pattern\"><\/use><\/svg>',self::SLUG); break;\n\t\t\t\t\tdefault:\t\t\t$menu_icon = 'dashicons-marker';\n\t\t\t\t}\n\t\t\t} \n\t\t\t\/\/ custom menu icon as URL (e.g. ACF) - try to convert to img tag\n\t\t\tif (filter_var($menu_icon, FILTER_VALIDATE_URL)) {\n\t\t\t\t$menu_icon = sprintf('<img src=\"%s\" alt=\"%s\" style=\"width:16px;height:16px;margin-right:6px;vertical-align:middle;\">', esc_url($menu_icon), esc_attr($label));\n\t\t\t}\t\n\t\t\t$icon_label = $menu_icon.$label;\n\t\t\t\/\/ special cases for icon label\n\t\t\tif (preg_match('\/^dashicons\\-\/', $menu_icon)) {\n\t\t\t\t\/\/ menu_icon is dashicon, embed label in a dashicon-before span\n\t\t\t\t$icon_label = '<span class=\"ab-item dashicon-before '.$menu_icon.'\">'.$label.'<\/span>';\n\t\t\t}\n\t\t\tif ($this->builder('Oxygen') && $ptn == 'ct_template') { \n\t\t\t\t$icon = '<svg><use xlink:href=\"#logo-oxygen\"><\/use><\/svg>';\n\t\t\t\t$icon_label = $icon.$label;\n\t\t\t}\n\t\t\tif ($this->builder('Bricks') && $ptn == 'bricks_template') { \n\t\t\t\t$icon = '<svg><use xlink:href=\"#logo-bricks\"><\/use><\/svg>';\n\t\t\t\t$icon_label = $icon.$label;\n\t\t\t}\n\n\t\t\t\/\/ create menu item for post type\n\t\t\t$wp_admin_bar->add_menu([\n\t\t\t\t'id'\t=> sprintf('%s-%s', self::SLUG, $ptn), \n\t\t\t\t'parent'=> $parent_menu_slug,\n\t\t\t\t'title'\t=> sprintf('<span class=\"%s-flexer\">%s<\/span>', self::SHRT, $icon_label),\n\t\t\t\t'href'\t=> admin_url('edit.php?post_type='.$ptn),\n\t\t\t\t'meta'\t=> [\n\t\t\t\t\t'class' => self::SHRT.' '.self::SHRT.'-post-list',\n\t\t\t\t\t'title' => $title, \n\t\t\t\t\t]\n\t\t\t\t]);\n\n\t\t\tif ($this->settings['quick_search']??null) {\n\t\t\t\t\/\/ add search\n\t\t\t\t$search_html = sprintf('<input id=\"%1$s-%2$s-search\" type=\"text\" name=\"search\" placeholder=\"%3$s\">', self::SHRT, $ptn, __('Search'));\n\t\t\t\t$wp_admin_bar->add_node([\n\t\t\t\t\t'id'\t=> sprintf('%s-%s-%s', self::SLUG, $ptn, 'search'), \n\t\t\t\t\t'parent'=> sprintf('%s-%s', self::SLUG, $ptn),\n\t\t\t\t\t'group'\t=> null,\n\t\t\t\t\t'title'\t=> $search_html,\n\t\t\t\t\t'meta'\t=> [\n\t\t\t\t\t\t\/\/'title' => __('Search'), \/\/ distracting\n\t\t\t\t\t\t'class' => sprintf('%1$s-flexer %1$s-search', self::SHRT),\n\t\t\t\t\t\t]\n\t\t\t\t\t]);\n\t\t\t}\n\n\t\t\tif ($this->settings['add_new']??null) {\n\t\t\t\t\/\/ Add new...\n\t\t\t\tif (current_user_can($ptobj->cap->create_posts??false)) {\n\t\t\t\t\tif ($add_new_url = admin_url('post-new.php?post_type='.$ptn)) {\n\t\t\t\t\t\t$wp_admin_bar->add_node([\n\t\t\t\t\t\t\t'id'\t=> sprintf('%s-%s-%s', self::SLUG, $ptn, 'add_new'), \n\t\t\t\t\t\t\t'parent'=> sprintf('%s-%s', self::SLUG, $ptn),\n\t\t\t\t\t\t\t'group'\t=> null,\n\t\t\t\t\t\t\t'title'\t=> esc_html($ptobj->labels->add_new_item??'Add new ...').' ...',\n\t\t\t\t\t\t\t'href'\t=> $add_new_url,\n\t\t\t\t\t\t\t'meta'\t=> [\n\t\t\t\t\t\t\t\t'class' => sprintf('%1$s-flexer %1$s-add_new', self::SHRT),\n\t\t\t\t\t\t\t\t]\n\t\t\t\t\t\t\t]);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\n\t\t\t\/\/ get config for post type list\n\t\t\t$pto = array_merge([ \n\t\t\t\t'orderby'\t\t=> self::DEFAULT_ORDERBY,\n\t\t\t\t'order'\t\t\t=> self::DEFAULT_ORDER,\n\t\t\t\t'limit'\t\t\t=> self::DEFAULT_LIMIT,\n\t\t\t\t'hierarchical'\t=> false,\n\t\t\t\t'happyfiles'\t=> false,\n\t\t\t],$this->settings['post_types'][$ptn] ?? []);\n\n\t\t\t$args = [\n\t\t\t\t'post_type'\t\t\t=> $ptn,\n\t\t\t\t'post_status'\t\t=> $this->settings['status'],\n\t\t\t];\n\t\t\tif (defined('POLYLANG_VERSION')) {\n\t\t\t\t$args['lang'] = ''; \/\/ Deactivate Polylang language filter, @see https:\/\/polylang.pro\/doc\/developpers-how-to\/\t\n\t\t\t}\n\t\t\tif ($pto['hierarchical']) {\n\t\t\t\t\/\/ get_pages() returns hierachical results\n\t\t\t\t$args['sort_column']\t= $pto['orderby'];\n\t\t\t\t$args['sort_order']\t\t= $pto['order'];\n\t\t\t\t$args['number']\t\t\t= $pto['limit'] === '' ? -1 : $pto['limit'];\n\t\t\t\t$posts = get_pages($args);\n\t\t\t} else {\n\t\t\t\t\/\/ get_posts() doesn't provide hierachical results\n\t\t\t\t$args['orderby']\t\t= $pto['orderby'];\n\t\t\t\t$args['order']\t\t\t= $pto['order'];\n\t\t\t\t$args['posts_per_page']\t= $pto['limit'] === '' ? -1 : $pto['limit'];\n\t\t\t\t$posts = get_posts($args);\n\t\t\t}\n\t\t\tif ($pto['happyfiles'] && in_array($ptn,$this->get_happyfiles_post_types())) {\n\t\t\t\t$happyfiles_terms = []; \/\/ to collect used happyfiles folders\n\t\t\t\tdo_action('qm\/start', __METHOD__.' HappyFiles (post type \"'.$ptn.'\")'); \/\/ Query Monitor Profiling\n\t\t\t\t\/\/ loop posts and evaluate HappyFiles terms\n\t\t\t\tforeach ($posts as $post) {\n\t\t\t\t\tif ($post->happyfiles_term??null) continue; \/\/ we already handled this post by an append\n\t\t\t\t\t\/\/ get assigned HappyFiles terms\n\t\t\t\t\t$happyfiles_terms_post = get_the_terms($post->ID, 'hf_cat_'.$ptn);\n\t\t\t\t\tif (($happyfiles_terms_post !== false) && (!is_wp_error($happyfiles_terms_post)) && count($happyfiles_terms_post)) {\n\t\t\t\t\t\t\/\/ get assigned HappyFiles term names\n\t\t\t\t\t\tforeach($happyfiles_terms_post as $idx => $happyfiles_term_post) {\n\t\t\t\t\t\t\t\/\/ multiple terms assigned? duplicate post\n\t\t\t\t\t\t\tif ($idx>0) {\n\t\t\t\t\t\t\t\t$new_post = clone $post;\n\t\t\t\t\t\t\t\t$new_post->happyfiles_term = $happyfiles_term_post; \/\/->name;\n\t\t\t\t\t\t\t\t$posts[] = $new_post;\n\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\/\/ assign HappyFiles term names to post\n\t\t\t\t\t\t\t\t$post->happyfiles_term = $happyfiles_term_post; \/\/->name;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$post->happyfiles_term = (object)['term_id'=>0, 'name'=>' Uncategorized','parent'=>0]; \/\/ fake term\n\t\t\t\t\t}\n\t\t\t\t\tif (!in_array($post->happyfiles_term, $happyfiles_terms)) $happyfiles_terms[] = $post->happyfiles_term;\n\t\t\t\t}\n\t\t\t\t\/\/ --- care about HappyFiles Folder hierarchy\n\t\t\t\t\/\/ create simple happyfiles folder list for quick reference\n\t\t\t\t$happyfiles_terms_registry=[];\n\t\t\t\tforeach ($happyfiles_terms as $happyfiles_term) {\n\t\t\t\t\t$happyfiles_terms_registry[$happyfiles_term->term_id] = $happyfiles_term;\n\t\t\t\t}\n\t\t\t\t\/\/ store HappyFiles Folder path to posts\n\t\t\t\tforeach ($posts as $post) {\n\t\t\t\t\t$happyfiles_term = $post->happyfiles_term;\n\t\t\t\t\t$happyfiles_term_path = $happyfiles_term->name;\n\t\t\t\t\twhile (($happyfiles_term->parent??0)!=0) {\n\t\t\t\t\t\tif ($happyfiles_term = $happyfiles_terms_registry[$happyfiles_term->parent]??null){\n\t\t\t\t\t\t\t$happyfiles_term_path = $happyfiles_term->name.' \u25b8 '.$happyfiles_term_path;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t\/\/ replace space by &shy; (except leading space from ' Uncategorized') to assure correct sorting\n\t\t\t\t\t$post->happyfiles_term_path = preg_replace('\/^([^ ]+) \/','\\1'.\"\\u{00A0}\",$happyfiles_term_path); \n\t\t\t\t}\n\n\t\t\t\t\/\/ --- order posts by HappyFiles Folder, plus the defined sort field and order\n\t\t\t\t$orderby_key = $pto['orderby'];\n\t\t\t\t$order_key = $pto['order'];\n\t\t\t\tusort($posts,function($a,$b) use ($orderby_key, $order_key) {\n\t\t\t\t\tif ($order_key == 'ASC') {\n\t\t\t\t\t\treturn strcmp($a->happyfiles_term_path.' '.$a->{$orderby_key},$b->happyfiles_term_path.' '.$b->{$orderby_key});\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn strcmp($a->happyfiles_term_path.' '.$b->{$orderby_key},$b->happyfiles_term_path.' '.$a->{$orderby_key});\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tdo_action('qm\/stop', __METHOD__.' HappyFiles (post type \"'.$ptn.'\")'); \/\/ Query Monitor Profiling\n\t\t\t}\n\t\t\t\n\t\t\t$hierarchy_parents = [];\n\t\t\t$happyfiles_previous_folder = null;\n\t\t\t\/\/ loop posts\n\t\t\tforeach ($posts as $post) {\n\n\t\t\t\t\/\/ permission check\n\t\t\t\tif (!current_user_can($ptobj->cap->edit_post??false, $post->ID)) continue;\n\n\t\t\t\t\/\/ hierarchy view\t\t\t\t\n\t\t\t\tif ($pto['hierarchical']) {\n\t\t\t\t\t\/\/ indent post titles as necessary\n\t\t\t\t\tif ($post->post_parent??0) { \n\t\t\t\t\t\t$parent_level = array_search($post->post_parent, $hierarchy_parents);\n\t\t\t\t\t\tif ($parent_level !== false) {\n\t\t\t\t\t\t\t$post->post_title = str_replace(' ','&ndash;',str_pad('',$parent_level+1,' ')).' '.$post->post_title;\n\t\t\t\t\t\t\tif ($parent_level < count($hierarchy_parents)-1) {\n\t\t\t\t\t\t\t\t$hierarchy_parents = array_slice($hierarchy_parents, 0, $parent_level+1);\n\t\t\t\t\t\t\t} \n\t\t\t\t\t\t\t$hierarchy_parents[] = $post->ID;\n\t\t\t\t\t\t}\n\t\t\t\t\t} else {\n\t\t\t\t\t\t$hierarchy_parents = [$post->ID];\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t$actions = array_merge([], $this->actions_wordpress($post));\n\t\t\t\tif ($pto['builder']??false) {\n\t\t\t\t\tif ($this->builder('Oxygen')) {$actions = array_merge($actions, $this->actions_oxygen($post));}\n\t\t\t\t\tif ($this->builder('Bricks')) {$actions = array_merge($actions, $this->actions_bricks($post));}\n\t\t\t\t}\n\t\t\t\t\/\/ provide view action, except for templates\n\t\t\t\tif (!in_array($ptn, ['ct_template','bricks_template'])) {\n\t\t\t\t\t$actions = array_merge($actions, $this->actions_frontend($post));\n\t\t\t\t} \n\n\t\t\t\t\/\/ create builder links\n\t\t\t\t$action_links = '';\n\t\t\t\tforeach ($actions as $link) {\n\t\t\t\t\t$action_links .= sprintf('<a href=\"%s\" title=\"%s\" class=\"%s\">%s<\/a>', \n\t\t\t\t\t\t\t\t\t\t$link->href??'',esc_attr($link->title??''), esc_attr($link->has_content?self::SHRT.'-has-content':''), $link->icon??'');\n\t\t\t\t}\n\t\t\t\tif ($action_links) {\n\t\t\t\t\t$action_links = sprintf('<span class=\"%s-action-links\">%s<\/span>', self::SHRT, $action_links);\n\t\t\t\t}\n\n\t\t\t\t$title_appendix = '';\n\t\t\t\tif (defined('POLYLANG_VERSION') && ($this->settings['show_language']??false)) {\n\t\t\t\t\t\/\/ language\n\t\t\t\t\t$pll_function_name = 'pll_get_post_language';\n\t\t\t\t\tif (function_exists($pll_function_name)) {\n\t\t\t\t\t\t$pll_post_language = call_user_func($pll_function_name, $post->ID);\n\t\t\t\t\t\t$title_appendix .= ' <span class=\"language\">'.$pll_post_language.'<\/span>';\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\/\/ status\n\t\t\t\t$title_appendix .= ($post->post_status == 'publish') ? '' : ' <span class=\"status\">['.$post_statuses[$post->post_status].']<\/span>';\n\t\t\t\tif ($pto['happyfiles'] && ($happyfiles_previous_folder != $post->happyfiles_term)) {\n\t\t\t\t\t\/\/ add HappyFiles Folder\n\t\t\t\t\t$wp_admin_bar->add_node([\n\t\t\t\t\t\t'id'\t=> sprintf('%s-%s-hf-%d', self::SLUG, $ptn, $post->happyfiles_term->term_id??0), \n\t\t\t\t\t\t'parent'=> sprintf('%s-%s', self::SLUG, $ptn),\n\t\t\t\t\t\t'group'\t=> null,\n\t\t\t\t\t\t'title'\t=> '\u25b8 '.$post->happyfiles_term_path, \n\t\t\t\t\t\t'meta'\t=> [\n\t\t\t\t\t\t\t'title' => 'HappyFiles '.esc_html__( 'Folders', 'happyfiles' ), \n\t\t\t\t\t\t\t'class' => sprintf('%1$s-flexer %1$s-happyfiles-folder', self::SHRT),\n\t\t\t\t\t\t\t]\n\t\t\t\t\t\t]);\n\t\t\t\t\t$happyfiles_previous_folder = $post->happyfiles_term;\n\t\t\t\t}\n\t\t\t\t\/\/ create menu item for post. append happyfiles term to menu ID to avoid duplicate menu entries\n\t\t\t\t$wp_admin_bar->add_menu([ \n\t\t\t\t\t'id'\t=> sprintf('%s-%s-%d-%d', self::SLUG, $ptn, $post->ID, $post->happyfiles_term->term_id??0), \n\t\t\t\t\t'parent'=> sprintf('%s-%s', self::SLUG, $ptn),\n\t\t\t\t\t'group'\t=> null,\n\t\t\t\t\t'title'\t=> $post->post_title . $title_appendix, \n\t\t\t\t\t'href'\t=> admin_url(sprintf('post.php?post=%d&action=edit', $post->ID)),  \n\t\t\t\t\t'meta'\t=> [\n\t\t\t\t\t\t'title' => __('Edit').' in WordPress', \n\t\t\t\t\t\t'html'\t=> $action_links,\n\t\t\t\t\t\t'class' => sprintf('%1$s-flexer %1$s-post', self::SHRT),\n\t\t\t\t\t\t]\n\t\t\t\t\t]);\n\t\t\t}\n\t\t\t$post_counts = wp_count_posts($ptn);\n\t\t\t$post_count = 0;\n\t\t\tforeach ($this->settings['status'] as $status) {\n\t\t\t\t$post_count += (int)$post_counts->{$status};\n\t\t\t}\n\t\t\tif ($pto['limit'] && $post_count > $pto['limit']) {\n\t\t\t\t\/\/ add a hint about limits\n\t\t\t\t$link = false;\n\t\t\t\t$hint = __('Number of entries exceeds limit.');\n\t\t\t\tif (current_user_can('manage_options')) {\n\t\t\t\t\t$link = admin_url('options-general.php?page='.self::SLUG);\n\t\t\t\t\t$hint .= ' '.__('Click to change the limit');\n\t\t\t\t}\n\t\t\t\t$wp_admin_bar->add_node([\n\t\t\t\t\t'id'\t=> sprintf('%s-%s-%s', self::SLUG, $ptn, 'limit-hint'), \n\t\t\t\t\t'parent'=> sprintf('%s-%s', self::SLUG, $ptn),\n\t\t\t\t\t'group'\t=> null,\n\t\t\t\t\t'title'\t=> 'Limit: '.$pto['limit'], \n\t\t\t\t\t'href'\t=> $link,  \n\t\t\t\t\t'meta'\t=> [\n\t\t\t\t\t\t'title' => $hint,\n\t\t\t\t\t\t'class' => sprintf('%1$s-divider %1$s-limit', self::SHRT),\n\t\t\t\t\t\t]\n\t\t\t\t\t]);\n\t\t\t}\n\t\t\tDONE_POSTTYPE:\n\t\t\t$etpt = microtime(true);\n\t\t\tif (WP_DEBUG && self::$timing) {error_log(sprintf('    %s%s::%s() Post Type \"%s\" Timing: %.5f sec.', '', __CLASS__, __FUNCTION__, $ptn, $etpt-$stpt));}\n\t\t\tdo_action('qm\/stop', __METHOD__.' (post type \"'.$ptn.'\")'); \/\/ Query Monitor Profiling\n\t\t}\n\n\t\t\/\/ @since 1.8.0: create menu item \"Custom Links\" with sub items for every custom link\n\t\tif ($this->settings['custom-links']??[]) {\n\t\t\t$custom_links_menu_slug = sprintf('%s-%s', self::SLUG, 'custom-links');\n\t\t\t$wp_admin_bar->add_menu([\n\t\t\t\t'id'\t=> $custom_links_menu_slug,\n\t\t\t\t'parent'=> $parent_menu_slug, \/\/self::SLUG,\n\t\t\t\t'title'\t=> sprintf('<span class=\"%s-flexer\">%s<\/span>', self::SHRT, __('Custom Links')),\n\t\t\t\t'meta'\t=> [\n\t\t\t\t\t\/\/ add divider in collapsed menu\n\t\t\t\t\t'class' =>\tself::SHRT.' '.(($this->settings['collapsed_menu']??false) ? sprintf('%1$s-divider', self::SHRT) : ''),\n\t\t\t\t\t]\n\t\t\t\t]);\n\t\t\t\/\/ add custom links\n\t\t\tforeach ($this->settings['custom-links']??[] as $cln => $clo) {\n\t\t\t\t\/\/ create menu item for custom link\n\t\t\t\t$wp_admin_bar->add_menu([\n\t\t\t\t\t'id'\t=> sprintf('%s-%s', self::SLUG, 'custom-'.$cln), \n\t\t\t\t\t'parent'=> $custom_links_menu_slug,\n\t\t\t\t\t'title'\t=> sprintf('<span class=\"%s-flexer\">%s<\/span>', self::SHRT, $clo['title']??''),\n\t\t\t\t\t'href'\t=> esc_url($clo['url']??'#'),\n\t\t\t\t\t'meta'\t=> [\n\t\t\t\t\t\t'class' => self::SHRT.' '.self::SHRT.'-custom-link',\n\t\t\t\t\t\t'title' => $clo['label']??'', \n\t\t\t\t\t\t]\n\t\t\t\t\t]);\n\t\t\t}\n\t\t}\n\t\t\n\n\t\t\/\/ create menu item for settings\n\t\tif (current_user_can('manage_options')) {\n\t\t\t$wp_admin_bar->add_menu([\n\t\t\t\t'id'\t=> sprintf('%s-%s', self::SLUG, 'settings'), \n\t\t\t\t'parent'=> self::SLUG,\n\t\t\t\t'title'\t=> sprintf('<span class=\"%s-flexer\">%s<\/span>', self::SHRT, __('Settings')),\n\t\t\t\t'href'\t=> admin_url('options-general.php?page='.self::SLUG),\n\t\t\t\t'meta'\t=> [\n\t\t\t\t\t'class' => sprintf('%1$s-divider', self::SHRT),\n\t\t\t\t\t'title' => __('Settings'), \n\t\t\t\t\t]\n\t\t\t\t]);\n\t\t\t}\n\t\t$et = microtime(true);\n\t\tif (WP_DEBUG && self::$timing) {error_log(sprintf('  %s%s::%s() Timing: %.5f sec.', '', __CLASS__, __FUNCTION__, $et-$st));}\n\t\tself::$total_runtime += $et-$st;\n\t\tdo_action('qm\/stop', __METHOD__.' (total)'); \/\/ Query Monitor Profiling\n\t}\n\t\/\/-------------------------------------------------------------------------------------------------------------------\n\t\/**\n\t * Returns action links for WordPress.\n\t * @param WP_Post $post\t\tThe WP_Post to generate action links for. \n\t * @return array\t\t\tAn array of objects containing properties `href`, `icon`, `title`, `has_content`. \n\t *\/\n\tprivate function actions_wordpress(WP_Post $post):array {\n\t\t$st = microtime(true);\n\t\t$retval = [];\n\n\t\t$link = admin_url(sprintf('post.php?post=%d&action=edit', $post->ID));\n\t\t$icon = '<span class=\"dashicons dashicons-wordpress\" ><\/span>';\n\t\t$retval[] = (object)['href'=>esc_url($link), 'icon'=>$icon, 'title'=>__('Edit').' in WordPress', 'has_content' => $post->post_content?1:0]; \n\n\t\tDONE:\n\t\t$et = microtime(true);\n\t\tif (WP_DEBUG && self::$timing>1) {error_log(sprintf('      %s%s::%s(%s) Timing: %.5f sec.', '', __CLASS__, __FUNCTION__, ($post->post_type??'').' '.($post->ID??''), $et-$st));}\n\t\treturn $retval;\n\t}\n\t\/\/-------------------------------------------------------------------------------------------------------------------\n\t\/**\n\t * Returns action link for view in frontend.\n\t * @param WP_Post $post\t\tThe WP_Post to generate action links for. \n\t * @return array\t\t\tAn array of objects containing properties `href`, `icon`, `title`, `has_content`. \n\t *\/\n\tprivate function actions_frontend(WP_Post $post):array {\n\t\t$st = microtime(true);\n\t\t$retval = [];\n\n\t\t\/\/ @since 1.8.0: check if post type is public\n\t\tif ((get_post_type_object($post->post_type)->public??false) !== true) goto DONE;\n\n\t\t$link = get_permalink($post->ID);\n\t\t$icon = sprintf('<svg><use xlink:href=\"#icon-%s-eye\"><\/use><\/svg>',self::SLUG);\n\t\t$retval[] = (object)['href'=>esc_url($link), 'icon'=>$icon, 'title'=>__('View'), 'has_content' => 1]; \n\n\t\tDONE:\n\t\t$et = microtime(true);\n\t\tif (WP_DEBUG && self::$timing>1) {error_log(sprintf('      %s%s::%s(%s) Timing: %.5f sec.', '', __CLASS__, __FUNCTION__, ($post->post_type??'').' '.($post->ID??''), $et-$st));}\n\t\treturn $retval;\n\t}\n\t\/\/-------------------------------------------------------------------------------------------------------------------\n\t\/**\n\t * Returns action links for Oxygen.\n\t * @param WP_Post $post\t\tThe WP_Post to generate action links for. \n\t * @return array\t\t\tAn array of objects containing properties `href`, `icon`, `title`, `has_content`. \n\t *\/\n\tprivate function actions_oxygen(WP_Post $post):array {\n\t\t$st = microtime(true);\n\t\t$retval = [];\n\t\tif (!$this->builder('Oxygen')) goto DONE;\n\t\t\/\/ Oxygen hidden for this post type?\n\t\tif (get_option('oxygen_vsb_ignore_post_type_'.$post->post_type) == 'true') goto DONE;\n\t\t\/\/ permission check\n\t\tif (!function_exists('oxygen_vsb_current_user_can_access') || !call_user_func('oxygen_vsb_current_user_can_access')) goto DONE;\n\n\t\t\/\/ Oxygen pre or post 4.8.3?\n\t\t$meta_function_name = function_exists('oxy_get_post_meta') ? 'oxy_get_post_meta' : 'get_post_meta';\n\n\t\t\/\/ Edit a template or a post?\n\t\tif ($post->post_type == 'ct_template') {\n\t\t\t$parent_template_id = call_user_func($meta_function_name, $post->ID, 'ct_parent_template', true);\n\t\t\t$inner_content = false;\n\t\t\tif ($parent_template_id) {\n\t\t\t\tif ($json = call_user_func($meta_function_name, $parent_template_id, 'ct_builder_json', true)) {\n\t\t\t\t\t$inner_content = (strpos($json, '\"name\":\"ct_inner_content\"') !== false);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tif ($shortcodes = call_user_func($meta_function_name, $parent_template_id, 'ct_builder_shortcodes', true)) {\n\t\t\t\t\t\t$inner_content = (strpos($shortcodes, '[ct_inner_content') !== false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t$edit_url = call_user_func('ct_get_post_builder_link', $post->ID) . ($inner_content ? '&ct_inner=true' : '');\n\t\t\t$icon = '<svg><use xlink:href=\"#logo-oxygen\"><\/use><\/svg>';\n\t\t\t$json = call_user_func($meta_function_name, $post->ID, 'ct_builder_json', true);\n\n\t\t\t$retval[] = (object)['href'=>esc_url($edit_url), 'icon'=>$icon, 'title'=>sprintf(__('Edit').' in %s Builder',$this->builder()), 'has_content'=>!empty($json)]; \n\t\t} else {\n\n\t\t\t\/\/ -1: None, undefined or 0: Automatic, > 0: Manually assigned\n\t\t\t$template_id = (int)call_user_func($meta_function_name, $post->ID, 'ct_other_template', true);\n\t\t\t\n\t\t\tif ($template_id == -1) { \/\/ None\n\t\t\t\t\/\/ nothing to do\n\t\t\t} elseif ($template_id > 0) { \/\/ manually assigned \n\t\t\t\t\/\/ get specified template post\n\t\t\t\t$template_post = get_post($template_id);\n\t\t\t\t$template_id = $template_post->ID ?? -1; \/\/ if template not found, assume None\n\t\t\t} else {\n\t\t\t\t\/\/ get template for single\n\t\t\t\t$template_post = call_user_func('ct_get_posts_template', $post->ID);\n\t\t\t\t$template_id = $template_post->ID ?? -1; \/\/ if template not found, assume None\n\t\t\t}\n\t\t\t\/\/ if we have a template, check for inner content element\n\t\t\t$inner_content = false;\n\t\t\tif ($template_id && $template_id != -1) {\n\t\t\t\tif ($json = call_user_func($meta_function_name, $template_id, 'ct_builder_json', true)) {\n\t\t\t\t\t$inner_content = (strpos($json, '\"name\":\"ct_inner_content\"') !== false);\n\t\t\t\t}\n\t\t\t\telse { \/\/ only read shortcodes if we didn't find json; might be an old unmigrated post\n\t\t\t\t\tif ($shortcodes = call_user_func($meta_function_name, $template_id, 'ct_builder_shortcodes', true)) {\n\t\t\t\t\t\t$inner_content = (strpos($shortcodes, '[ct_inner_content') !== false);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t\/\/ Check if post has Oxygen content\n\t\t\t$json = call_user_func($meta_function_name, $post->ID, 'ct_builder_json', true);\n\t\t\t\/\/ only read shortcodes if we didn't find json; might be an old unmigrated post\n\t\t\t$shortcodes = ''; \n\t\t\t\/\/$shortcodes = $json ? true : call_user_func($meta_function_name, $post->ID, 'ct_builder_shortcodes', true);\n\t\t\t$icon = '<svg><use xlink:href=\"#logo-oxygen\"><\/use><\/svg>';\n\t\t\t$link = call_user_func('ct_get_post_builder_link', $post->ID) . ($inner_content ? '&ct_inner=true' : '');\n\t\t\t$retval[] = (object)['href'=>esc_url($link), 'icon'=>$icon, 'title'=>sprintf(__('Edit').' in %s Builder',$this->builder()), 'has_content'=>($json||$shortcodes)]; \n\t\t}\n\t\tDONE:\n\t\t$et = microtime(true);\n\t\tif (WP_DEBUG && self::$timing>1) {error_log(sprintf('      %s%s::%s(%s) Timing: %.5f sec.', '', __CLASS__, __FUNCTION__, ($post->post_type??'').' '.($post->ID??''), $et-$st));}\n\t\treturn $retval;\n\t}\n\t\/\/-------------------------------------------------------------------------------------------------------------------\n\t\/**\n\t * Returns action links for Bricks.\n\t * @param WP_Post $post\t\tThe WP_Post to generate action links for. \n\t * @return array\t\t\tAn array of objects containing properties `href`, `icon`, `title`, `has_content`. \n\t *\/\n\tprivate function actions_bricks(WP_Post $post):array {\n\t\t$st = microtime(true);\n\t\t$retval = [];\n\t\tif (!$this->builder('Bricks')) goto DONE;\n\t\t\/\/ permission check\n\t\tif (!method_exists('\\Bricks\\Capabilities','current_user_can_use_builder') || !call_user_func('\\Bricks\\Capabilities::current_user_can_use_builder')) goto DONE;\n\t\t\/\/ create action link\n\t\t$icon = '<svg><use xlink:href=\"#logo-bricks\"><\/use><\/svg>';\n\t\t$link = call_user_func('\\Bricks\\Helpers::get_builder_edit_link',$post->ID);\n\t\t$template_type = call_user_func('\\Bricks\\Templates::get_template_type',$post->ID);\n\t\t$template_data = call_user_func('\\Bricks\\Database::get_data',$post->ID, $template_type);\n\t\t$retval[] = (object)['href'=>esc_url($link), 'icon'=>$icon, 'title'=>sprintf(__('Edit').' in %s Builder',$this->builder()), 'has_content'=>!empty($template_data)]; \n\t\tDONE:\n\t\t$et = microtime(true);\n\t\tif (WP_DEBUG && self::$timing>1) {error_log(sprintf('      %s%s::%s(%s) Timing: %.5f sec.', '', __CLASS__, __FUNCTION__, ($post->post_type??'').' '.($post->ID??''), $et-$st));}\n\t\treturn $retval;\n\t}\n\t\/\/-------------------------------------------------------------------------------------------------------------------\n\t\/**\n\t * Checks if builder is enabled for specified post type.\n\t * @since 1.8.0\n\t * @param string $post_type\t\tThe post type to check.\n\t * @return bool\t\t\t\t\tTrue if builder is enabled for specified post type, false otherwise.\n\t *\/\n\tprivate function builder_post_type(string $post_type):bool {\n\t\t$st = microtime(true);\n\t\t$retval = false;\n\t\tif ($this->builder('Oxygen')) {\n\t\t\t\/\/ Oxygen hidden for this post type?\n\t\t\tif (get_option('oxygen_vsb_ignore_post_type_'.$post_type) != 'true') {\n\t\t\t\t$retval = true;\n\t\t\t}\n\t\t\t\/\/ @since 1.8.0: Pattern (wp_block) is not supported by Oxygen, so always ignore it\n\t\t\tif ($post_type == 'wp_block') {\n\t\t\t\t$retval = false;\n\t\t\t}\n\n\t\t}\n\t\tif ($this->builder('Bricks')) {\n\t\t\t\/\/ Bricks hidden for this post type?\n\t\t\t$bricks_ignored_post_types = get_option('bricks_ignored_post_types', []);\n\t\t\t\/\/ @since 1.8.0: Pattern (wp_block) is not supported by Bricks, so always ignore it\n\t\t\t$bricks_ignored_post_types[] = 'wp_block';\n\t\t\tif (!in_array($post_type, $bricks_ignored_post_types)) {\n\t\t\t\t$retval = true;\n\t\t\t}\n\t\t}\n\t\t$et = microtime(true);\n\t\tif (WP_DEBUG && self::$timing>1) {error_log(sprintf('      %s%s::%s(%s) Timing: %.5f sec.', '', __CLASS__, __FUNCTION__, $post_type, $et-$st));}\n\t\treturn $retval;\n\t}\n\t\/\/===================================================================================================================\n\t\/\/ UTILITIES\n\t\/\/-------------------------------------------------------------------------------------------------------------------\n\t\/\/-------------------------------------------------------------------------------------------------------------------\n\t\/**\n\t * Returns builder name or boolean if builder matches specified name `Bricks` or `Oxygen`. \n\t * @param ?string $builder\t[Optional] Checks if builder matches.\n\t * @return mixed\t\t\tBuilder name, or true\/false for builder name match \n\t *\/\n\tprivate function builder(?string $builder = null) {\n\t\t$retval = '';\n\t\tif (defined('BRICKS_VERSION')) \t$retval = 'Bricks';\n\t\tif (defined('CT_VERSION')) \t\t$retval = 'Oxygen';\n\t\tif (isset($builder)) $retval = strtolower($retval) == strtolower($builder);\n\t\treturn $retval;\n\t}\n\t\/\/-------------------------------------------------------------------------------------------------------------------\n\t\/**\n\t * Returns HappyFiles configured post types, or empty array if not configured\n\t *\/\n\tprivate function get_happyfiles_post_types() {\n\t\t$retval = get_option('happyfiles_post_types',[]); \/\/ doesn't return empty array but string if none configured\n\t\tif (!is_array($retval)) {$retval = [];}\n\t\treturn $retval;\n\t}\n\t\/\/-------------------------------------------------------------------------------------------------------------------\n\t\/**\n\t * Returns the script details as type, version, combined.\n\t * @return object\tThe script  type and version:\n\t * - `type`: The type as 'Plugin' or 'Code Snippet'.\n\t * - `version`: The version.\n\t * - `combined`: The combination of type and version.\n\t * - `full`: The combination of type, title and version\n\t *\/\n\tpublic function get_script_details() {\n\t\t$type = basename(__FILE__) == 'ma-admin-quick-nav.php' ? 'Plugin' : 'Code Snippet';\n\t\t$retval =(object)[\n\t\t\t'type'\t\t=> $type,\n\t\t\t'version'\t=> self::VERSION,\n\t\t\t'combined'\t=> sprintf('%s %s', $type, self::VERSION),\n\t\t\t'full'\t\t=> sprintf('%s \"%s\" %s', $type, self::TITLE, self::VERSION),\n\t\t];\n\t\treturn $retval;;\n\t}\n}\n\n\/\/===================================================================================================================\n\/\/ Initialize\nnew MA_Admin_Quick_Nav();\n\nendif;","modified":"2026-04-12 09:55:12","revision":"1"}]}