12 $::lxdebug = LXDebug->new;
 
  15   'menus/erp.ini'   => 'menus/user/00-erp.yaml',
 
  16   'menus/crm.ini'   => 'menus/user/10-crm.yaml',
 
  17   'menus/admin.ini' => 'menus/admin/00-admin.yaml',
 
  20 my %known_arguments = (
 
  23   INSTANCE_CONF => 'INSTANCE_CONF',
 
  30 sub translate_to_yaml {
 
  31   my ($menu_file, $new_file) = @_;
 
  35   my $menu       = Inifile->new($menu_file);
 
  36   my @menu_items = map { +{ %{ $menu->{$_} }, ID => $_ } } @{ delete $menu->{ORDER} };
 
  38   for my $item (@menu_items) {
 
  40     my @tokens = split /--/, delete $item->{ID};
 
  41     my $name   = pop @tokens;
 
  42     my $parent = join '_', map { lc $_ } @tokens;
 
  43     my $id     = join '_', grep $_, $parent, lc $name;
 
  45     # move unknown arguments to param subhash
 
  46     my @keys = keys %$item;
 
  49       next if $known_arguments{$_};
 
  50       $params{$_} = delete $item->{$_};
 
  53     $item->{params} = \%params if keys %params;
 
  56     for (keys %known_arguments) {
 
  57       next unless exists $item->{$_};
 
  58       my $val = delete $item->{$_};
 
  59       $item->{ $known_arguments{$_} } = $val;
 
  63     if ($item->{submenu}) {
 
  64       delete $item->{submenu};
 
  67     #sanitize those stupid menu inlinks
 
  68     if ($item->{module} eq 'menu.pl') {
 
  69       delete $item->{module};
 
  70       delete $item->{action};
 
  71       delete $item->{target};
 
  74     # sanitize INSTANCE_CONF
 
  75     if ($item->{INSTANCE_CONF}) {
 
  76       my $instance_conf = delete $item->{INSTANCE_CONF};
 
  77       if ($item->{access}) {
 
  78         if ($item->{access} =~ /\W/) {
 
  79           $item->{access} = "client/$instance_conf & ( $item->{access} )";
 
  81           $item->{access} = "client/$instance_conf & $item->{access}";
 
  84         $item->{access} = "client/$instance_conf";
 
  88     # make controller.pl implicit
 
  89     if ($item->{module} && $item->{module} eq 'controller.pl') {
 
  90       delete $item->{module};
 
  95     $item->{id} =~ s/[^\w]+/_/g;
 
  98     $item->{name} = $name;
 
 102       $item->{parent} = $parent;
 
 103       $item->{parent} =~ s/[^\w]+/_/g if $item->{parent};
 
 107     $item->{order} = 100 * ++$counter{ $item->{parent} };
 
 110   if ($menu_file =~ /crm/) {
 
 111     $menu_items[0]{order} = 50; # crm first
 
 114   open my $out_file, '>:utf8', $new_file or die $!;
 
 115   print $out_file yaml_dump(\@menu_items);
 
 120   # YAML dumps keys lexically sorted, which isn't what we want.
 
 121   # we want this order:
 
 135   # ...oh and we want action in params first
 
 138   # 1. parent is what is used to anchor. one could argue that id should be
 
 139   #    first, but parent is easier for understanding structure.
 
 140   # 2. after parent the logical structure is
 
 142   #    2. stuff related to vidual presentation (name/icon)
 
 143   #    3. stuff needed for logical presentaion (order/access)
 
 144   #    4. stuff related to the action after clicking it
 
 145   # 3. without parent and href (the second is pretty rare) the keys are nicely
 
 146   #    ascending in length, which is very easy to parse visually.
 
 149   for my $node (@$ary_ref) {
 
 151     for my $key (@order) {
 
 152       next unless exists $node->{$key};
 
 153       $yaml .= ($first++ ? '  ' : '- ') . $key . ":";
 
 154       if (!ref $node->{$key}) {
 
 155         $yaml .= ' ' . $node->{$key} . "\n";
 
 158         for ('action', grep !/^action$/, keys %{ $node->{$key} }) {
 
 159           next unless exists $node->{$key}{$_};
 
 160           $yaml .= "    $_: $node->{$key}{$_}\n";
 
 170 while (my ($in, $out) = each(%menu_files)) {
 
 171   translate_to_yaml($in, $out);