diff --git a/MarkupMenuBuilder.module b/MarkupMenuBuilder.module index 53ceb41..b07c147 100644 --- a/MarkupMenuBuilder.module +++ b/MarkupMenuBuilder.module @@ -33,7 +33,7 @@ class MarkupMenuBuilder extends WireData implements Module { 'title' => 'Menu Builder: Markup', 'summary' => 'Render menus created by Process Menu Builder', 'author' => 'Francis Otieno (Kongondo)', - 'version' => '0.2.7', + 'version' => '0.2.82', 'href' => 'http://processwire.com/talk/topic/4451-module-menu-builder/', 'singular' => true, 'autoload' => false, @@ -77,6 +77,32 @@ class MarkupMenuBuilder extends WireData implements Module { // required } + /** + * Get nested array of given menu identity's menu_items + * + * @param int|string $menuID + * @return array $nested + * + */ + public function nestedArray(int|string $menuID) { + $m = wire('pages')->get('template=menus, name|id|title='.$menuID); + if (!$m->id) return false; + + // Get flat items array: + $json = $m->menu_items; + $array = json_decode($json, true); + // Init nested array: + $nested = []; + + // Populate nested array: + foreach ($array as $k => $v) { + if (array_key_exists('parent_id', $v)) $nested[$v['parent_id']][$k] = $v; + else $nested[$k] = $v; + } + + return $nested; + } + /** * Pass on menu items for processing and rendering. * @@ -275,6 +301,8 @@ class MarkupMenuBuilder extends WireData implements Module { // NEW TAB $m->newtab = isset($item['newtab']) ? 1 : ''; + // TARGET + $m->target = isset($item['target']) ? $item['target'] : ''; // CSS $m->cssID = isset($item['css_itemid']) ? $item['css_itemid'] : ''; @@ -614,7 +642,9 @@ class MarkupMenuBuilder extends WireData implements Module { foreach ($menu as $m) { // set properties - $newtab = $m->newtab ? " target='_blank'" : ''; + $newtab = $m->newtab && empty($m->target) ? " target='_blank'" : ''; + $target = $m->target ? " target='".$m->target."'" : ''; + // if (!empty($target)) $newtab = ''; // Override newtab target attribute // if this menu item is a parent; create the sub-items/child-menu-items if ($m->parentID == $parent) { @@ -658,13 +688,21 @@ class MarkupMenuBuilder extends WireData implements Module { // apply current item class to current page + ancestors if specified for both native and included menu items $itemCurrent = $m->isCurrent == 1 ? $o->currentClass . ' ' : ''; + // a tag: + $aTopLevelClass = $o['aTopLevelClass'] ? $o['aTopLevelClass'].' ' : ''; + $aChildrenClass = $o['aChildrenClass'] ? $o['aChildrenClass'].' ' : ''; + $aParentClass = $o['aParentClass'] ? $o['aParentClass'].' ' : ''; + $aTagClass = $parent == 0 ? $aTopLevelClass : $aChildrenClass; + if ($m->isParent) $aTagClass .= $aParentClass; + $aTagClass = trim($aTagClass); + $classes = $itemCSSClass . $itemHasChildren . $itemLast . $itemCurrent . $itemFirst; $classes = trim(preg_replace('/\s+/', ' ', $classes)); $class = strlen($classes) ? ' class="' . $classes . '"' : ''; // if $iTag is empty, apply css id and classes to instead - if (!$iTag) $out .= "\n\t{$m->title}"; - else $out .= "\n\t<{$iTag}{$itemCSSID}{$class}>\n\t\t{$m->title}"; + if (!$iTag) $out .= "\n\t{$m->title}"; + else $out .= "\n\t<{$iTag}{$itemCSSID}{$class}>\n\t\turl}\">{$m->title}"; // build nested/sub-elements $out .= str_replace("\n", "\n\t\t", $this->buildMenu($m->id)); @@ -717,6 +755,7 @@ class MarkupMenuBuilder extends WireData implements Module { // set properties $newtab = $m->newtab ? " target='_blank'" : ''; + $target = $m->target ? " target='".$m->target."'" : ''; // if this menu item is a parent; create the sub-items/child-menu-items if ($m->parent_id == $parent) { // @note: here and throughout, property names == original array index! @@ -758,8 +797,8 @@ class MarkupMenuBuilder extends WireData implements Module { $class = strlen($classes) ? ' class="' . $classes . '"' : ''; // if $iTag is empty, apply css id and classes to instead - if (!$iTag) $out .= "\n\t{$m->title}"; - else $out .= "\n\t<{$iTag}{$itemCSSID}{$class}>\n\t\t{$m->title}"; + if (!$iTag) $out .= "\n\t{$m->title}"; + else $out .= "\n\t<{$iTag}{$itemCSSID}{$class}>\n\t\t{$m->title}"; // build nested/sub-elements $out .= str_replace("\n", "\n\t\t", $this->buildMenuFromCache($id)); @@ -940,6 +979,7 @@ class MarkupMenuBuilder extends WireData implements Module { $url = $item->url; $newtab = ''; + $target = ''; } else { // grab the menu item in the WireArray with the id=$item @@ -947,14 +987,15 @@ class MarkupMenuBuilder extends WireData implements Module { $title = $m->title; $url = $m->url; $newtab = $m->newtab ? "target='_blank'" : ''; + $target = $m->target ? "target='".$m->target."'" : ''; } // ancestor items if ($total - $i != 1) { $divider = " {$o->divider} "; // note the spaces before and after! // if $iTag is empty, default to instead - if (!$iTag) $out .= "{$title}{$divider}"; - else $out .= "<$iTag>{$title}{$divider}"; + if (!$iTag) $out .= "{$title}{$divider}"; + else $out .= "<$iTag>{$title}{$divider}"; } // the last breadcrumb item, i.e., the current page @@ -1413,6 +1454,7 @@ class MarkupMenuBuilder extends WireData implements Module { 'title' => $m->title, // default_title: 0=show saved mb titles;1=show actual/current pw titles 'url' => $m->url, // menu items URL 'newtab' => $m->newtab, // open link in new tab? + 'target' => $m->target, // open link in new tab? 'css_itemid' => $m->cssID, // the items own CSS ID 'css_itemclass' => $m->cssClass, // the items own CSS class(es) 'include_children' => $m->includeChildren, // include this menu items natural children @@ -2131,6 +2173,9 @@ class MenuOptions extends WireData { current_class_level// NOT for breadcrumbs - 0=unlimited (+ include ancestors of current page that are not menu items);1=current item only;2=current+parent;3=current+parent+grandparent current_css_id// only for breadcrumbs default_class// a default css class to be applied to all menu items + $this->set('aTopLevelClass', isset($options['a_toplevel_class']) ? $options['a_toplevel_class'] : ''); // a tags + $this->set('aChildrenClass', isset($options['a_children_class']) ? $options['a_children_class'] : ''); // a tags + $this->set('aParentClass', isset($options['a_parent_class']) ? $options['a_parent_class'] : ''); // a tags divider// only for breadcrumbs prepend home page at the as topmost item even if it isn't part of the breadcrumb prepend_home// only for breadcrumbs => 0=no;1=yes @@ -2165,6 +2210,9 @@ class MenuOptions extends WireData { $this->set('currentClassLevel', isset($options['current_class_level']) ? $options['current_class_level'] : 1); $this->set('currentCSSID', isset($options['current_css_id']) ? $options['current_css_id'] : ''); $this->set('defaultClass', isset($options['default_class']) ? $options['default_class'] : ''); + $this->set('aTopLevelClass', isset($options['a_toplevel_class']) ? $options['a_toplevel_class'] : ''); + $this->set('aChildrenClass', isset($options['a_children_class']) ? $options['a_children_class'] : ''); + $this->set('aParentClass', isset($options['a_parent_class']) ? $options['a_parent_class'] : ''); $this->set('divider', isset($options['divider']) ? $options['divider'] : '»'); $this->set('prependHome', isset($options['prepend_home']) ? $options['prepend_home'] : 0); $this->set('defaultTitle', isset($options['default_title']) ? $options['default_title'] : 0); @@ -2201,6 +2249,7 @@ class Menu extends WireData { $this->set('title', ''); $this->set('url', ''); $this->set('newtab', ''); + $this->set('target', ''); $this->set('parentID', ''); // mb menu item parent [not pw!] $this->set('pagesID', ''); // for pw pages $this->set('cssID', ''); diff --git a/ProcessMenuBuilder.module b/ProcessMenuBuilder.module index 59493a8..1622151 100644 --- a/ProcessMenuBuilder.module +++ b/ProcessMenuBuilder.module @@ -37,7 +37,7 @@ class ProcessMenuBuilder extends Process implements Module { 'title' => 'Menu Builder: Process', 'summary' => 'Easy, drag and drop menu builder', 'author' => 'Francis Otieno (Kongondo)', - 'version' => '0.2.7', + 'version' => '0.2.82', 'href' => 'http:// processwire.com/talk/topic/4451-module-menu-builder/', 'singular' => true, 'autoload' => false, @@ -430,6 +430,7 @@ class ProcessMenuBuilder extends Process implements Module { $this->_('CSS ID'), $this->_('CSS Class'), $this->_('New Tab'), + $this->_('Target'), )); $n = $modules->get('InputfieldName'); @@ -452,12 +453,17 @@ class ProcessMenuBuilder extends Process implements Module { $itemCustomNewTab = ""; $itemCustomNewTabHidden = "";// force send a value for new tabs + $nt = $modules->get('InputfieldName'); + $nt->attr('name', 'new_item_custom_target[]'); + $nt->attr('class', 'new_custom'); + $t->row(array( $n->render(), $u->render(), $n2->render(), $n3->render(), $itemCustomNewTab . $itemCustomNewTabHidden, + $nt->render(), '', )); @@ -552,6 +558,7 @@ class ProcessMenuBuilder extends Process implements Module { $this->_('CSS ID'), $this->_('CSS Class'), $this->_('New Tab'), + $this->_('Target'), $this->_('Type'),// custom or PW page )); @@ -580,6 +587,8 @@ class ProcessMenuBuilder extends Process implements Module { // does this menu item link open in a new window or not (i.e. target='_blank') - for custom menu items only $itemNewTab = isset($menuItem['newtab']) ? $this->_('Yes') : $this->_('No'); + $itemTarget = isset($menuItem['target']) ? $menuItem['target'] : ''; + $itemCSSID = isset($menuItem['css_itemid']) ? $menuItem['css_itemid'] :''; $itemCSSClass = isset($menuItem['css_itemclass']) ? $menuItem['css_itemclass'] : ''; @@ -590,6 +599,7 @@ class ProcessMenuBuilder extends Process implements Module { $itemCSSID, $itemCSSClass, $itemNewTab, + $itemTarget, $itemType) ); @@ -773,7 +783,7 @@ class ProcessMenuBuilder extends Process implements Module { $r = new InputfieldRadios(); $r->attr('id+name', 'menu_item_disable_items'); $r->label = $this->_('Use enable/disable menu items feature'); - $r->notes = $this->_('Allows you to set some menu items as disabled. If an item is disabled, the item together will all of its descendants will be set as disabled after you save the menu settings. Disabled items will not be output when the menu is viewed in the frontend.'); + $r->notes = $this->_('Allows you to set some menu items as disabled. If an item is disabled, the item together with all of its descendants will be set as disabled after you save the menu settings. Disabled items will not be output when the menu is viewed in the frontend.'); $radioOptions = array ( 1 => $this->_('Yes'), @@ -927,7 +937,7 @@ class ProcessMenuBuilder extends Process implements Module { - css_itemclass: this menu items's CSS Class (optional) - pages_id: for PW pages items = $page->id; for custom menu items = 0 (note: this is different from id!) - optional include children feature - - opitional disable menu items feature + - optional disable menu items feature */ @@ -959,6 +969,7 @@ class ProcessMenuBuilder extends Process implements Module { $this->cssItemClass = isset($item['css_itemclass']) ? $item['css_itemclass'] : ''; $this->itemPagesID = isset($item['pages_id']) ? $item['pages_id'] : 0;// only PW pages will have a pages_id > 0 (equal to their PW page->id) $this->newTab = isset($item['newtab']) ? $item['newtab'] : 0; + $this->target = isset($item['target']) ? $item['target'] : '';// bd($this->target); // 210x bd() for 14 menu items $this->itemIncludeChildren = isset($item['include_children']) ? $item['include_children'] : ''; $this->itemMenuMaxLevel = isset($item['m_max_level']) ? $item['m_max_level'] : ''; $this->disabledItem = isset($item['disabled_item']) ? $item['disabled_item'] : ''; @@ -1332,6 +1343,8 @@ class ProcessMenuBuilder extends Process implements Module { if($this->disableItems == 1) $out .= $this->buildMenuItemDisabledMarkup(); ######################### custom menu item new tab markup ######################### if(0 == $this->itemPagesID) $out .= $this->buildMenuItemNewTabMarkup(); + ######################### custom menu item target markup ######################### + $out .= $this->buildMenuItemTargetMarkup(); ######################### item hidden inputs markup ######################### $out .= $this->buildMenuItemHiddenInputs(); @@ -1534,7 +1547,24 @@ class ProcessMenuBuilder extends Process implements Module { } /** - * Builds hiden inputs for tracking menu items settings. + * Builds input for specifying menu item target attribute. + * + * Used in a menu item's settings. + * + * @access private + * @return string $out Markup of input. + * + */ + private function buildMenuItemTargetMarkup() { + $id = $this->itemID; + $value = isset($this->menuItems[$this->itemID]['target']) ? $this->menuItems[$this->itemID]['target'] : ''; + $out = ' + '; + return $out; + } + + /** + * Builds hidden inputs for tracking menu items settings. * * @access private * @return string $out Markup of hidden inputs. @@ -2425,6 +2455,8 @@ class ProcessMenuBuilder extends Process implements Module { $itemCSSClass = $sanitizer->text($post->css_itemclass[$itemID]);// sanitizer->text to accept multiple classes $itemNewTab = isset($post->newtab[$itemID]) ? 1 : '';// only save for custom menu items with target='_blank' + $itemTarget = isset($post->target[$itemID]) ? $sanitizer->name($post->target[$itemID]) : '';// save for any custom/page(s) + // if current user can edit include children values + change include children setting $itemIncludeChildren = ''; if( $this->includeChildren && isset($post->include_children[$itemID]) ) { @@ -2451,6 +2483,7 @@ class ProcessMenuBuilder extends Process implements Module { 'include_children' => $itemIncludeChildren, 'm_max_level' => $itemMMaxLevel, 'disabled_item' => $itemDisabled, + 'target' => $itemTarget, ); // add disabled item to array to check if to apply same status to descendants @@ -2553,6 +2586,7 @@ class ProcessMenuBuilder extends Process implements Module { $itemCSSClass = $sanitizer->name($post->new_css_itemclass[$i]); //$itemNewTab = (!isset($post->new_newtab[$i])) ? '' : 1;// using checkbox unreliable; use hidden input instead (below) $itemNewTab = (int) $post->new_newtab_hidden[$i] ? 1 : '';// hidden input to resolve above + $itemTarget = isset($post->new_item_custom_target[$i]) ? $post->new_item_custom_target[$i] : ''; // add custom (external) menu items to our menu $menuItems[$menuItemID] = array( @@ -2563,7 +2597,10 @@ class ProcessMenuBuilder extends Process implements Module { 'css_itemclass' => $itemCSSClass, 'pages_id' => '', 'newtab' => $itemNewTab, + 'target' => $itemTarget, ); + // Auto-disable new items: + if ($this->disableItems) $menuItems[$menuItemID]['disabled_item'] = 1; $menuItemID++; @@ -2637,6 +2674,8 @@ class ProcessMenuBuilder extends Process implements Module { 'm_max_level' => $itemMMaxLevel, // 'b_max_level' => $itemBMaxLevel,// @todo - not setting individually for now ); + // Auto-disable new items: + if ($this->disableItems) $menuItems[$menuItemID]['disabled_item'] = 1; $menuItemID++; @@ -2688,6 +2727,8 @@ class ProcessMenuBuilder extends Process implements Module { 'pages_id' => $item->id,// the PW page ID // 'newtab' => ''// NOT necessary for PW pages ); + // Auto-disable new items: + if ($this->disableItems) $menuItems[$menuItemID]['disabled_item'] = 1; $menuItemID++; diff --git a/README.md b/README.md index a259740..2a4039d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # Menu Builder This Module allows you to easily create custom menus/navigation lists in the ProcessWire Admin Panel using drag and drop. In the backend, it uses the [nestedSortable](https://github.com/mjsarfatti/nestedSortable) jQueryUI plugin by Manuele J Sarfatti. +_(Alterations v0.2.8 - 0.2.82 by chimmel)_ + ## Features * Visual menu builder @@ -78,6 +80,10 @@ $defaultOptions = array( 'm_max_level' => 1,// how deep to fetch 'include_children' 'current_class_level' => 1,// how high up the ancestral tree to apply 'current_class' 'default_class' => '',// a CSS class to apply to all menu items + // inner a tags: + 'a_toplevel_class' => '', + 'a_children_class' => '', + 'a_parent_class' => '', ); ```` @@ -1026,6 +1032,16 @@ GPL2 ## Changelog +### Version 0.2.82 +1. Add nestedArray method to MarkupMenuBuilder. + +### Version 0.2.81 +1. Add options a_toplevel_class, a_children_class, a_parent_class. + +### Version 0.2.8 +1. If disable_items menu setting is enabled: automatically disable newly added menu items until positioned in the menu, so as to allow for positioning post-save/addition in a published menu in use, prior to the item being added to its output, e.g., if menu is already in use on the frontend. Once saved/added/disabled, and then positioned, 'Disable' can be unchecked, save, and proper placement attained with no disruption. +2. Add text input for target attribute that overrides 'New Tab', and is applicable on any menu item (custom/page). + ### Version 0.2.7 1. In multi-lingual environments, menus and breadcrumbs can retrieved using their titles or names in any language irrespective of the current user's language. 2. For getMenuItems() usage only, added option extra_fields to return values of some specific fields on the menu item pages. See documentation for compatible Fieldtypes.