Name

bootmenu —

ATTRIBUTES

Attribute Pos. Req. Default Description

DESCRIPTION

BEHAVIOR

This tag appears to be affected by, or affects, the following:
Catalog Variables: MV_TREE_TABLE
Global Variables: MV_PAGE

EXAMPLES

No examples are available at this time. We do consider this a problem and will try to supply some.

NOTES

AVAILABILITY

bootmenu is available in Interchange versions:

5.8.0, 5.9.0 (git-head)

SOURCE

Interchange 5.9.0:

Source: dist/strap/config/bootmenu.tag
Lines: 583


UserTag bootmenu Order        name
UserTag bootmenu hasEndTag
UserTag bootmenu AddAttr
UserTag bootmenu Description  Display menu using jQuery and standard HTML + CSS.
UserTag bootmenu Documentation <<EOD

Returns a menu using an unordered list with associated Bootstrap-recognizable \
 classes (<ul><li>foo</li></ul>). No Javascript is needed for basic functionality.

Call with something like:
[timed-build file="timed/bootmenu" login=1 force=1 minutes=1440][bootmenu \
 name="catalog/menu" timed=1][/bootmenu][/timed-build]
(bootmenu's "name" opt requires a DB menu)
or for simple menu:
[bootmenu file="includes/menus/catalog/menu.txt"][/bootmenu]

You can also put a template inside, and also use "transforms" like "logged_in", e.g.:
[bootmenu file="include/menus/catalog/top.txt" class="nav nav-pills pull-right" logged_in=member]
  <li class="{HELP_NAME}"><a{PAGE?} href="{PAGE}"{/PAGE?} title="{DESCRIPTION}">{NAME}</a>
[/bootmenu]

To be used with Bootstrap and the Javascript plugin for dropdown menus. \
 See http://getbootstrap.com for more.

EOD
UserTag bootmenu Routine      <<EOR

my $indicated;
my $last_line;
my $first_line;
my $logical_field;

my %transform = (
nbsp => sub {
  my ($row, $fields) = @_;
  return 1 if ref($fields) ne 'ARRAY';
  for(@$fields) {
    $row->{$_} =~ s/ /&nbsp;/g;
  }
  return 1;
},
entities => sub {
  my ($row, $fields) = @_;
  return 1 if ref($fields) ne 'ARRAY';
  for(@$fields) {
    $row->{$_} = HTML::Entities::encode_entities($row->{$_});
  }
  return 1;
},
localize => sub {
  my ($row, $fields) = @_;
  return 1 if ref($fields) ne 'ARRAY';
  for(@$fields) {
    $row->{$_} = errmsg($row->{$_});
  }
  return 1;
},
first_line => sub {
  my ($row, $fields) = @_;
  return undef if ref($fields) ne 'ARRAY';
  return 1 if $first_line;
  my $status;
  for(@$fields) {
    if(s/^!\s*//) {
      $status = $status && ! $row->{$_};
    }
    else {
      $status = $status && $row->{$_};
    }
  }
  return $first_line = $status;
},
last_line => sub {
  my ($row, $fields) = @_;
#::logDebug("last_line transform, last_line=$last_line");
  return 1 if ref($fields) ne 'ARRAY';
  return 0 if $last_line;
  my $status;
  for(@$fields) {
#::logDebug("last_line transform checking field $_=$row->{$_}");
    if(s/^!\s*//) {
      $status = ! $row->{$_};
    }
    else {
      $status = $row->{$_};
    }
#::logDebug("last_line transform checked field $_=$row->{$_}, status=$status");
    last if $status;
  }
#::logDebug("last_line transform returning last_line=$status");
  $last_line = $status;
#::logDebug("last_line transform returning status=" . ! $status);
  return ! $status;
},
first_line => sub {
  my ($row, $fields) = @_;
  return 1 if ref($fields) ne 'ARRAY';
  my $status = 1;
  for(@$fields) {
    if(s/^!\s*//) {
      $status = $status && ! $row->{$_};
    }
    else {
      $status = $status && $row->{$_};
    }
  }
  return $status;
},
inactive => sub {
  my ($row, $fields) = @_;
  return 1 if ref($fields) ne 'ARRAY';
  my $status = 1;
  for(@$fields) {
    if(s/^!\s*//) {
      $status = $status && $row->{$_};
    }
    else {
      $status = $status && ! $row->{$_};
    }
  }
  return $status;
},
active => sub {
  my ($row, $fields) = @_;
  return 1 if ref($fields) ne 'ARRAY';
  my $status = 1;
  for(@$fields) {
    if(s/^!\s*//) {
      $status = $status && ! $row->{$_};
    }
    else {
      $status = $status && $row->{$_};
    }
  }
  return $status;
},
ui_security => sub {
  my ($row, $fields) = @_;
  return 1 if ref($fields) ne 'ARRAY';
  my $status = 1;
  for(@$fields) {
    next if ! length($row->{$_});
    $status = $status && Vend::Tags->if_mm('advanced', $row->{$_});
  }
  return $status;
},
full_interpolate => sub {
  my ($row, $fields) = @_;
  return 1 if ref($fields) ne 'ARRAY';
  for(@$fields) {
    next unless $row->{$_} =~ /\[|__[A-Z]\w+__/;
    $row->{$_} = Vend::Interpolate::interpolate_html($row->{$_});
  }
  return 1;
},
page_class => sub {
  my ($row, $fields) = @_;
  return 1 unless $row->{indicated};
  return 1 if $row->{mv_level};
  return 1 if ref($fields) ne 'ARRAY';
  my $status = 1;
  for(@$fields) {
    my($f, $c) = split /[=~]+/, $_;
    $c ||= $f;
#::logDebug("setting scratch $f to row=$c=$row->{$c}");
    $::Scratch->{$f} = $row->{$c};
  }
  $$indicated = 0;
  return 1;
},
menu_group => sub {
  my ($row, $fields) = @_;
  return 1 if ref($fields) ne 'ARRAY';
  my $status = 1;
  eval {
    for(@$fields) {
      my($f, $c) = split /[=~]+/, $_;
      $c ||= $f;
      $status = $status && (
              !  $row->{$f}
              or $CGI::values{$c} =~ /$row->{$f}/i
              );
    }
  };
  return $status;
},
superuser => sub {
  my ($row, $fields) = @_;
  return 1 if ref($fields) ne 'ARRAY';
  my $status = 1;
  for(@$fields) {
    $status = $status && (! $row->{$_} or Vend::Tags->if_mm('super'));
  }
  return $status;
},
items  => sub {
  my ($row, $fields) = @_;
  return 1 if ref($fields) ne 'ARRAY';
  my $status = 1;
  my $nitems = scalar(@{$Vend::Items}) ? 1 : 0;
  for(@$fields) {
    next if ! length($row->{$_});
    $status = $status && (! $nitems ^ $row->{$_});
  }
  return $status;
},
logged_in => sub {
  my ($row, $fields) = @_;
#::logDebug("logged_in... doing:$_, fields=" . ref($fields) . ', ' . uneval($fields));
  return 1 if ref($fields) ne 'ARRAY';
  my $status = 1;
  for(@$fields) {
    next if ! length($row->{$_});
    $status = $status && (! $::Vend::Session->{logged_in} ^ $row->{$_});
  }
#::logDebug("logged_in... got here. doing:$_, status=$status, row=$row->{$_}");
  return $status;
},
depends_on => sub {
  my ($row, $fields) = @_;
  return 1 if ref($fields) ne 'ARRAY';
  my $status = 1;
  for(@$fields) {
    next if ! $row->{$_};
    $status = $status && $CGI::values{$row->{$_}};
  }
  return $status;
},
exclude_on => sub {
  my ($row, $fields) = @_;
  return 1 if ref($fields) ne 'ARRAY';
  my $status = 1;
  for(@$fields) {
    $status = $status && (! $CGI::values{$row->{$_}});
  }
  return $status;
},
indicator_class => sub {
  my ($row, $fields) = @_;
  return 1 if ref($fields) ne 'ARRAY';
  for(@$fields) {
    my ($indicator,$rev, $last, $status);
    my($s,$r) = split /=/, $_;
    $rev = $indicator =~ s/^\s*!\s*// ? 1 : 0;
    $last = $indicator =~ s/\s*!\s*$// ? 1 : 0;
#::logDebug("checking scratch $s=$::Scratch->{$s} eq row=$r=$row->{$r}");
    $status = $::Scratch->{$s} eq $row->{$r};
    if($rev xor $status) {
      $row->{indicated} = 1;
    }
    last if $last;
  }
  if($row->{indicated}) {
    $indicated = \$row->{indicated};
  }
  return 1;
},
indicator_profile => sub {
  my ($row, $fields) = @_;
  return 1 if ref($fields) ne 'ARRAY';
  for(@$fields) {
    my ($indicator,$rev, $last, $status);
    next unless $indicator = $row->{$_};
    $rev = $indicator =~ s/^\s*!\s*// ? 1 : 0;
    $last = $indicator =~ s/\s*!\s*$// ? 1 : 0;
    $status = Vend::Tags->run_profile($indicator);
    if($rev xor $status) {
      $row->{indicated} = 1;
      next unless $last;
    }
    last if $last;
  }
  return 1;
},
indicator_page => sub {
  my ($row, $fields) = @_;
  return 1 if ref($fields) ne 'ARRAY';
  for(@$fields) {
    if ($::Scratch->{mv_logical_page} eq $row->{$_}) {
      unless(
          $::Scratch->{mv_logical_page_used}
          and $::Scratch->{mv_logical_page_used}
            ne
          $row->{$logical_field}
        )
    {
      $row->{indicated} = 1;
      $::Scratch->{mv_logical_page_used} = $row->{$logical_field};
      last;
    }
  }
  ($row->{indicated} = 1, last)
    if  $Global::Variable->{MV_PAGE} eq $row->{$_}
    and ! defined $row->{indicated};
}
return 1;
},
indicator => sub {
  my ($row, $fields) = @_;
  return 1 if ref($fields) ne 'ARRAY';
  for(@$fields) {
    my ($indicator,$rev, $last, $status);
    next unless $indicator = $row->{$_};
    $rev = $indicator =~ s/^\s*!\s*// ? 1 : 0;
    $last = $indicator =~ s/\s*!\s*$// ? 1 : 0;
    if($indicator =~ /^\s*([-\w.:][-\w.:]+)\s*$/) {
      $status =  $CGI::values{$1};
    }
    elsif ($indicator =~ /^\s*`(.*)`\s*$/s) {
      $status = Vend::Interpolate::tag_calc($1);
    }
    elsif ($indicator =~ /\[/s) {
      $status = Vend::Interpolate::interpolate_html($indicator);
      $status =~ s/\s+//g;
    }
    if($rev xor $status) {
      $row->{indicated} = 1;
    }
    else {
      $row->{indicated} = '';
    }
    last if $last;
  }
  return 1;
},
expand_values_form => sub {
  my ($row, $fields) = @_;
  return 1 if ref($fields) ne 'ARRAY';
  for(@$fields) {
    next unless $row->{$_} =~ /\%5b|\[/i;
    my @parms = split $Global::UrlSplittor, $row->{$_};
    my @out;
    for my $p (@parms) {
      my ($parm, $val) = split /=/, $p, 2;
      $val = unhexify($val);
      $val =~ s/\[cgi\s+([^\[]+)\]/$CGI::values{$1}/g;
      $val =~ s/\[var\s+([^\[]+)\]/$::Variable->{$1}/g;
      $val =~ s/\[value\s+([^\[]+)\]/$::Values->{$1}/g;
      push @out, join('=', $parm, hexify($val));
    }
    $row->{$_} = join $Global::UrlJoiner, @out;
  }
  return 1;
},
expand_values => sub {
  my ($row, $fields) = @_;
  return 1 if ref($fields) ne 'ARRAY';
  for(@$fields) {
    next unless $row->{$_} =~ /\[/;
    $row->{$_} =~ s/\[cgi\s+([^\[]+)\]/$CGI::values{$1}/g;
    $row->{$_} =~ s/\[var\s+([^\[]+)\]/$::Variable->{$1}/g;
    $row->{$_} =~ s/\[value\s+([^\[]+)\]/$::Values->{$1}/g;
  }
  return 1;
},
);

sub reset_transforms {
#::logDebug("resetting transforms");
my $opt = shift;
if($opt) {
  $logical_field = $opt->{logical_page_field} || 'name';
}
undef $last_line;
undef $first_line;
undef $indicated;
}

sub {
my($name, $opt, $template) = @_;

reset_transforms($opt);

my @transform;
my @ordered_transform = qw/full_interpolate indicator_page page_class \
 indicator_class localize entities nbsp/;
my %ordered;
@ordered{@ordered_transform} = @ordered_transform;

for(keys %transform) {
  next if $ordered{$_};
  next unless $opt->{$_};
  my @fields = grep /\S/, split /[\s,\0]+/, $opt->{$_};
  $opt->{$_} = \@fields;
#::logDebug("opt $_ = " . uneval(\@fields));
  push @transform, $_;
}
for(@ordered_transform) {
  next unless $opt->{$_};
  my @fields = grep /\S/, split /[\s,\0]+/, $opt->{$_};
  $opt->{$_} = \@fields;
  push @transform, $_;
}
$opt->{_transform} = \@transform;
#::logDebug("in menu sub main");

my @out;

$template = <<EOF if $template !~ /\S/;
  {INDICATOR?}<li class="{INDICATOR}">{/INDICATOR?}
{INDICATOR:}<li class="{BOOT_LI}">
  <a{PAGE?} href="{PAGE}"{/PAGE?} title="{DESCRIPTION}" class="{LINK_CLASS}" \
 \
 {BOOT_CONTENT}>{ICON?}{ICON} {/ICON?}{NAME}{CARET?} {CARET}{/CARET?}</a>
{/INDICATOR:}
EOF

my $top_timeout = $opt->{timeout} || 1000;

my %o = (
  start       => $opt->{tree_selector} || $opt->{name},
  file    => $opt->{file},
  table       => $opt->{table} || $::Variable->{MV_TREE_TABLE} || 'tree',
  master      => 'parent_fld',
  subordinate => 'code',
  autodetect  => '1',
  sort        => $opt->{sort} || 'code',
    full        => '1',
    timed    => $opt->{timed},
    spacing     => '4',
    _transform   => $opt->{_transform},
  );

for(@{$opt->{_transform} || []}) {
  $o{$_} = $opt->{$_};
}

my $main;
my $rows;
if($opt->{iterator}) {
  $o{iterator} = $opt->{iterator};
  $main =  Vend::Tags->tree(\%o);
  $rows = $o{object}{mv_results};
}
else {
  Vend::Tags->tree(\%o);
#::logDebug("bootmenu: " . uneval({ ref => \%o }) );
  my @o;
  for(@{$o{object}{mv_results}}) {
    next if $_->{deleted};

    for my $tr (@{$o{_transform}}) {
#::logDebug("running transform: $tr, on: " . uneval($_) . ", " . uneval($opt->{$tr}));
      my $status = $transform{$tr}->($_, $opt->{$tr});
#::logDebug("transform... status=$status, did: $tr, result: " . uneval($_));
      $opt->{next_level} = $_->{mv_level}
        if ! $status;
      $_->{deleted} = 1 unless $status;
    }

    if($_->{page} and $_->{page} !~ m{^(\w+:)?/}) {
      my $form = $_->{form};
      if($form and $form !~ /[\r\n]/) {
        $form = join "\n", split $Global::UrlSplittor, $form;
      }

      $_->{page} = "" if $_->{page} eq 'index';

      my $add = ($::Scratch->{mv_add_dot_html} && $_->{page} !~ /\.\w+$/) || 0;

      $_->{page} = Vend::Tags->area({
                  href => $_->{page},
                  form => $form,
                  no_count => $o{timed},
                  add_dot_html => $add,
                  no_session_id => $o{timed},
                  auto_format => 1,
                }) unless $_->{page} =~ /^#/;

    }
    
    push @o, $_ unless $_->{deleted};
  }
  $rows = \@o;
}

$rows->[-1]{mv_last_row} = 1 if @$rows;

#::logDebug("rows = " . ::uneval({ ref => $rows }) );

$name =~ s|/|_|g;
$opt->{ul_id} ||= $name;
$opt->{class} ||= 'nav navbar-nav';

my $id = $opt->{ul_id} ? q{ id="$opt->{ui_id}"} : '';
my $style = $opt->{style} ? q{ style="$opt->{style}"} : '';

push @out, <<EOF;
<ul$id class="$opt->{class}"$style $opt->{extra}>
EOF

#return Vend::Tags->uneval({ ref => $rows });

## Dropdown classes

my $boot_content = qq| data-toggle="dropdown" role="button" data-target="#"|;
my $boot_class = qq|dropdown-toggle|;
my $boot_li = qq|dropdown|;
my $caret = $opt->{caret} || qq|<b class="caret"></b>|;

my $class = $opt->{class};
my $link_class = $opt->{link_class};
my $li_class = $opt->{li_class};

my $z = 0;
my $last_level = 0;

for my $row (@$rows) {
#Debug("mvlevel:$row->{mv_level} last:$last_level lastrow:$row->{mv_last_row} z:$z");
  next if $row->{deleted};
  if($row->{mv_children} > 0){
    $row->{boot_content} = $boot_content;
    $row->{link_class} = "$boot_class $link_class";
    $row->{caret} = $caret;
    $row->{boot_li} = "$boot_li $li_class";
  }
  else{
    $row->{link_class} = $link_class;
    $row->{boot_li} = "$li_class";
  }

  ## Allow for bootstrap icon set

  unless ($row->{img_icon} =~ /\.(jpg|gif|png|jpeg)$/i){
    $row->{icon} = qq|<i class="$row->{img_icon}"></i>| if $row->{img_icon};
  }

  my ($in_template, $list_open, $list_close);
  if($row->{mv_level} > $last_level) {   # new nested list
    $list_open = <<EOF;
<ul class="dropdown dropdown-menu">
EOF

  }
  elsif($row->{mv_level} < $last_level) {   # end of nested list
    $list_close = <<EOF;
  <!-- level:$row->{mv_level} -->
  </li>
</ul>
  </li>
EOF
    my $add_close = <<EOF;
</ul>
EOF
    my $level_diff = $last_level - $row->{mv_level};
    for(2..$level_diff) {
      $list_close .= $add_close;
    }
  }
  elsif ($z) {
    $list_close = <<EOF;
  </li>
EOF
  }
  $in_template = $list_close . $list_open . $template;
  if($row->{mv_last_row}) {

    my $scl = <<EOF;
  </li>
EOF
    my $cl = <<EOF;
  </li>
</ul>
EOF
    if ($row->{mv_level} == 1){
      $in_template .= $cl;
    }
    elsif ($row->{mv_level} > 1) {
      while($last_level >= 0){
        $in_template .= $cl;
        $last_level--;
      }
    }
    $in_template .= $scl;
  }

  push @out, Vend::Tags->uc_attr_list($row, $in_template);
  $last_level = $row->{mv_level};
  $z++;
}

push @out, <<EOF;
</ul>
EOF

return join "", @out;
}

EOR

AUTHORS

Interchange Development Group

SEE ALSO

DocBook! Interchange!