sub init_sub_layouts {
[
SL::Layout::None->new,
- SL::Layout::CssMenu->new(menu => Menu->new('menus/admin.ini')),
+ SL::Layout::CssMenu->new(menu => SL::Menu->new('admin')),
]
}
}
sub init_menu {
- my @menu_files = qw(menus/erp.ini);
- unshift @menu_files, 'menus/crm.ini' if $::instance_conf->crm_installed;
- Menu->new(@menu_files);
+ SL::Menu->new('user');
}
sub init_auto_reload_resources_param {
use strict;
use parent qw(SL::Layout::Base);
-use URI;
-
-sub print_menu {
- my ($self, $parent, $depth) = @_;
-
- my $html;
-
- die if ($depth * 1 > 5);
-
- my @menuorder;
- my $menu = $self->menu;
-
- @menuorder = $menu->access_control(\%::myconfig, $parent);
-
- $parent .= "--" if ($parent);
-
- foreach my $item (@menuorder) {
- substr($item, 0, length($parent)) = "";
- next if (($item eq "") || ($item =~ /--/));
-
- my $menu_item = $menu->{"${parent}${item}"};
- my $menu_title = $::locale->text($item);
- my $menu_text = $menu_title;
-
- if ($menu_item->{"submenu"} || !defined($menu_item->{"module"}) && !defined($menu_item->{href})) {
-
- my $h = $self->print_menu("${parent}${item}", $depth * 1 + 1)."\n";
- if (!$parent) {
- $html .= qq|<ul><li><h2>${menu_text}</h2><ul>${h}</ul></li></ul>\n|;
- } else {
- $html .= qq|<li><div class="x">${menu_text}</div><ul>${h}</ul></li>\n|;
- }
- } else {
- if ($self->{sub_class} && $depth > 1) {
- $html .= qq|<li class='sub'>|;
- } else {
- $html .= qq|<li>|;
- }
- $html .= $self->menuitem_v3("${parent}$item", { "title" => $menu_title });
- $html .= qq|${menu_text}</a></li>\n|;
- }
- }
-
- return $html;
-}
-
-sub menuitem_v3 {
- $main::lxdebug->enter_sub();
-
- my ($self, $item, $other) = @_;
- my $menuitem = $self->menu->{$item};
-
- my $action = "section_menu";
- my $module;
-
- if ($menuitem->{module}) {
- $module = $menuitem->{module};
- }
- if ($menuitem->{action}) {
- $action = $menuitem->{action};
- }
-
- my $level = $::form->escape($item);
-
- my @vars;
- my $target = $menuitem->{target} ? qq| target="| . $::form->escape($menuitem->{target}) . '"' : '';
- my $str = qq|<a${target} href="|;
-
- if ($menuitem->{href}) {
- $main::lxdebug->leave_sub();
- return $str . $menuitem->{href} . '">';
- }
-
- $str .= qq|$module?action=| . $::form->escape($action);
-
- map { delete $menuitem->{$_} } qw(module action target href);
-
- # add other params
- foreach my $key (keys %{ $menuitem }) {
- $str .= "&" . $::form->escape($key, 1) . "=";
- my ($value, $conf) = split(/=/, $menuitem->{$key}, 2);
- $value = $::myconfig{$value} . "/$conf" if ($conf);
- $str .= $::form->escape($value, 1);
- }
-
- $str .= '"';
-
- if ($other) {
- foreach my $key (keys(%{$other})) {
- $str .= qq| ${key}="| . $::form->quote($other->{$key}) . qq|"|;
- }
- }
-
- $str .= ">";
-
- $main::lxdebug->leave_sub();
-
- return $str;
-}
-
sub use_stylesheet {
qw(icons16.css frame_header/header.css),
}
sub pre_content {
- $_[0]->presenter->render('menu/menuv3',
- menu => $_[0]->print_menu,
- );
+ $_[0]->presenter->render('menu/menuv3', menu => $_[0]->menu);
}
1;
$self->SUPER::use_javascript(@_);
}
+sub javascripts_inline {
+ $_[0]->SUPER::javascripts_inline,
+<<'EOJS'
+ DHTMLSuite.createStandardObjects();
+ DHTMLSuite.configObj.setImagePath('image/dhtmlsuite/');
+ var menu_model = new DHTMLSuite.menuModel();
+ menu_model.addItemsFromMarkup('main_menu_model');
+ menu_model.init();
+ var menu_bar = new DHTMLSuite.menuBar();
+ menu_bar.addMenuItems(menu_model);
+ menu_bar.setTarget('main_menu_div');
+ menu_bar.init();
+EOJS
+}
+
sub pre_content {
$_[0]->SUPER::pre_content .
- &display
+ $_[0]->presenter->render("menu/menunew",
+ force_ul_width => 1,
+ menu => $_[0]->menu,
+ icon_path => sub { my $img = "image/icons/16x16/$_[0].png"; -f $img ? $img : () },
+ max_width => sub { 10 * max map { length $::locale->text($_->{name}) } @{ $_[0]{children} || [] } },
+ );
}
sub start_content {
$_[0]->SUPER::stylesheets;
}
-sub display {
- my ($self) = @_;
-
- $self->presenter->render("menu/menunew",
- force_ul_width => 1,
- menu_items => $self->acc_menu,
- );
-}
-
-sub acc_menu {
- my ($self) = @_;
-
- my $menu = $self->menu;
-
- my $all_items = [];
- $self->create_menu($menu, $all_items);
-
- my $item = { 'subitems' => $all_items };
- calculate_width($item);
-
- return $all_items;
-}
-
-sub calculate_width {
- my $item = shift;
-
- $item->{max_width} = max map { length $_->{title} } @{ $item->{subitems} };
-
- foreach my $subitem (@{ $item->{subitems} }) {
- calculate_width($subitem) if ($subitem->{subitems});
- }
-}
-
-sub create_menu {
- my ($self, $menu, $all_items, $parent, $depth) = @_;
- my $html;
-
- my $form = $main::form;
- my %myconfig = %main::myconfig;
-
- $depth ||= 0;
-
- die if ($depth * 1 > 5);
-
- my @menuorder = $menu->access_control(\%myconfig, $parent);
- $parent .= "--" if ($parent);
- $parent ||= '';
-
- foreach my $name (@menuorder) {
- substr($name, 0, length($parent), "");
- next if (($name eq "") || ($name =~ /--/));
-
- my $menu_item = $menu->{"${parent}${name}"};
- my $item = { 'title' => $::locale->text($name) };
- push @{ $all_items }, $item;
-
- if ($menu_item->{submenu} || (!defined($menu_item->{module}) && !defined($menu_item->{href}))) {
- $item->{subitems} = [];
- $item->{image} = _icon_path($menu_item->{ICON});
- $self->create_menu($menu, $item->{subitems}, "${parent}${name}", $depth * 1 + 1);
-
- } else {
- $item->{image} = _icon_path("${parent}${name}.png");
- $menu->menuitem_new("${parent}${name}", $item);
- }
- }
-}
-
-sub _icon_path {
- my ($label, $size) = @_;
-
- $size ||= 16;
-
- my $img = "image/icons/${size}x${size}/$label.png";
-
- return unless -f $img;
- return $img;
-}
-
1;
sub section_menu {
my ($menu) = @_;
- my @menuorder = @{ $menu->{ORDER} };
my @items;
my @id_stack = (-1);
- for my $item (@menuorder) {
- my $menuitem = $menu->{$item};
- my $olabel = apply { s/.*--// } $item;
- my $ml = apply { s/--.*// } $item;
- my $icon_class = apply { $_ = lc $_; s/[^a-z0-9_-]/-/g } $menuitem->{ICON};
- my $level = (0 + $item =~ s/--/--/g);
- my $spacer = "s" . $level;
+ for my $node ($menu->tree_walk) {
+ my $level = $node->{level};
# do id stack
push @id_stack, -1 if $level > $#id_stack;
pop @id_stack while $level < $#id_stack;
$id_stack[-1]++;
- my $label = $::locale->text($olabel);
+ my $label = $::locale->text($node->{name});
+ my $href = $menu->href_for_node($node);
- $menuitem->{module} ||= $::form->{script};
- $menuitem->{action} ||= "section_menu";
- $menuitem->{href} ||= "$menuitem->{module}?action=$menuitem->{action}";
+ my @common_args = ($label, "s" . $level, join '_', @id_stack);
- # add other params
- foreach my $key (keys %$menuitem) {
- next if $key =~ /target|module|action|href|ICON/;
- $menuitem->{href} .= "&" . $::form->escape($key, 1) . "=";
- my ($value, $conf) = split(/=/, $menuitem->{$key}, 2);
- $value = $::myconfig{$value} . "/$conf" if ($conf);
- $menuitem->{href} .= $::form->escape($value, 1);
- }
-
- my @common_args = ($label, $spacer, join '_', @id_stack);
-
- if ($spacer eq 's0') { # toplevel
- push @items, [ @common_args, "icon24 $icon_class", 'm' ];
- } elsif ($menuitem->{submenu}) {
+ if (!$node->{parent}) { # toplevel
+ push @items, [ @common_args, "icon24 $node->{icon}", 'm' ];
+ } elsif ($node->{children}) {
push @items, [ @common_args, "icon16 submenu", 'sm' ];
- } elsif ($menuitem->{module}) {
- push @items, [ @common_args, "icon16 $icon_class", 'i', $menuitem->{href}, $menuitem->{target} ];
+ } else {
+ push @items, [ @common_args, "icon16 $node->{icon}", 'i', $href, $node->{target} ];
}
}
-#=====================================================================
-# LX-Office ERP
-# Copyright (C) 2004
-# Based on SQL-Ledger Version 2.1.9
-# Web http://www.lx-office.org
-#
-#=====================================================================
-# SQL-Ledger Accounting
-# Copyright (C) 2001
-#
-# Author: Dieter Simader
-# Email: dsimader@sql-ledger.org
-# Web: http://www.sql-ledger.org
-#
-# Contributors:
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-#=====================================================================
-#
-# routines for menu items
-#
-#=====================================================================
-
-package Menu;
+package SL::Menu;
use strict;
use SL::Auth;
-use SL::Inifile;
-
-our @ISA = qw(Inifile);
-
+use YAML::XS ();
+use File::Spec;
+use SL::MoreCommon qw(uri_encode);
sub new {
- $main::lxdebug->enter_sub();
+ my ($package, $domain) = @_;
- my ($package, @menufiles) = @_;
+ my $path = File::Spec->catdir('menus', $domain);
- my $self = $package->SUPER::new($menufiles[0]);
+ opendir my $dir, $path or die "can't open $path: $!";
+ my @files = sort grep -f "$path/$_", readdir $dir;
+ close $dir;
- for (@menufiles[1..$#menufiles]) {
- my $inifile = Inifile->new($_);
- push @{ $self->{ORDER} }, @{ delete $inifile->{ORDER} };
- $self->{$_} = $inifile->{$_} for keys %$inifile;
+ my $nodes = [];
+ my $nodes_by_id = {};
+ for my $file (@files) {
+ my $data = YAML::XS::LoadFile(File::Spec->catfile($path, $file));
+ _merge($nodes, $nodes_by_id, $data);
}
- $self->set_access;
- $main::lxdebug->leave_sub();
+ my $self = bless {
+ nodes => $nodes,
+ by_id => $nodes_by_id,
+ }, $package;
+
+ $self->build_tree;
+ $self->set_access;
return $self;
}
-sub menuitem_new {
- $main::lxdebug->enter_sub(LXDebug::DEBUG2());
+sub _merge {
+ my ($nodes, $by_id, $data) = @_;
- my ($self, $name, $item) = @_;
+ die 'not an array ref' unless $data && 'ARRAY' eq ref $data; # TODO check this sooner, to get better diag to user
- my $module = $self->{$name}->{module} || $::form->{script};
- my $action = $self->{$name}->{action};
+ for my $node (@$data) {
+ my $id = $node->{id};
- $item->{target} = $self->{$name}->{target} || "main_window";
- $item->{href} = $self->{$name}->{href} || "${module}?action=" . $::form->escape($action);
+ my $merge_to = $by_id->{$id};
- my @vars = qw(module target href);
- push @vars, 'action' unless ($self->{$name}->{href});
+ if (!$merge_to) {
+ push @$nodes, $node;
+ $by_id->{$id} = $node;
+ next;
+ }
- map { delete $self->{$name}{$_} } @vars;
+ # TODO make this a real recursive merge
+ # TODO add support for arrays
+
+ # merge keys except params
+ for my $key (keys %$node) {
+ if (ref $node->{$key}) {
+ if ('HASH' eq ref $node->{$key}) {
+ $merge_to->{$key} = {} if !exists $merge_to->{$key} || 'HASH' ne ref $merge_to->{$key};
+ for (keys %{ $node->{params} }) {
+ $merge_to->{$key}{$_} = $node->{params}{$_};
+ }
+ } else {
+ die "unsupported structure @{[ ref $node->{$key} ]}";
+ }
+ } else {
+ $merge_to->{$key} = $node->{$key};
+ }
+ }
+ }
+}
- # add other params
- foreach my $key (keys %{ $self->{$name} }) {
- my ($value, $conf) = split(m/=/, $self->{$name}->{$key}, 2);
- $value = $::myconfig->{$value} . "/$conf" if ($conf);
- $item->{href} .= "&" . $::form->escape($key) . "=" . $::form->escape($value);
+sub build_tree {
+ my ($self) = @_;
+
+ # first, some sanity check. are all parents valid ids or empty?
+ for my $node ($self->nodes) {
+ next if !exists $node->{parent} || !$node->{parent} || $self->{by_id}->{$node->{id}};
+ die "menu: node $node->{id} has non-existant parent $node->{parent}";
}
- $main::lxdebug->leave_sub(LXDebug::DEBUG2());
-}
+ my %by_parent;
+ # order them by parent
+ for my $node ($self->nodes) {
+ push @{ $by_parent{ $node->{parent} } //= [] }, $node;
+ }
-sub access_control {
- $main::lxdebug->enter_sub(2);
+ my $tree = { };
+ $self->{by_id}{''} = $tree;
- my ($self, $myconfig, $menulevel) = @_;
- my @menu = ();
+ for (keys %by_parent) {
+ my $parent = $self->{by_id}{$_};
+ $parent->{children} = [ sort { $a->{order} <=> $b->{order} } @{ $by_parent{$_} } ];
+ }
+
+ _set_level_rec($tree->{children}, 0);
+
+ $self->{tree} = $tree->{children};
+}
+
+sub _set_level_rec {
+ my ($ary_ref, $level) = @_;
- if (!$menulevel) {
- @menu = grep { !/--/ } @{ $self->{ORDER} };
- } else {
- @menu = grep { /^${menulevel}--/ } @{ $self->{ORDER} };
+ for (@$ary_ref) {
+ $_->{level} = $level;
+ _set_level_rec($_->{children}, $level + 1) if $_->{children};
}
+}
- $main::lxdebug->leave_sub(2);
+sub nodes {
+ @{ $_[0]{nodes} }
+}
+
+sub tree_walk {
+ my ($self, $all) = @_;
- return @menu;
+ _tree_walk_rec($self->{tree}, $all);
}
-sub parse_access_string {
- my $self = shift;
- my $key = shift;
- my $access = shift;
+sub _tree_walk_rec {
+ my ($ary_ref, $all) = @_;
+ map { $_->{children} ? ($_, _tree_walk_rec($_->{children}, $all)) : ($_) } grep { $all || $_->{visible} } @$ary_ref;
+}
- my $form = $main::form;
- my $auth = $main::auth;
- my $myconfig = \%main::myconfig;
+sub parse_access_string {
+ my ($self, $node) = @_;
my @stack;
my $cur_ary = [];
push @stack, $cur_ary;
- while ($access =~ m/^([a-z_]+|\||\&|\(|\)|\s+)/) {
+ my $access = $node->{access};
+
+ while ($access =~ m/^([a-z_\/]+|\||\&|\(|\)|\s+)/) {
my $token = $1;
substr($access, 0, length($1)) = "";
} elsif ($token eq ")") {
pop @stack;
if (!@stack) {
- $form->error("Error in menu.ini for entry ${key}: missing '('");
+ die "Error in menu.ini for entry $node->{id}: missing '('";
}
$cur_ary = $stack[-1];
push @{$cur_ary}, $token;
} else {
- push @{$cur_ary}, $auth->check_right($::myconfig{login}, $token, 1);
+ if ($token =~ m{^ client / (.*) }x) {
+ push @{$cur_ary}, $self->parse_instance_conf_string($1);
+ } else {
+ push @{$cur_ary}, $::auth->check_right($::myconfig{login}, $token, 1);
+ }
}
}
if ($access) {
- $form->error("Error in menu.ini for entry ${key}: unrecognized token at the start of '$access'\n");
+ die "Error in menu.ini for entry $node->{id}: unrecognized token at the start of '$access'\n";
}
if (1 < scalar @stack) {
- $main::form->error("Error in menu.ini for entry ${key}: Missing ')'\n");
+ die "Error in menu.ini for entry $node->{id}: Missing ')'\n";
}
return SL::Auth::evaluate_rights_ary($stack[0]);
}
-sub parse_instance_conf_string {
- my ($self, $setting) = @_;
- return $::instance_conf->data->{$setting};
-}
-
-sub set_access {
- my $self = shift;
-
- my $key;
-
- foreach $key (@{ $self->{ORDER} }) {
- my $entry = $self->{$key};
-
- $entry->{GRANTED} = $entry->{ACCESS} ? $self->parse_access_string($key, $entry->{ACCESS}) : 1;
- $entry->{GRANTED} &&= $self->parse_instance_conf_string($entry->{INSTANCE_CONF}) if $entry->{INSTANCE_CONF};
- $entry->{IS_MENU} = $entry->{submenu} || ($key !~ m/--/);
- $entry->{NUM_VISIBLE_CHILDREN} = 0;
-
- if ($key =~ m/--/) {
- my $parent = $key;
- substr($parent, rindex($parent, '--')) = '';
- $entry->{GRANTED} &&= $self->{$parent}->{GRANTED};
- }
-
- $entry->{VISIBLE} = $entry->{GRANTED};
- }
-
- foreach $key (reverse @{ $self->{ORDER} }) {
- my $entry = $self->{$key};
+sub href_for_node {
+ my ($self, $node) = @_;
- if ($entry->{IS_MENU}) {
- $entry->{VISIBLE} &&= $entry->{NUM_VISIBLE_CHILDREN} > 0;
- }
+ return undef if !$node->{href} && !$node->{module} && !$node->{params};
- next if (($key !~ m/--/) || !$entry->{VISIBLE});
+ my $href = $node->{href} || $node->{module} || 'controller.pl';
+ my @tokens;
- my $parent = $key;
- substr($parent, rindex($parent, '--')) = '';
- $self->{$parent}->{NUM_VISIBLE_CHILDREN}++;
+ while (my ($key, $value) = each %{ $node->{params} }) {
+ push @tokens, uri_encode($key, 1) . "=" . uri_encode($value, 1);
}
-# $self->dump_visible();
+ return join '?', $href, grep $_, join '&', @tokens;
+}
- $self->{ORDER} = [ grep { $self->{$_}->{VISIBLE} } @{ $self->{ORDER} } ];
+sub name_for_node {
+ $::locale->text($_[1]{name})
+}
- { no strict 'refs';
- # ToDO: fix this. nuke and pave algorithm without type checking screams for problems.
- map { delete @{$self->{$_}}{qw(GRANTED IS_MENU NUM_VISIBLE_CHILDREN VISIBLE ACCESS)} if ($_ ne 'ORDER') } keys %{ $self };
- }
+sub parse_instance_conf_string {
+ my ($self, $setting) = @_;
+ return $::instance_conf->data->{$setting};
}
-sub dump_visible {
- my $self = shift;
- foreach my $key (@{ $self->{ORDER} }) {
- my $entry = $self->{$key};
- $main::lxdebug->message(0, "$entry->{GRANTED} $entry->{VISIBLE} $entry->{NUM_VISIBLE_CHILDREN} $key");
+sub set_access {
+ my ($self) = @_;
+ # 1. evaluate access for all
+ # 2. if a menu has no visible children, its not visible either
+
+ for my $node (reverse $self->tree_walk("all")) {
+ $node->{visible} = $node->{access} ? $self->parse_access_string($node)
+ : !$node->{children} ? 1
+ : $node->{visible_children} ? 1
+ : 0;
+ if ($node->{visible} && $node->{parent}) {
+ $self->{by_id}{ $node->{parent} }{visible_children} = 1;
+ }
}
}
--- /dev/null
+---
+- id: users_clients_and_user_groups
+ name: Users, Clients and User Groups
+ order: 100
+- parent: users_clients_and_user_groups
+ id: users_clients_and_user_groups_list_users_clients_and_user_groups
+ name: List Users, Clients and User Groups
+ order: 100
+ params:
+ action: Admin/show
+- parent: users_clients_and_user_groups
+ id: users_clients_and_user_groups_add_user
+ name: Add User
+ order: 200
+ params:
+ action: Admin/new_user
+- parent: users_clients_and_user_groups
+ id: users_clients_and_user_groups_add_client
+ name: Add Client
+ order: 300
+ params:
+ action: Admin/new_client
+- parent: users_clients_and_user_groups
+ id: users_clients_and_user_groups_add_user_group
+ name: Add User Group
+ order: 400
+ params:
+ action: Admin/new_group
+- id: database_management
+ name: Database Management
+ order: 200
+- parent: database_management
+ id: database_management_create_dataset
+ name: Create Dataset
+ order: 100
+ params:
+ action: Admin/create_dataset_login
+- parent: database_management
+ id: database_management_delete_dataset
+ name: Delete Dataset
+ order: 200
+ params:
+ action: Admin/delete_dataset_login
+- id: printer_management
+ name: Printer Management
+ order: 300
+- parent: printer_management
+ id: printer_management_list_printers
+ name: List Printers
+ order: 100
+ params:
+ action: Admin/list_printers
+- parent: printer_management
+ id: printer_management_add_printer
+ name: Add Printer
+ order: 200
+ params:
+ action: Admin/new_printer
+- id: system
+ name: System
+ icon: system
+ order: 400
+- parent: system
+ id: system_lock_and_unlock_installation
+ name: Lock and unlock installation
+ order: 100
+ params:
+ action: Admin/show_lock
+- parent: system
+ id: system_documentation_in_german_
+ name: Documentation (in German)
+ order: 200
+ href: doc/kivitendo-Dokumentation.pdf
+ target: _blank
+- parent: system
+ id: system_kivitendo_website_external_
+ name: kivitendo website (external)
+ order: 300
+ href: http://www.kivitendo.de/
+ target: _blank
+- parent: system
+ id: system_to_user_login
+ name: To user login
+ order: 400
+ params:
+ action: LoginScreen/user_login
+- parent: system
+ id: system_logout
+ name: Logout
+ order: 500
+ params:
+ action: Admin/logout
--- /dev/null
+---
+- id: master_data
+ name: Master Data
+ icon: master_data
+ order: 100
+- parent: master_data
+ id: master_data_add_customer
+ name: Add Customer
+ icon: customer_add
+ order: 100
+ access: customer_vendor_edit
+ params:
+ action: CustomerVendor/add
+ db: customer
+- parent: master_data
+ id: master_data_add_vendor
+ name: Add Vendor
+ icon: vendor_add
+ order: 200
+ access: customer_vendor_edit
+ params:
+ action: CustomerVendor/add
+ db: vendor
+- parent: master_data
+ id: master_data_add_part
+ name: Add Part
+ icon: part_add
+ order: 300
+ access: part_service_assembly_edit
+ module: ic.pl
+ params:
+ action: add
+ item: part
+- parent: master_data
+ id: master_data_add_service
+ name: Add Service
+ icon: service_add
+ order: 400
+ access: part_service_assembly_edit
+ module: ic.pl
+ params:
+ action: add
+ item: service
+- parent: master_data
+ id: master_data_add_assembly
+ name: Add Assembly
+ icon: assembly_add
+ order: 500
+ access: part_service_assembly_edit
+ module: ic.pl
+ params:
+ action: add
+ item: assembly
+- parent: master_data
+ id: master_data_add_project
+ name: Add Project
+ icon: project_add
+ order: 600
+ access: project_edit
+ params:
+ action: Project/new
+- parent: master_data
+ id: master_data_add_requirement_spec_template
+ name: Add Requirement Spec Template
+ order: 700
+ access: requirement_spec_edit
+ params:
+ action: RequirementSpec/new
+ is_template: 1
+- parent: master_data
+ id: master_data_update_prices
+ name: Update Prices
+ icon: prices_update
+ order: 800
+ access: part_service_assembly_edit
+ module: ic.pl
+ params:
+ action: search_update_prices
+- parent: master_data
+ id: master_data_price_rules
+ name: Price Rules
+ order: 900
+ access: part_service_assembly_edit
+ params:
+ action: PriceRule/list
+ filter.obsolete: 0
+- parent: master_data
+ id: master_data_reports
+ name: Reports
+ icon: master_data_report
+ order: 1000
+ module: menu.pl
+ params:
+ action: acc_menu
+- parent: master_data_reports
+ id: master_data_reports_customers
+ name: Customers
+ icon: customer_report
+ order: 100
+ access: customer_vendor_edit
+ params:
+ action: CustomerVendor/search
+ db: customer
+- parent: master_data_reports
+ id: master_data_reports_vendors
+ name: Vendors
+ icon: vendor_report
+ order: 200
+ access: customer_vendor_edit
+ params:
+ action: CustomerVendor/search
+ db: vendor
+- parent: master_data_reports
+ id: master_data_reports_contacts
+ name: Contacts
+ order: 300
+ access: customer_vendor_edit
+ params:
+ action: CustomerVendor/search_contact
+ db: customer
+- parent: master_data_reports
+ id: master_data_reports_parts
+ name: Parts
+ icon: part_report
+ order: 400
+ access: part_service_assembly_details
+ module: ic.pl
+ params:
+ action: search
+ searchitems: part
+- parent: master_data_reports
+ id: master_data_reports_services
+ name: Services
+ icon: service_report
+ order: 500
+ access: part_service_assembly_details
+ module: ic.pl
+ params:
+ action: search
+ searchitems: service
+- parent: master_data_reports
+ id: master_data_reports_assemblies
+ name: Assemblies
+ icon: assembly_report
+ order: 600
+ access: part_service_assembly_details
+ module: ic.pl
+ params:
+ action: search
+ searchitems: assembly
+- parent: master_data_reports
+ id: master_data_reports_projects
+ name: Projects
+ icon: project_report
+ order: 700
+ access: project_edit
+ params:
+ action: Project/list
+ filter.active: active
+ filter.valid: valid
+- parent: master_data_reports
+ id: master_data_reports_requirement_spec_templates
+ name: Requirement Spec Templates
+ order: 800
+ access: requirement_spec_edit
+ params:
+ action: RequirementSpec/list
+ is_template: 1
+- id: ar
+ name: AR
+ icon: ar
+ order: 200
+- parent: ar
+ id: ar_add_requirement_spec
+ name: Add Requirement Spec
+ order: 100
+ access: requirement_spec_edit
+ params:
+ action: RequirementSpec/new
+- parent: ar
+ id: ar_add_quotation
+ name: Add Quotation
+ icon: quotation_add
+ order: 200
+ access: sales_quotation_edit
+ module: oe.pl
+ params:
+ action: add
+ type: sales_quotation
+- parent: ar
+ id: ar_add_sales_order
+ name: Add Sales Order
+ icon: sales_order_add
+ order: 300
+ access: sales_order_edit
+ module: oe.pl
+ params:
+ action: add
+ type: sales_order
+- parent: ar
+ id: ar_add_delivery_order
+ name: Add Delivery Order
+ icon: delivery_order_add
+ order: 400
+ access: sales_delivery_order_edit
+ module: do.pl
+ params:
+ action: add
+ type: sales_delivery_order
+- parent: ar
+ id: ar_add_sales_invoice
+ name: Add Sales Invoice
+ icon: sales_invoice_add
+ order: 500
+ access: invoice_edit
+ module: is.pl
+ params:
+ action: add
+ type: invoice
+- parent: ar
+ id: ar_add_credit_note
+ name: Add Credit Note
+ icon: credit_note_add
+ order: 600
+ access: invoice_edit
+ module: is.pl
+ params:
+ action: add
+ type: credit_note
+- parent: ar
+ id: ar_add_dunning
+ name: Add Dunning
+ icon: dunning_add
+ order: 700
+ access: dunning_edit
+ module: dn.pl
+ params:
+ action: add
+- parent: ar
+ id: ar_add_letter
+ name: Add Letter
+ order: 800
+ access: sales_letter_edit
+ module: letter.pl
+ params:
+ action: add
+- parent: ar
+ id: ar_reports
+ name: Reports
+ icon: ar_report
+ order: 900
+ module: menu.pl
+ params:
+ action: acc_menu
+- parent: ar_reports
+ id: ar_reports_requirement_specs
+ name: Requirement Specs
+ order: 100
+ access: requirement_spec_edit
+ params:
+ action: RequirementSpec/list
+- parent: ar_reports
+ id: ar_reports_quotations
+ name: Quotations
+ icon: report_quotations
+ order: 200
+ access: sales_quotation_edit
+ module: oe.pl
+ params:
+ action: search
+ type: sales_quotation
+- parent: ar_reports
+ id: ar_reports_sales_orders
+ name: Sales Orders
+ icon: report_sales_orders
+ order: 300
+ access: sales_order_edit
+ module: oe.pl
+ params:
+ action: search
+ type: sales_order
+- parent: ar_reports
+ id: ar_reports_delivery_orders
+ name: Delivery Orders
+ icon: delivery_order_report
+ order: 400
+ access: sales_delivery_order_edit
+ module: do.pl
+ params:
+ action: search
+ type: sales_delivery_order
+- parent: ar_reports
+ id: ar_reports_invoices_credit_notes_ar_transactions
+ name: Invoices, Credit Notes & AR Transactions
+ icon: invoices_report
+ order: 500
+ access: invoice_edit
+ module: ar.pl
+ params:
+ action: search
+ nextsub: ar_transactions
+- parent: ar_reports
+ id: ar_reports_sales_report
+ name: Sales Report
+ order: 600
+ access: invoice_edit
+ module: vk.pl
+ params:
+ action: search_invoice
+ nextsub: invoice_transactions
+- parent: ar_reports
+ id: ar_reports_dunnings
+ name: Dunnings
+ icon: dunnings_report
+ order: 700
+ access: dunning_edit
+ module: dn.pl
+ params:
+ action: search
+- parent: ar_reports
+ id: ar_reports_delivery_plan
+ name: Delivery Plan
+ order: 800
+ access: delivery_plan
+ params:
+ action: DeliveryPlan/list
+ vc: customer
+ mode: delivery_plan
+- parent: ar_reports
+ id: ar_reports_delivery_value_report
+ name: Delivery Value Report
+ order: 900
+ access: delivery_value_report
+ params:
+ action: DeliveryPlan/list
+ mode: delivery_value_report
+ vc: customer
+- parent: ar_reports
+ id: ar_reports_financial_controlling
+ name: Financial Controlling
+ order: 1000
+ access: sales_order_edit
+ params:
+ action: FinancialControllingReport/list
+- parent: ar_reports
+ id: ar_reports_letters
+ name: Letters
+ order: 1100
+ access: sales_letter_report
+ module: letter.pl
+ params:
+ action: search
+- id: ap
+ name: AP
+ icon: ap
+ order: 300
+- parent: ap
+ id: ap_add_rfq
+ name: Add RFQ
+ icon: rfq_add
+ order: 100
+ access: request_quotation_edit
+ module: oe.pl
+ params:
+ action: add
+ type: request_quotation
+- parent: ap
+ id: ap_add_purchase_order
+ name: Add Purchase Order
+ icon: purchase_order_add
+ order: 200
+ access: purchase_order_edit
+ module: oe.pl
+ params:
+ action: add
+ type: purchase_order
+- parent: ap
+ id: ap_add_delivery_note
+ name: Add Delivery Note
+ order: 300
+ access: client/allow_new_purchase_delivery_order & purchase_delivery_order_edit
+ module: do.pl
+ params:
+ action: add
+ type: purchase_delivery_order
+- parent: ap
+ id: ap_add_vendor_invoice
+ name: Add Vendor Invoice
+ order: 400
+ access: client/allow_new_purchase_invoice & vendor_invoice_edit
+ module: ir.pl
+ params:
+ action: add
+ type: invoice
+- parent: ap
+ id: ap_reports
+ name: Reports
+ icon: ap_report
+ order: 500
+ module: menu.pl
+ params:
+ action: acc_menu
+- parent: ap_reports
+ id: ap_reports_rfqs
+ name: RFQs
+ icon: rfq_report
+ order: 100
+ access: request_quotation_edit
+ module: oe.pl
+ params:
+ action: search
+ type: request_quotation
+- parent: ap_reports
+ id: ap_reports_purchase_orders
+ name: Purchase Orders
+ icon: purchase_order_report
+ order: 200
+ access: purchase_order_edit
+ module: oe.pl
+ params:
+ action: search
+ type: purchase_order
+- parent: ap_reports
+ id: ap_reports_delivery_orders
+ name: Delivery Orders
+ order: 300
+ access: purchase_delivery_order_edit
+ module: do.pl
+ params:
+ action: search
+ type: purchase_delivery_order
+- parent: ap_reports
+ id: ap_reports_vendor_invoices_ap_transactions
+ name: Vendor Invoices & AP Transactions
+ order: 400
+ access: vendor_invoice_edit
+ module: ap.pl
+ params:
+ action: search
+ nextsub: ap_transactions
+- parent: ap_reports
+ id: ap_reports_delivery_plan
+ name: Delivery Plan
+ order: 500
+ access: delivery_plan
+ params:
+ action: DeliveryPlan/list
+ mode: delivery_plan
+ vc: vendor
+- parent: ap_reports
+ id: ap_reports_delivery_value_report
+ name: Delivery Value Report
+ order: 600
+ access: delivery_value_report
+ params:
+ action: DeliveryPlan/list
+ vc: vendor
+ mode: delivery_value_report
+- id: warehouse
+ name: Warehouse
+ icon: warehouse
+ order: 400
+- parent: warehouse
+ id: warehouse_stock
+ name: Stock
+ order: 100
+ access: warehouse_management
+ params:
+ action: Inventory/stock_in
+- parent: warehouse
+ id: warehouse_produce_assembly
+ name: Produce Assembly
+ icon: assembly_produce
+ order: 200
+ access: warehouse_management
+ module: wh.pl
+ params:
+ action: transfer_warehouse_selection
+ trans_type: assembly
+- parent: warehouse
+ id: warehouse_transfer
+ name: Transfer
+ order: 300
+ access: warehouse_management
+ module: wh.pl
+ params:
+ action: transfer_warehouse_selection
+ trans_type: transfer
+- parent: warehouse
+ id: warehouse_removal
+ name: Removal
+ order: 400
+ access: warehouse_management
+ module: wh.pl
+ params:
+ action: transfer_warehouse_selection
+ trans_type: removal
+- parent: warehouse
+ id: warehouse_reports
+ name: Reports
+ order: 500
+ module: menu.pl
+ params:
+ action: acc_menu
+- parent: warehouse_reports
+ id: warehouse_reports_warehouse_content
+ name: Warehouse content
+ order: 100
+ access: warehouse_contents | warehouse_management
+ module: wh.pl
+ params:
+ action: report
+- parent: warehouse_reports
+ id: warehouse_reports_whjournal
+ name: WHJournal
+ order: 200
+ access: warehouse_management
+ module: wh.pl
+ params:
+ action: journal
+- id: general_ledger
+ name: General Ledger
+ icon: gl
+ order: 500
+- parent: general_ledger
+ id: general_ledger_add_transaction
+ name: Add Transaction
+ icon: transaction_add
+ order: 100
+ access: general_ledger
+ module: gl.pl
+ params:
+ action: add
+- parent: general_ledger
+ id: general_ledger_add_ar_transaction
+ name: Add AR Transaction
+ icon: ar_transaction_add
+ order: 200
+ access: general_ledger
+ module: ar.pl
+ params:
+ action: add
+- parent: general_ledger
+ id: general_ledger_add_ap_transaction
+ name: Add AP Transaction
+ icon: ap_transaction_add
+ order: 300
+ access: general_ledger
+ module: ap.pl
+ params:
+ action: add
+- parent: general_ledger
+ id: general_ledger_datev_export_assistent
+ name: DATEV - Export Assistent
+ icon: datev
+ order: 400
+ access: datev_export
+ module: datev.pl
+ params:
+ action: export
+- parent: general_ledger
+ id: general_ledger_reports
+ name: Reports
+ icon: gl_report
+ order: 500
+ module: menu.pl
+ params:
+ action: acc_menu
+- parent: general_ledger_reports
+ id: general_ledger_reports_ar_aging
+ name: AR Aging
+ icon: ar_aging
+ order: 100
+ access: general_ledger
+ module: rp.pl
+ params:
+ action: report
+ report: ar_aging
+- parent: general_ledger_reports
+ id: general_ledger_reports_ap_aging
+ name: AP Aging
+ icon: ap_aging
+ order: 200
+ access: general_ledger
+ module: rp.pl
+ params:
+ action: report
+ report: ap_aging
+- parent: general_ledger_reports
+ id: general_ledger_reports_journal
+ name: Journal
+ icon: journal
+ order: 300
+ access: general_ledger
+ module: gl.pl
+ params:
+ action: search
+- id: cash
+ name: Cash
+ icon: cash
+ order: 600
+- parent: cash
+ id: cash_receipt
+ name: Receipt
+ icon: receipt
+ order: 100
+ access: cash
+ module: cp.pl
+ params:
+ action: payment
+ vc: customer
+ type: receipt
+- parent: cash
+ id: cash_payment
+ name: Payment
+ icon: payment
+ order: 200
+ access: cash
+ module: cp.pl
+ params:
+ action: payment
+ vc: vendor
+ type: check
+- parent: cash
+ id: cash_bank_collection_via_sepa
+ name: Bank collection via SEPA
+ order: 300
+ access: cash
+ module: sepa.pl
+ params:
+ action: bank_transfer_add
+ vc: customer
+- parent: cash
+ id: cash_bank_transfer_via_sepa
+ name: Bank transfer via SEPA
+ order: 400
+ access: cash
+ module: sepa.pl
+ params:
+ action: bank_transfer_add
+ vc: vendor
+- parent: cash
+ id: cash_bank_import
+ name: Bank Import
+ order: 500
+ module: menu.pl
+ params:
+ action: acc_menu
+- parent: cash_bank_import
+ id: cash_bank_import_csv
+ name: CSV
+ order: 100
+ access: bank_transaction
+ params:
+ action: CsvImport/new
+ profile.type: bank_transactions
+- parent: cash_bank_import
+ id: cash_bank_import_mt940
+ name: MT940
+ order: 200
+ access: bank_transaction
+ params:
+ action: BankImport/upload_mt940
+- parent: cash
+ id: cash_bank_transactions_mt940
+ name: Bank transactions MT940
+ order: 600
+ access: bank_transaction
+ params:
+ action: BankTransaction/search
+- parent: cash
+ id: cash_reconciliation_with_bank
+ name: Reconciliation with bank
+ order: 700
+ access: bank_transaction
+ params:
+ action: Reconciliation/search
+ next_sub: Reconciliation/reconciliation
+- parent: cash
+ id: cash_reconciliation
+ name: Reconciliation
+ icon: reconcilliation
+ order: 800
+ access: cash
+ module: rc.pl
+ params:
+ action: reconciliation
+- parent: cash
+ id: cash_reports
+ name: Reports
+ icon: cash_report
+ order: 900
+ module: menu.pl
+ params:
+ action: acc_menu
+- parent: cash_reports
+ id: cash_reports_receipts
+ name: Receipts
+ icon: receipt_report
+ order: 100
+ access: cash
+ module: rp.pl
+ params:
+ action: report
+ report: receipts
+- parent: cash_reports
+ id: cash_reports_payments
+ name: Payments
+ icon: payment_report
+ order: 200
+ access: cash
+ module: rp.pl
+ params:
+ action: report
+ report: payments
+- parent: cash_reports
+ id: cash_reports_bank_collections_via_sepa
+ name: Bank collections via SEPA
+ order: 300
+ access: cash
+ module: sepa.pl
+ params:
+ action: bank_transfer_search
+ vc: customer
+- parent: cash_reports
+ id: cash_reports_bank_transfers_via_sepa
+ name: Bank transfers via SEPA
+ order: 400
+ access: cash
+ module: sepa.pl
+ params:
+ action: bank_transfer_search
+ vc: vendor
+- parent: cash_reports
+ id: cash_reports_bank_transactions
+ name: Bank transactions
+ order: 500
+ access: bank_transaction
+ params:
+ action: BankTransaction/list_all
+- id: reports
+ name: Reports
+ icon: report
+ order: 700
+- parent: reports
+ id: reports_chart_of_accounts
+ name: Chart of Accounts
+ icon: chart_of_accounts
+ order: 100
+ access: report
+ module: ca.pl
+ params:
+ action: chart_of_accounts
+- parent: reports
+ id: reports_trial_balance
+ name: Trial Balance
+ order: 200
+ access: report
+ module: rp.pl
+ params:
+ action: report
+ report: trial_balance
+- parent: reports
+ id: reports_income_statement
+ name: Income Statement
+ icon: income_statement
+ order: 300
+ access: report
+ module: rp.pl
+ params:
+ action: report
+ report: income_statement
+- parent: reports
+ id: reports_bwa
+ name: BWA
+ order: 400
+ access: report
+ module: rp.pl
+ params:
+ action: report
+ report: bwa
+- parent: reports
+ id: reports_balance_sheet
+ name: Balance Sheet
+ icon: balance_sheet
+ order: 500
+ access: report
+ module: rp.pl
+ params:
+ action: report
+ report: balance_sheet
+- parent: reports
+ id: reports_ustva
+ name: UStVa
+ icon: ustva
+ order: 600
+ access: advance_turnover_tax_return
+ module: ustva.pl
+ params:
+ action: report
+- parent: reports
+ id: reports_projecttransactions
+ name: Projecttransactions
+ order: 700
+ access: report
+ module: rp.pl
+ params:
+ action: report
+ report: projects
+- parent: reports
+ id: reports_financial_overview
+ name: Financial Overview
+ order: 800
+ access: report
+ params:
+ action: FinancialOverview/list
+- parent: reports
+ id: reports_liquidity_projection
+ name: Liquidity projection
+ order: 900
+ access: report
+ params:
+ action: LiquidityProjection/show
+- id: batch_printing
+ name: Batch Printing
+ icon: printing
+ order: 800
+ access: batch_printing
+- parent: batch_printing
+ id: batch_printing_sales_invoices
+ name: Sales Invoices
+ icon: sales_invoice_printing
+ order: 100
+ access: invoice_edit
+ module: bp.pl
+ params:
+ action: search
+ vc: customer
+ type: invoice
+- parent: batch_printing
+ id: batch_printing_sales_orders
+ name: Sales Orders
+ icon: sales_order_printing
+ order: 200
+ access: sales_order_edit
+ module: bp.pl
+ params:
+ action: search
+ type: sales_order
+ vc: customer
+- parent: batch_printing
+ id: batch_printing_quotations
+ name: Quotations
+ icon: quotation_printing
+ order: 300
+ access: sales_quotation_edit
+ module: bp.pl
+ params:
+ action: search
+ vc: customer
+ type: sales_quotation
+- parent: batch_printing
+ id: batch_printing_packing_lists
+ name: Packing Lists
+ icon: package_lists
+ order: 400
+ access: invoice_edit | sales_order_edit
+ module: bp.pl
+ params:
+ action: search
+ type: packing_list
+ vc: customer
+- parent: batch_printing
+ id: batch_printing_purchase_orders
+ name: Purchase Orders
+ icon: purchase_order_printing
+ order: 500
+ access: purchase_order_edit
+ module: bp.pl
+ params:
+ action: search
+ type: purchase_order
+ vc: vendor
+- parent: batch_printing
+ id: batch_printing_rfqs
+ name: RFQs
+ icon: rfq_printing
+ order: 600
+ access: request_quotation_edit
+ module: bp.pl
+ params:
+ action: search
+ vc: vendor
+ type: request_quotation
+- parent: batch_printing
+ id: batch_printing_checks
+ name: Checks
+ order: 700
+ access: cash
+ module: bp.pl
+ params:
+ action: search
+ type: check
+ vc: vendor
+- parent: batch_printing
+ id: batch_printing_receipts
+ name: Receipts
+ icon: receipt_printing
+ order: 800
+ access: cash
+ module: bp.pl
+ params:
+ action: search
+ vc: customer
+ type: receipt
+- id: productivity
+ name: Productivity
+ icon: productivity
+ order: 900
+ access: productivity
+- parent: productivity
+ id: productivity_show_todo_list
+ name: Show TODO list
+ order: 100
+ module: todo.pl
+ params:
+ action: show_todo_list
+- parent: productivity
+ id: productivity_add_follow_up
+ name: Add Follow-Up
+ order: 200
+ module: fu.pl
+ params:
+ action: add
+- parent: productivity
+ id: productivity_edit_access_rights
+ name: Edit Access Rights
+ order: 300
+ module: fu.pl
+ params:
+ action: edit_access_rights
+- parent: productivity
+ id: productivity_reports
+ name: Reports
+ order: 400
+ module: menu.pl
+ params:
+ action: acc_menu
+- parent: productivity_reports
+ id: productivity_reports_follow_ups
+ name: Follow-Ups
+ order: 100
+ module: fu.pl
+ params:
+ action: search
+- id: system
+ name: System
+ icon: system
+ order: 1000
+ access: config
+- parent: system
+ id: system_client_configuration
+ name: Client Configuration
+ order: 100
+ access: admin
+ params:
+ action: ClientConfig/edit
+- parent: system
+ id: system_ustva_einstellungen
+ name: UStVa Einstellungen
+ order: 200
+ module: ustva.pl
+ params:
+ action: config_step1
+- parent: system
+ id: system_edit_dunning
+ name: Edit Dunning
+ order: 300
+ module: dn.pl
+ params:
+ action: edit_config
+- parent: system
+ id: system_chart_of_accounts
+ name: Chart of Accounts
+ order: 400
+ module: menu.pl
+ params:
+ action: acc_menu
+- parent: system_chart_of_accounts
+ id: system_chart_of_accounts_add_account
+ name: Add Account
+ order: 100
+ module: am.pl
+ params:
+ action: add_account
+- parent: system_chart_of_accounts
+ id: system_chart_of_accounts_list_accounts
+ name: List Accounts
+ order: 200
+ module: am.pl
+ params:
+ action: list_account
+- parent: system
+ id: system_buchungsgruppen
+ name: Buchungsgruppen
+ order: 500
+ params:
+ action: Buchungsgruppen/list
+- parent: system
+ id: system_taxzones
+ name: Taxzones
+ order: 600
+ params:
+ action: Taxzones/list
+- parent: system
+ id: system_taxes
+ name: Taxes
+ order: 700
+ module: am.pl
+ params:
+ action: list_tax
+- parent: system
+ id: system_bank_accounts
+ name: Bank accounts
+ order: 800
+ params:
+ action: BankAccount/list
+- parent: system
+ id: system_groups
+ name: Groups
+ order: 900
+ module: pe.pl
+ params:
+ action: search
+ type: partsgroup
+- parent: system
+ id: system_pricegroups
+ name: Pricegroups
+ order: 1000
+ module: pe.pl
+ params:
+ action: search
+ type: pricegroup
+- parent: system
+ id: system_edit_units
+ name: Edit units
+ order: 1100
+ module: am.pl
+ params:
+ action: edit_units
+- parent: system
+ id: system_price_factors
+ name: Price Factors
+ order: 1200
+ module: am.pl
+ params:
+ action: list_price_factors
+- parent: system
+ id: system_departments
+ name: Departments
+ order: 1300
+ params:
+ action: Department/list
+- parent: system
+ id: system_types_of_business
+ name: Types of Business
+ order: 1400
+ params:
+ action: Business/list
+- parent: system
+ id: system_leads
+ name: Leads
+ order: 1500
+ module: am.pl
+ params:
+ action: list_lead
+- parent: system
+ id: system_project_types
+ name: Project Types
+ order: 1600
+ params:
+ action: ProjectType/list
+- parent: system
+ id: system_project_status
+ name: Project Status
+ order: 1700
+ params:
+ action: ProjectStatus/list
+- parent: system
+ id: system_requirement_specs
+ name: Requirement specs
+ order: 1800
+ module: menu.pl
+ target: acc_menu
+ params:
+ action: acc_menu
+- parent: system_requirement_specs
+ id: system_requirement_specs_pre_defined_texts
+ name: Pre-defined Texts
+ order: 100
+ params:
+ action: RequirementSpecPredefinedText/list
+- parent: system_requirement_specs
+ id: system_requirement_specs_requirement_spec_types
+ name: Requirement Spec Types
+ order: 200
+ params:
+ action: RequirementSpecType/list
+- parent: system_requirement_specs
+ id: system_requirement_specs_requirement_spec_statuses
+ name: Requirement Spec Statuses
+ order: 300
+ params:
+ action: RequirementSpecStatus/list
+- parent: system_requirement_specs
+ id: system_requirement_specs_complexities
+ name: Complexities
+ order: 400
+ params:
+ action: RequirementSpecComplexity/list
+- parent: system_requirement_specs
+ id: system_requirement_specs_risks
+ name: Risks
+ order: 500
+ params:
+ action: RequirementSpecRisk/list
+- parent: system_requirement_specs
+ id: system_requirement_specs_acceptance_statuses
+ name: Acceptance Statuses
+ order: 600
+ params:
+ action: RequirementSpecAcceptanceStatus/list
+- parent: system
+ id: system_languages_and_translations
+ name: Languages and translations
+ order: 1900
+ module: menu.pl
+ params:
+ action: acc_menu
+- parent: system_languages_and_translations
+ id: system_languages_and_translations_add_language
+ name: Add Language
+ order: 100
+ module: am.pl
+ params:
+ action: add_language
+- parent: system_languages_and_translations
+ id: system_languages_and_translations_list_languages
+ name: List Languages
+ order: 200
+ module: am.pl
+ params:
+ action: list_language
+- parent: system_languages_and_translations
+ id: system_languages_and_translations_greetings
+ name: Greetings
+ order: 300
+ module: generictranslations.pl
+ params:
+ action: edit_greetings
+- parent: system_languages_and_translations
+ id: system_languages_and_translations_sepa_strings
+ name: SEPA strings
+ order: 400
+ module: generictranslations.pl
+ params:
+ action: edit_sepa_strings
+- parent: system
+ id: system_payment_terms
+ name: Payment Terms
+ order: 2000
+ params:
+ action: PaymentTerm/list
+- parent: system
+ id: system_delivery_terms
+ name: Delivery Terms
+ order: 2100
+ params:
+ action: DeliveryTerm/list
+- parent: system
+ id: system_manage_custom_variables
+ name: Manage Custom Variables
+ order: 2200
+ params:
+ action: CustomVariableConfig/list
+- parent: system
+ id: system_warehouses
+ name: Warehouses
+ order: 2300
+ module: am.pl
+ params:
+ action: list_warehouses
+- parent: system
+ id: system_import_csv
+ name: Import CSV
+ order: 2400
+ module: menu.pl
+ params:
+ action: acc_menu
+- parent: system_import_csv
+ id: system_import_csv_customers_and_vendors
+ name: Customers and vendors
+ order: 100
+ params:
+ action: CsvImport/new
+ profile.type: customers_vendors
+- parent: system_import_csv
+ id: system_import_csv_contacts
+ name: Contacts
+ order: 200
+ params:
+ action: CsvImport/new
+ profile.type: contacts
+- parent: system_import_csv
+ id: system_import_csv_shipto
+ name: Shipto
+ order: 300
+ params:
+ action: CsvImport/new
+ profile.type: addresses
+- parent: system_import_csv
+ id: system_import_csv_parts
+ name: Parts
+ order: 400
+ params:
+ action: CsvImport/new
+ profile.type: parts
+- parent: system_import_csv
+ id: system_import_csv_inventories
+ name: Inventories
+ order: 500
+ params:
+ action: CsvImport/new
+ profile.type: inventories
+- parent: system_import_csv
+ id: system_import_csv_projects
+ name: Projects
+ order: 600
+ params:
+ action: CsvImport/new
+ profile.type: projects
+- parent: system_import_csv
+ id: system_import_csv_orders
+ name: Orders
+ order: 700
+ params:
+ action: CsvImport/new
+ profile.type: orders
+- parent: system
+ id: system_templates
+ name: Templates
+ order: 2500
+ access: admin
+ module: menu.pl
+ params:
+ action: acc_menu
+- parent: system_templates
+ id: system_templates_html_templates
+ name: HTML Templates
+ order: 100
+ module: amtemplates.pl
+ params:
+ action: display_template_form
+ type: templates
+ format: html
+- parent: system_templates
+ id: system_templates_latex_templates
+ name: LaTeX Templates
+ order: 200
+ module: amtemplates.pl
+ params:
+ action: display_template_form
+ format: tex
+ type: templates
+- parent: system_templates
+ id: system_templates_stylesheet
+ name: Stylesheet
+ order: 300
+ module: amtemplates.pl
+ params:
+ action: display_template_form
+ type: stylesheet
+- parent: system
+ id: system_general_ledger_corrections
+ name: General Ledger Corrections
+ order: 2600
+ module: acctranscorrections.pl
+ params:
+ action: analyze_filter
+- parent: system
+ id: system_background_jobs_and_task_server
+ name: Background jobs and task server
+ order: 2700
+ access: admin
+ module: menu.pl
+ params:
+ action: acc_menu
+- parent: system_background_jobs_and_task_server
+ id: system_background_jobs_and_task_server_list_current_background_jobs
+ name: List current background jobs
+ order: 100
+ params:
+ action: BackgroundJob/list
+- parent: system_background_jobs_and_task_server
+ id: system_background_jobs_and_task_server_background_job_history
+ name: Background job history
+ order: 200
+ params:
+ action: BackgroundJobHistory/list
+- parent: system_background_jobs_and_task_server
+ id: system_background_jobs_and_task_server_task_server_control
+ name: Task server control
+ order: 300
+ params:
+ action: TaskServer/show
+- parent: system
+ id: system_audit_control
+ name: Audit Control
+ order: 2800
+ module: am.pl
+ params:
+ action: audit_control
+- parent: system
+ id: system_history_search_engine
+ name: History Search Engine
+ order: 2900
+ module: am.pl
+ params:
+ action: show_history_search
+- parent: system
+ id: system_employees
+ name: Employees
+ order: 3000
+ access: admin
+ params:
+ action: Employee/list
+- id: program
+ name: Program
+ icon: program
+ order: 1100
+- parent: program
+ id: program_user_preferences
+ name: User Preferences
+ order: 100
+ module: am.pl
+ params:
+ action: config
+- parent: program
+ id: program_internal_phone_list
+ name: Internal Phone List
+ order: 200
+ params:
+ action: CTI/list_internal_extensions
+- parent: program
+ id: program_version
+ name: Version
+ icon: version
+ order: 300
+ module: login.pl
+ params:
+ action: company_logo
+ no_todo_list: 1
+- parent: program
+ id: program_administration_area
+ name: Administration area
+ order: 400
+ access: display_admin_link
+ params:
+ action: Admin/login
+- parent: program
+ id: program_documentation_in_german_
+ name: Documentation (in German)
+ order: 500
+ href: doc/kivitendo-Dokumentation.pdf
+ target: _blank
+- parent: program
+ id: program_kivitendo_website_external_
+ name: kivitendo website (external)
+ order: 600
+ href: http://www.kivitendo.de/
+ target: _blank
+- parent: program
+ id: program_logout
+ name: Logout
+ icon: logout
+ order: 700
+ params:
+ action: LoginScreen/logout
--- /dev/null
+---
+- id: crm
+ name: CRM
+ icon: crm
+ order: 50
+- parent: crm
+ id: crm_search
+ name: Search
+ icon: search
+ order: 100
+ access: crm_search
+ module: crm/getData.php
+- parent: crm
+ id: crm_add
+ name: Add
+ order: 200
+- parent: crm_add
+ id: crm_add_customer
+ name: Customer
+ icon: customer
+ order: 100
+ access: crm_new
+ module: crm/firmen3.php
+ params:
+ Q: C
+- parent: crm_add
+ id: crm_add_vendor
+ name: Vendor
+ icon: vendor
+ order: 200
+ access: crm_new
+ module: crm/firmen3.php
+ params:
+ Q: V
+- parent: crm_add
+ id: crm_add_person
+ name: Person
+ icon: contact
+ order: 300
+ access: crm_new
+ module: crm/personen3.php
+- parent: crm
+ id: crm_appointments
+ name: Appointments
+ icon: appointment
+ order: 300
+ access: crm_termin
+ module: crm/termin.php
+- parent: crm
+ id: crm_opportunity
+ name: Opportunity
+ icon: opportunity
+ order: 400
+ access: crm_opportunity
+ module: crm/opportunity.php
+- parent: crm
+ id: crm_follow_up
+ name: Follow-Up
+ icon: follow_up
+ order: 500
+ access: crm_follow
+ module: crm/wvl1.php
+- parent: crm
+ id: crm_e_mail
+ name: E-mail
+ icon: email
+ order: 600
+ access: crm_email
+ module: crm/mail.php
+- parent: crm
+ id: crm_knowledge
+ name: Knowledge
+ icon: knowledge
+ order: 700
+ access: crm_knowhow
+ module: crm/wissen.php
+- parent: crm
+ id: crm_memo
+ name: Memo
+ icon: memo
+ order: 800
+ access: crm_notices
+ module: crm/postit.php
+- parent: crm
+ id: crm_documents
+ name: Documents
+ order: 900
+ access: crm_other
+ module: crm/dokument.php
+- parent: crm
+ id: crm_time_tracking
+ name: Time Tracking
+ order: 1000
+ access: crm_service
+ module: crm/timetrack.php
+- parent: crm
+ id: crm_other
+ name: Other
+ order: 1100
+- parent: crm_other
+ id: crm_other_etikett
+ name: Etikett
+ order: 100
+ access: crm_other
+ module: crm/prtetikett.php
+ target: _blank
+- parent: crm_other
+ id: crm_other_dhl
+ name: DHL
+ order: 200
+ access: crm_other
+ module: crm/dhl.php
+- parent: crm_other
+ id: crm_other_ebayimporter
+ name: eBayImporter
+ order: 300
+ access: crm_other
+ module: crm/ebayImporter.php
+- parent: crm_other
+ id: crm_other_catalog
+ name: Catalog
+ order: 400
+ access: crm_other
+ module: crm/katalog.php
+- parent: crm_other
+ id: crm_other_warehouse_list
+ name: Warehouse list
+ order: 500
+ access: crm_other
+ module: crm/inventur.php
+- parent: crm_other
+ id: crm_other_warehouse_correction
+ name: Warehouse correction
+ order: 600
+ access: crm_admin
+ module: crm/inventurlager.php
+- parent: crm_other
+ id: crm_other_partsedit
+ name: Partsedit
+ order: 700
+ access: crm_other
+ module: crm/partsedit.php
+- parent: crm_other
+ id: crm_other_packliste
+ name: Packliste
+ order: 800
+ access: crm_other
+ module: crm/packliste.php
+- parent: crm_other
+ id: crm_other_eur
+ name: EuR
+ order: 900
+ access: crm_other
+ module: crm/eur.php
+- parent: crm_other
+ id: crm_other_zm
+ name: ZM
+ order: 1000
+ access: crm_other
+ module: crm/ustva_zm.php
+- parent: crm
+ id: crm_service
+ name: Service
+ icon: service
+ order: 1200
+- parent: crm_service
+ id: crm_service_service_contract
+ name: Service Contract
+ order: 100
+ access: crm_service
+ module: crm/vertrag1.php
+- parent: crm_service
+ id: crm_service_add_service_contract
+ name: Add Service Contract
+ order: 200
+ access: crm_service
+ module: crm/vertrag3.php
+- parent: crm_service
+ id: crm_service_machine
+ name: Machine
+ order: 300
+ access: crm_service
+ module: crm/maschine1.php
+- parent: crm_service
+ id: crm_service_add_machine
+ name: Add Machine
+ order: 400
+ access: crm_service
+ module: crm/maschine3.php
+- parent: crm
+ id: crm_admin
+ name: Admin
+ icon: admin
+ order: 1300
+- parent: crm_admin
+ id: crm_admin_document_template
+ name: Document Template
+ icon: document_template
+ order: 100
+ access: crm_admin
+ module: crm/dokument1.php
+- parent: crm_admin
+ id: crm_admin_label
+ name: Label
+ icon: label
+ order: 200
+ access: crm_admin
+ module: crm/aufkleber_def.php
+- parent: crm_admin
+ id: crm_admin_appointment_category
+ name: Appointment Category
+ order: 300
+ access: crm_admin
+ module: crm/tcatedit.php
+- parent: crm_admin
+ id: crm_admin_message
+ name: Message
+ icon: message
+ order: 400
+ access: crm_admin
+ module: crm/user3.php
+- parent: crm_admin
+ id: crm_admin_client
+ name: Client
+ order: 500
+ access: crm_adminstatus
+ module: crm/mandant.php
+- parent: crm_admin
+ id: crm_admin_user_groups
+ name: User Groups
+ icon: user_group
+ order: 600
+ access: crm_admin
+ module: crm/user2.php
+- parent: crm_admin
+ id: crm_admin_user
+ name: User
+ icon: user
+ order: 700
+ access: crm_adminuser
+ module: crm/user1.php
+- parent: crm_admin
+ id: crm_admin_dhl
+ name: DHL
+ order: 800
+ access: crm_adminuser
+ module: crm/dhladm.php
+- parent: crm_admin
+ id: crm_admin_status
+ name: Status
+ icon: status
+ order: 900
+ access: crm_adminstatus
+ module: crm/status.php
use List::MoreUtils qw(apply);
use List::Util qw(first);
use Pod::Usage;
+use YAML ();
+use YAML::Loader (); # YAML tries to load Y:L at runtime, but can't find it after we chdir'ed
$OUTPUT_AUTOFLUSH = 1;
my $locales_dir = ".";
my $bindir = "$basedir/bin/mozilla";
my @progdirs = ( "$basedir/SL" );
-my @menufiles = <${basedir}/menus/*.ini>;
+my @menufiles = <"${basedir}/menus/*/*">;
my @javascript_dirs = ($basedir .'/js', $basedir .'/templates/webpages');
my $javascript_output_dir = $basedir .'/js';
my $submitsearch = qr/type\s*=\s*[\"\']?submit/i;
# put customized files into @customfiles
my %dir_h;
-if ($opt_n) {
- @customfiles = ();
-} else {
- tie %dir_h, 'IO::Dir', $basedir;
- push @menufiles, map { "$basedir/$_" } grep { /.*_menu.ini$/ } keys %dir_h;
-}
-
my @dbplfiles;
foreach my $sub_dir ("Pg-upgrade2", "Pg-upgrade2-auth") {
my $dir = "$basedir/sql/$sub_dir";
sub scanmenu {
my $file = shift;
- my $fh = new FileHandle;
- open $fh, '<:encoding(utf8)', $file or die "$! : $file";
-
- my @a = grep m/^\[/, <$fh>;
- close($fh);
+ print STDERR "trying to load file $file\n";
+ my $menu = YAML::LoadFile($file);
- # strip []
- grep { s/(\[|\])//g } @a;
-
- foreach my $item (@a) {
- my @b = split /--/, $item;
- foreach my $string (@b) {
- chomp $string;
- $locale{$string} = 1;
- $alllocales{$string} = 1;
- }
+ for my $node (@$menu) {
+ $locale{$node->{name}} = 1;
+ $alllocales{$node->{name}} = 1;
}
-
}
sub unescape_template_string {
=head1 DESCRIPTION
-This script collects strings from Perl files, the menu.ini file and
+This script collects strings from Perl files, the menu files and
HTML templates and puts them into the file "all" for translation.
=cut
}
open my $out_file, '>:utf8', $new_file or die $!;
- print $out_file YAML::Dump(\@menu_items);
+ print $out_file yaml_dump(\@menu_items);
+}
+
+sub yaml_dump {
+ my ($ary_ref) = @_;
+ # YAML dumps keys lexically sorted, which isn't what we want.
+ # we want this order:
+ my @order = qw(
+ parent
+ id
+ name
+ icon
+ order
+ access
+ href
+ module
+ target
+ params
+ );
+
+ # ...oh and we want action in params first
+ #
+ # why this? because:
+ # 1. parent is what is used to anchor. one could argue that id should be
+ # first, but parent is easier for understanding structure.
+ # 2. after parent the logical structure is
+ # 1. id
+ # 2. stuff related to vidual presentation (name/icon)
+ # 3. stuff needed for logical presentaion (order/access)
+ # 4. stuff related to the action after clicking it
+ # 3. without parent and href (the second is pretty rare) the keys are nicely
+ # ascending in length, which is very easy to parse visually.
+
+ my $yaml = "---\n";
+ for my $node (@$ary_ref) {
+ my $first = 0;
+ for my $key (@order) {
+ next unless exists $node->{$key};
+ $yaml .= ($first++ ? ' ' : '- ') . $key . ": ";
+ if (!ref $node->{$key}) {
+ $yaml .= $node->{$key} . "\n";
+ } else {
+ $yaml .= "\n";
+ for ('action', grep !/^action$/, keys %{ $node->{$key} }) {
+ next unless exists $node->{$key}{$_};
+ $yaml .= " $_: $node->{$key}{$_}\n";
+ }
+ }
+
+ }
+ }
+
+ $yaml;
}
while (my ($in, $out) = each(%menu_files)) {
[%- USE T8 %]
-[% USE HTML %][%- USE LxERP -%]
+[%- USE L %]
+[%- USE HTML %]
+[%- USE LxERP -%]
<div id="main_menu_div"></div>
-
[%- SET main_id = '100' %]
<ul id="main_menu_model" style='display:none'>
- [%- FOREACH mainitem = menu_items %]
+ [%- FOREACH node = menu.tree %]
+ [% NEXT UNLESS node.visible %]
[%- SET main_id = main_id + 1 %]
- <li id="[% main_id %]"[% IF mainitem.image %] itemIcon="[% mainitem.image %]"[% END %]>
- <a href="[% IF mainitem.href %][% mainitem.href %][% ELSE %]#[% END %]"[% IF mainitem.target && (mainitem.target != 'main_window') %] target="[% HTML.escape(mainitem.target) %]"[% END %]>
- [%- HTML.escape(mainitem.title) %]
- </a>
- [%- IF mainitem.subitems %]
- <ul[%- IF force_ul_width %] width="[% mainitem.max_width * 10 %]"[% END %]>
+ <li id="[% main_id %]"[% IF icon_path(node.icon) %] itemIcon="[% icon_path(node.icon) %]"[% END %]>
+ [% L.link(menu.href_for_node(node) || '#', menu.name_for_node(node), target=node.target) %]
+ [%- IF node.children %]
+ <ul width="[% max_width(node) %]">
[%- SET sub1_id = main_id * 100 %]
- [%- FOREACH sub1item = mainitem.subitems %]
+ [%- FOREACH sub1node = node.children %]
+ [% NEXT UNLESS sub1node.visible %]
[%- SET sub1_id = sub1_id + 1 %]
- <li id="[% sub1_id %]"[% IF sub1item.image %] itemIcon="[% sub1item.image %]"[% END %]>
- <a href="[% IF sub1item.href %][% sub1item.href %][% ELSE %]#[% END %]"[% IF sub1item.target && (sub1item.target != 'main_window') %] target="[% HTML.escape(sub1item.target) %]"[% END %]>
- [%- HTML.escape(sub1item.title) %]
- </a>
- [%- IF sub1item.subitems %]
- <ul[%- IF force_ul_width %] width="[% sub1item.max_width * 10 %]"[% END %]>
+ <li id="[% sub1_id %]"[% IF icon_path(sub1node.icon) %] itemIcon="[% icon_path(sub1node.icon) %]"[% END %]>
+ [% L.link(menu.href_for_node(sub1node) || '#', menu.name_for_node(sub1node), target=sub1node.target) %]
+ [%- IF sub1item.children %]
+ <ul width="[% max_width(sub1node) %]">
[%- SET sub2_id = sub1_id * 100 %]
- [%- FOREACH sub2item = sub1item.subitems %]
+ [%- FOREACH sub2node = sub1node.children %]
+ [% NEXT UNLESS sub2node.visible %]
[%- SET sub2_id = sub2_id + 1 %]
- <li id="[% sub2_id %]"[% IF sub2item.image %] itemIcon="[% sub2item.image %]"[% END %]>
- <a href="[% IF sub2item.href %][% sub2item.href %][% ELSE %]#[% END %]"[% IF sub2item.target && (sub2item.target != 'main_window') %] target="[% HTML.escape(sub2item.target) %]"[% END %]>
- [%- HTML.escape(sub2item.title) %]
- </a>
+ <li id="[% sub2_id %]"[% IF icon_path(sub2item.icon) %] itemIcon="[% icon_path(sub2item.icon) %]"[% END %]>
+ [% L.link(menu.href_for_node(sub2node) || '#', menu.name_for_node(sub2node), target=sub2node.target) %]
</li>
[%- END %]
</ul>
</li>
[%- END %]
</ul>
-
- <script type="text/javascript">
- <!--
-
-$(function(){
- DHTMLSuite.createStandardObjects();
-
- DHTMLSuite.configObj.setImagePath('image/dhtmlsuite/');
-
- var menu_model = new DHTMLSuite.menuModel();
- menu_model.addItemsFromMarkup('main_menu_model');
- menu_model.init();
-
- var menu_bar = new DHTMLSuite.menuBar();
- menu_bar.addMenuItems(menu_model);
- menu_bar.setTarget('main_menu_div');
- menu_bar.init();
-});
-
-
-
-function open_url(url, target) {
-
-}
-
- -->
- </script>
- <div id="menuv3">[% menu %]</div>
- <div style="clear: both;"></div>
+[%- USE L %]
+[%- USE HTML %]
+<div id="menuv3">[% FOREACH node = menu.tree %][% NEXT UNLESS node.visible %]<ul><li>[% PROCESS submenu top=1 %]</li></ul>[% END %]</div>
+<div style="clear: both;"></div>
+[%- %]
+[%- BLOCK submenu %]
+ [%- IF top %]<h2>[% menu.name_for_node(node) | html %]</h2>
+ [%- ELSIF node.children %]<div class="x">[% menu.name_for_node(node) | html %]</div>
+ [%- ELSE %][% L.link(menu.href_for_node(node), menu.name_for_node(node), title=menu.name_for_node(node), target=node.target) %]
+ [%- END %]
+ [%- IF node.children %]<ul>[%- FOREACH node = node.children %][% NEXT UNLESS node.visible %]<li>[% PROCESS submenu top=0 %]</li>[% END %]</ul>[%- END %]
+[%- END %]