From: Moritz Bunkus
Date: Fri, 7 Jun 2013 09:14:42 +0000 (+0200)
Subject: Admin: Anlegen, Bearbeiten und Löschen von Usern im Admin-Controller
X-Git-Tag: release-3.1.0beta1~331^2~49
X-Git-Url: http://wagnertech.de/git?a=commitdiff_plain;h=288111da982510d86d73db5cef54c52e07a84f5a;p=kivitendo-erp.git
Admin: Anlegen, Bearbeiten und Löschen von Usern im Admin-Controller
Dazu auch "ON DELETE CASCADE" für alle foreign keys in auth.*
Fixt #2279, #2280.
---
diff --git a/SL/Controller/Admin.pm b/SL/Controller/Admin.pm
index 04d9c759b..d6e753a28 100644
--- a/SL/Controller/Admin.pm
+++ b/SL/Controller/Admin.pm
@@ -10,10 +10,11 @@ use SL::DB::AuthUser;
use SL::DB::AuthGroup;
use SL::Helper::Flash;
use SL::Locale::String qw(t8);
+use SL::User;
use Rose::Object::MakeMethods::Generic
(
- 'scalar --get_set_init' => [ qw(client user nologin_file_name db_cfg) ],
+ 'scalar --get_set_init' => [ qw(client user nologin_file_name db_cfg all_dateformats all_numberformats all_countrycodes all_stylesheets all_menustyles all_clients all_groups) ],
);
__PACKAGE__->run_before(\&setup_layout);
@@ -25,7 +26,7 @@ sub keep_auth_vars {
}
#
-# actions
+# actions: login, logout
#
sub action_login {
@@ -35,7 +36,7 @@ sub action_login {
return if !$self->authenticate_root;
return if !$self->check_auth_db_and_tables;
return if $self->apply_dbupgrade_scripts;
- $self->redirect_to(action => 'list_clients_and_users');
+ $self->redirect_to(action => 'show');
}
sub action_logout {
@@ -44,11 +45,15 @@ sub action_logout {
$self->redirect_to(action => 'login');
}
+#
+# actions: creating the authentication database & tables, applying database ugprades
+#
+
sub action_apply_dbupgrade_scripts {
my ($self) = @_;
return if $self->apply_dbupgrade_scripts;
- $self->action_list_clients_and_users;
+ $self->action_show;
}
sub action_create_auth_db {
@@ -81,23 +86,94 @@ sub action_create_auth_tables {
}
}
-sub action_list_clients_and_users {
+#
+# actions: users
+#
+
+sub action_show {
my ($self) = @_;
$self->render(
- "admin/list_users",
+ "admin/show",
CLIENTS => SL::DB::Manager::AuthClient->get_all_sorted,
USERS => SL::DB::Manager::AuthUser->get_all_sorted,
LOCKED => (-e $self->nologin_file_name),
- title => "kivitendo " . $::locale->text('Administration'),
+ title => "kivitendo " . t8('Administration'),
);
}
+sub action_new_user {
+ my ($self) = @_;
+
+ $self->user(SL::DB::AuthUser->new(
+ config_values => {
+ vclimit => 200,
+ countrycode => "de",
+ numberformat => "1.000,00",
+ dateformat => "dd.mm.yy",
+ stylesheet => "kivitendo.css",
+ menustyle => "neu",
+ },
+ ));
+
+ $self->edit_user_form(title => t8('Create a new user'));
+}
+
+sub action_edit_user {
+ my ($self) = @_;
+ $self->edit_user_form(title => t8('Edit User'));
+}
+
+sub action_save_user {
+ my ($self) = @_;
+ my $params = delete($::form->{user}) || { };
+ my $props = delete($params->{config_values}) || { };
+ my $is_new = !$params->{id};
+
+ $self->user($is_new ? SL::DB::AuthUser->new : SL::DB::AuthUser->new(id => $params->{id})->load)
+ ->assign_attributes(%{ $params })
+ ->config_values({ %{ $self->user->config_values }, %{ $props } });
+
+ my @errors = $self->user->validate;
+
+ if (@errors) {
+ flash('error', @errors);
+ $self->edit_user_form(title => $is_new ? t8('Create a new user') : t8('Edit User'));
+ return;
+ }
+
+ $self->user->save;
+
+ if ($::auth->can_change_password && $::form->{new_password}) {
+ $::auth->change_password($self->user->login, $::form->{new_password});
+ }
+
+ flash_later('info', $is_new ? t8('The user has been created.') : t8('The user has been saved.'));
+ $self->redirect_to(action => 'show');
+}
+
+sub action_delete_user {
+ my ($self) = @_;
+
+ if (!$self->user->delete) {
+ flash('error', t8('The user could not be deleted.'));
+ $self->edit_user_form(title => t8('Edit User'));
+ return;
+ }
+
+ flash_later('info', t8('The user has been deleted.'));
+ $self->redirect_to(action => 'show');
+}
+
+#
+# actions: locking, unlocking
+#
+
sub action_unlock_system {
my ($self) = @_;
unlink $self->nologin_file_name;
flash_later('info', t8('Lockfile removed!'));
- $self->redirect_to(action => 'list_clients_and_users');
+ $self->redirect_to(action => 'show');
}
sub action_lock_system {
@@ -110,7 +186,7 @@ sub action_lock_system {
} else {
$fh->close;
flash_later('info', t8('Lockfile created!'));
- $self->redirect_to(action => 'list_clients_and_users');
+ $self->redirect_to(action => 'show');
}
}
@@ -118,10 +194,27 @@ sub action_lock_system {
# initializers
#
-sub init_db_cfg { $::lx_office_conf{'authentication/database'} }
-sub init_nologin_file_name { $::lx_office_conf{paths}->{userspath} . '/nologin'; }
-sub init_client { SL::DB::AuthClient->new(id => $::form->{client_id})->load; }
-sub init_user { SL::DB::AuthUser ->new(id => $::form->{user_id} )->load; }
+sub init_db_cfg { $::lx_office_conf{'authentication/database'} }
+sub init_nologin_file_name { $::lx_office_conf{paths}->{userspath} . '/nologin'; }
+sub init_client { SL::DB::AuthClient->new(id => ($::form->{id} || ($::form->{client} || {})->{id}))->load }
+sub init_user { SL::DB::AuthUser ->new(id => ($::form->{id} || ($::form->{user} || {})->{id}))->load }
+sub init_all_clients { SL::DB::Manager::AuthClient->get_all_sorted }
+sub init_all_groups { SL::DB::Manager::AuthGroup->get_all_sorted }
+sub init_all_dateformats { [ qw(mm/dd/yy dd/mm/yy dd.mm.yy yyyy-mm-dd) ] }
+sub init_all_numberformats { [ qw(1,000.00 1000.00 1.000,00 1000,00) ] }
+sub init_all_stylesheets { [ qw(lx-office-erp.css Mobile.css kivitendo.css) ] }
+sub init_all_menustyles {
+ return [
+ { id => 'old', title => $::locale->text('Old (on the side)') },
+ { id => 'v3', title => $::locale->text('Top (CSS)') },
+ { id => 'neu', title => $::locale->text('Top (Javascript)') },
+ ];
+}
+
+sub init_all_countrycodes {
+ my %cc = User->country_codes;
+ return [ map { id => $_, title => $cc{$_} }, sort { $cc{$a} cmp $cc{$b} } keys %cc ];
+}
#
# filters
@@ -136,7 +229,7 @@ sub setup_layout {
}
#
-# helpers
+# displaying forms
#
sub login_form {
@@ -145,6 +238,17 @@ sub login_form {
$self->render('admin/adminlogin', title => t8('kivitendo v#1 administration', $::form->{version}), %params);
}
+sub edit_user_form {
+ my ($self, %params) = @_;
+
+ $::request->layout->use_javascript("${_}.js") for qw(jquery.selectboxes jquery.multiselect2side);
+ $self->render('admin/edit_user', %params);
+}
+
+#
+# helpers
+#
+
sub check_auth_db_and_tables {
my ($self) = @_;
diff --git a/SL/DB/AuthUser.pm b/SL/DB/AuthUser.pm
index bd2670bc6..0b1942118 100644
--- a/SL/DB/AuthUser.pm
+++ b/SL/DB/AuthUser.pm
@@ -36,9 +36,8 @@ sub validate {
my ($self) = @_;
my @errors;
- push @errors, $::locale->text('The login is missing.') if !$self->login;
- push @errors, $::locale->text('The login is not unique.') if !SL::DB::Helper::Util::is_unique($self, 'login');
- push @errors, "chunky bacon";
+ push @errors, $::locale->text('The login is missing.') if !$self->login;
+ push @errors, $::locale->text('The login is not unique.') if !SL::DB::Helper::Util::is_unique($self, 'login');
return @errors;
}
@@ -46,7 +45,7 @@ sub validate {
sub get_config_value {
my ($self, $key) = @_;
- my $cfg = first { $_->cfg_key eq $key } @{ $self->configs };
+ my $cfg = first { $_->cfg_key eq $key } @{ $self->configs || [] };
return $cfg ? $cfg->cfg_value : undef;
}
@@ -58,7 +57,7 @@ sub config_values {
$self->configs([ map { SL::DB::AuthUserConfig->new(cfg_key => $_, cfg_value => $settings{$_}) } keys %settings ]);
}
- return { map { ($_->cfg_key => $_->cfg_value) } @{ $self->configs } };
+ return { map { ($_->cfg_key => $_->cfg_value) } @{ $self->configs || [] } };
}
1;
diff --git a/bin/mozilla/admin.pl b/bin/mozilla/admin.pl
index 67c5969fd..45da2f968 100755
--- a/bin/mozilla/admin.pl
+++ b/bin/mozilla/admin.pl
@@ -77,15 +77,6 @@ our $form;
our $locale;
our $auth;
-my @valid_dateformats = qw(mm/dd/yy dd/mm/yy dd.mm.yy yyyy-mm-dd);
-my @valid_numberformats = ('1,000.00', '1000.00', '1.000,00', '1000,00');
-my @all_stylesheets = qw(lx-office-erp.css Mobile.css kivitendo.css);
-my @all_menustyles = (
- { id => 'old', title => $::locale->text('Old (on the side)') },
- { id => 'v3', title => $::locale->text('Top (CSS)') },
- { id => 'neu', title => $::locale->text('Top (Javascript)') },
-);
-
sub run {
$::lxdebug->enter_sub;
my $session_result = shift;
@@ -131,241 +122,6 @@ sub adminlogin {
print $form->parse_html_template('admin/adminlogin');
}
-sub add_user {
- $::form->{title} = "kivitendo " . $::locale->text('Administration') . " / " . $::locale->text('Add User');
-
- # User does not have a well behaved new constructor, so we'll just have to build one ourself
- my $user = bless {
- "vclimit" => 200,
- "countrycode" => "de",
- "numberformat" => "1.000,00",
- "dateformat" => "dd.mm.yy",
- "stylesheet" => "kivitendo.css",
- "menustyle" => "neu",
- dbport => $::auth->{DB_config}->{port} || 5432,
- dbuser => $::auth->{DB_config}->{user} || 'lxoffice',
- dbhost => $::auth->{DB_config}->{host} || 'localhost',
- }, 'User';
-
- edit_user_form($user);
-}
-
-sub edit_user {
- $::form->{title} = "kivitendo " . $::locale->text('Administration') . " / " . $::locale->text('Edit User');
- $::form->{edit} = 1;
-
- # get user
- my $user = User->new(id => $::form->{user}{id});
-
- edit_user_form($user);
-}
-
-sub edit_user_form {
- my ($user) = @_;
-
- my %cc = $user->country_codes;
- my @all_countrycodes = map { id => $_, title => $cc{$_} }, sort { $cc{$a} cmp $cc{$b} } keys %cc;
- my ($all_dir, $all_master) = _search_templates();
- my $groups = [];
-
- if ($::form->{edit}) {
- my $user_id = $::auth->get_user_id($user->{login});
- my $all_groups = $::auth->read_groups();
-
- for my $group (values %{ $all_groups }) {
- push @{ $groups }, $group if (grep { $user_id == $_ } @{ $group->{members} });
- }
-
- $groups = [ sort { lc $a->{name} cmp lc $b->{name} } @{ $groups } ];
- }
-
- $::form->header;
- print $::form->parse_html_template("admin/edit_user", {
- GROUPS => $groups,
- CAN_CHANGE_PASSWORD => $::auth->can_change_password,
- user => $user->data,
- all_stylesheets => \@all_stylesheets,
- all_numberformats => \@valid_numberformats,
- all_dateformats => \@valid_dateformats,
- all_countrycodes => \@all_countrycodes,
- all_menustyles => \@all_menustyles,
- all_templates => $all_dir,
- all_master_templates => $all_master,
- });
-}
-
-sub save_user {
- my $form = $main::form;
- my $locale = $main::locale;
-
- my $user = $form->{user};
-
- $user->{dbdriver} = 'Pg';
-
- if (!$::form->{edit}) {
- # no spaces allowed in login name
- $user->{login} =~ s/\s//g;
- $::form->show_generic_error($::locale->text('Login name missing!')) unless $user->{login};
-
- # check for duplicates
- my %members = $::auth->read_all_users;
- if ($members{$user->{login}}) {
- $::form->show_generic_error($locale->text('Another user with the login #1 does already exist.', $user->{login}), 'back_button' => 1);
- }
- }
-
- # no spaces allowed in directories
- ($::form->{newtemplates}) = split / /, $::form->{newtemplates};
- $user->{templates} = $::form->{newtemplates} || $::form->{usetemplates} || $user->{login};
-
- # is there a basedir
- if (!-d $::lx_office_conf{paths}->{templates}) {
- $::form->error(sprintf($::locale->text("The directory %s does not exist."), $::lx_office_conf{paths}->{templates}));
- }
-
- # add base directory to $form->{templates}
- $user->{templates} =~ s|.*/||;
- $user->{templates} = $::lx_office_conf{paths}->{templates} . "/$user->{templates}";
-
- my $myconfig = new User(id => $user->{id});
-
- $::form->show_generic_error($::locale->text('Dataset missing!')) unless $user->{dbname};
- $::form->show_generic_error($::locale->text('Database User missing!')) unless $user->{dbuser};
-
- foreach my $item (keys %{$user}) {
- $myconfig->{$item} = $user->{$item};
- }
-
- $myconfig->save_member;
-
- $user->{templates} =~ s|.*/||;
- $user->{templates} = $::lx_office_conf{paths}->{templates} . "/$user->{templates}";
- $::form->{mastertemplates} =~ s|.*/||;
-
- # create user template directory and copy master files
- if (!-d "$user->{templates}") {
- umask(002);
-
- if (mkdir "$user->{templates}", oct("771")) {
-
- umask(007);
-
- # copy templates to the directory
-
- my $oldcurrdir = getcwd();
- if (!chdir("$::lx_office_conf{paths}->{templates}/print/$::form->{mastertemplates}")) {
- $form->error("$ERRNO: chdir $::lx_office_conf{paths}->{templates}/print/$::form->{mastertemplates}");
- }
-
- my $newdir = File::Spec->catdir($oldcurrdir, $user->{templates});
-
- find(
- sub
- {
- next if ($_ eq ".");
-
- if (-d $_) {
- if (!mkdir (File::Spec->catdir($newdir, $File::Find::name))) {
- chdir($oldcurrdir);
- $form->error("$ERRNO: mkdir $File::Find::name");
- }
- } elsif (-l $_) {
- if (!symlink (readlink($_),
- File::Spec->catfile($newdir, $File::Find::name))) {
- chdir($oldcurrdir);
- $form->error("$ERRNO: symlink $File::Find::name");
- }
- } elsif (-f $_) {
- if (!copy($_, File::Spec->catfile($newdir, $File::Find::name))) {
- chdir($oldcurrdir);
- $form->error("$ERRNO: cp $File::Find::name");
- }
- }
- }, "./");
-
- chdir($oldcurrdir);
-
- } else {
- $form->error("$ERRNO: $user->{templates}");
- }
- }
-
- # Add new user to his groups.
- if (ref $form->{new_user_group_ids} eq 'ARRAY') {
- my $all_groups = $main::auth->read_groups();
- my %user = $main::auth->read_user(login => $myconfig->{login});
-
- foreach my $group_id (@{ $form->{new_user_group_ids} }) {
- my $group = $all_groups->{$group_id};
-
- next if !$group;
-
- push @{ $group->{members} }, $user{id};
- $main::auth->save_group($group);
- }
- }
-
- if ($main::auth->can_change_password()
- && defined $::form->{new_password}
- && ($::form->{new_password} ne '********')) {
- my $verifier = SL::Auth::PasswordPolicy->new;
- my $result = $verifier->verify($::form->{new_password}, 1);
-
- if ($result != SL::Auth::PasswordPolicy->OK()) {
- $form->error($::locale->text('The settings were saved, but the password was not changed.') . ' ' . join(' ', $verifier->errors($result)));
- }
-
- $main::auth->change_password($myconfig->{login}, $::form->{new_password});
- }
-
- $::form->redirect($::locale->text('User saved!'));
-}
-
-sub save_user_as_new {
- my $form = $main::form;
-
- $form->{user}{login} = $::form->{new_user_login};
- delete $form->{user}{id};
- delete @{$form}{qw(id edit new_user_login)};
-
- save_user();
-}
-
-sub delete_user {
- my $form = $main::form;
- my $locale = $main::locale;
-
- my $user = $::form->{user} || {};
-
- $::form->show_generic_error($::locale->text('Missing user id!')) unless $user->{id};
-
- my $loaded_user = User->new(id => $user->{id});
-
- my %members = $main::auth->read_all_users();
- my $templates = $members{$loaded_user->{login}}->{templates};
-
- $main::auth->delete_user($loaded_user->{login});
-
- if ($templates) {
- my $templates_in_use = 0;
-
- foreach my $login (keys %members) {
- next if $loaded_user->{login} eq $login;
- next if $members{$login}->{templates} ne $templates;
- $templates_in_use = 1;
- last;
- }
-
- if (!$templates_in_use && -d $templates) {
- unlink <$templates/*>;
- rmdir $templates;
- }
- }
-
- $form->redirect($locale->text('User deleted!'));
-
-}
-
sub login_name {
my $login = shift;
diff --git a/locale/de/all b/locale/de/all
index 8400806da..59ad0cb53 100755
--- a/locale/de/all
+++ b/locale/de/all
@@ -75,6 +75,7 @@ $self->{texts} = {
'Abort' => 'Abbrechen',
'Abrechnungsnummer' => 'Abrechnungsnummer',
'Abteilung' => 'Abteilung',
+ 'Access to clients' => 'Zugriff auf Mandanten',
'Account' => 'Konto',
'Account Category A' => 'Aktiva/Mittelverwendung',
'Account Category C' => 'Kosten',
@@ -116,6 +117,7 @@ $self->{texts} = {
'Accounting Group saved!' => 'Buchungsgruppe gespeichert!',
'Accounting method' => 'Versteuerungsart',
'Accrual' => 'Soll-Versteuerung',
+ 'Actions' => 'Aktionen',
'Active' => 'Aktiv',
'Active?' => 'Aktiviert?',
'Add' => 'Erfassen',
@@ -178,6 +180,7 @@ $self->{texts} = {
'All Accounts' => 'Alle Konten',
'All Datasets up to date!' => 'Alle Datenbanken sind auf aktuellem Stand.',
'All changes in that file have been reverted.' => 'Alle Änderungen in dieser Datei wurden rückgängig gemacht.',
+ 'All clients' => 'Alle Mandanten',
'All database upgrades have been applied.' => 'Alle Datenbankupdates wurden eingespielt.',
'All general ledger entries' => 'Alle Hauptbucheinträge',
'All groups' => 'Alle Gruppen',
@@ -200,7 +203,6 @@ $self->{texts} = {
'An invalid character was used (valid characters: #1).' => 'Ein ungültiges Zeichen wurde benutzt (gültige Zeichen: #1).',
'An upper-case character is required.' => 'Ein GroÃbuchstabe ist vorgeschrieben.',
'Annotations' => 'Anmerkungen',
- 'Another user with the login #1 does already exist.' => 'Es existiert bereits ein anderer Benutzer mit diesem Login.',
'Any stock contents containing a best before date will be impossible to stock out otherwise.' => 'Sonst können Artikel, bei denen ein Mindesthaltbarkeitsdatum gesetzt ist, nicht mehr ausgelagert werden.',
'Ap aging on %s' => 'Offene Verbindlichkeiten zum %s',
'Application Error. No Format given' => 'Fehler in der Anwendung. Das Ausgabeformat fehlt.',
@@ -436,12 +438,12 @@ $self->{texts} = {
'City' => 'Stadt',
'Cleared Balance' => 'abgeschlossen',
'Clearing Tax Received (No 71)' => 'Verrechnung des Erstattungsbetrages erwünscht (Zeile 71)',
- 'Click on login name to edit!' => 'Zum Bearbeiten den Benutzernamen anklicken!',
'Client #1' => 'Mandant #1',
'Client Configuration' => 'Mandantenkonfiguration',
'Client Configuration saved!' => 'Mandantenkonfiguration gespeichert!',
'Client list' => 'Mandantenliste',
'Client name' => 'Mandantenname',
+ 'Clients this user has access to' => 'Mandaten, auf die Benutzer Zugriff hat',
'Close' => 'Ãbernehmen',
'Close Books up to' => 'Die Bücher abschlieÃen bis zum',
'Close Flash' => 'SchlieÃen',
@@ -497,6 +499,7 @@ $self->{texts} = {
'Create a new department' => 'Eine neue Abteilung erfassen',
'Create a new payment term' => 'Neue Zahlungsbedingungen anlegen',
'Create a new project' => 'Neues Projekt anlegen',
+ 'Create a new user' => 'Einen neuen Benutzer anlegen',
'Create and edit RFQs' => 'Lieferantenanfragen erfassen und bearbeiten',
'Create and edit dunnings' => 'Mahnungen erfassen und bearbeiten',
'Create and edit invoices and credit notes' => 'Rechnungen und Gutschriften erfassen und bearbeiten',
@@ -585,13 +588,11 @@ $self->{texts} = {
'DUNNING STARTED' => 'Mahnprozess gestartet',
'DUNS-Nr' => 'DUNS-Nr.',
'Data' => 'Daten',
- 'Database' => 'Datenbank',
'Database Administration' => 'Datenbankadministration',
'Database Connection Test' => 'Test der Datenbankverbindung',
'Database Host' => 'Datenbankcomputer',
'Database ID' => 'Datenbank-ID',
'Database User' => 'Datenbankbenutzer',
- 'Database User missing!' => 'Datenbankbenutzer fehlt!',
'Database backups and restorations are disabled in the configuration.' => 'Datenbanksicherungen und -wiederherstellungen sind in der Konfiguration deaktiviert.',
'Database name' => 'Datenbankname',
'Database settings' => 'Datenbankeinstellungen',
@@ -995,6 +996,7 @@ $self->{texts} = {
'Group saved!' => 'Warengruppe gespeichert!',
'Groups' => 'Warengruppen',
'Groups that are valid for this client for access rights' => 'Gruppen, die für diesen Mandanten gültig sind',
+ 'Groups this user is a member in' => 'Gruppen, in denen Benutzer Mitglied ist',
'Groups valid for this client' => 'Für Mandanten gültige Gruppen',
'HTML' => 'HTML',
'HTML Templates' => 'HTML-Vorlagen',
@@ -1208,7 +1210,6 @@ $self->{texts} = {
'Lockfile removed!' => 'System entsperrt!',
'Login' => 'Anmelden',
'Login Name' => 'Benutzer',
- 'Login name missing!' => 'Benutzer - Feld darf nicht leer sein!',
'Login of User' => 'Login',
'Logout' => 'Abmelden',
'Logout now' => 'kivitendo jetzt verlassen',
@@ -1261,7 +1262,6 @@ $self->{texts} = {
'Missing parameter #1 in call to sub #2.' => 'Fehlender Parameter \'#1\' in Funktionsaufruf \'#2\'.',
'Missing parameter (at least one of #1) in call to sub #2.' => 'Fehlernder Parameter (mindestens einer aus \'#1\') in Funktionsaufruf \'#2\'.',
'Missing taxkeys in invoices with taxes.' => 'Fehlende Steuerschlüssel in Rechnungen mit Steuern',
- 'Missing user id!' => 'Benutzer ID fehlt!',
'Mitarbeiter' => 'Mitarbeiter',
'Mixed (requires column "type")' => 'Gemischt (erfordert Spalte "type")',
'Mobile' => 'Mobiltelefon',
@@ -1289,7 +1289,7 @@ $self->{texts} = {
'Net amount' => 'Nettobetrag',
'Netto Terms' => 'Zahlungsziel netto',
'New Buchungsgruppe #1' => 'Neue Buchungsgruppe #1',
- 'New Templates' => 'Erzeuge Vorlagen, Name',
+ 'New Password' => 'Neues Passwort',
'New assembly' => 'Neues Erzeugnis',
'New bank account' => 'Neues Bankkonto',
'New client #1: The database configuration fields "host", "port", "name" and "user" must not be empty.' => 'Neuer Mandant #1: Die Datenbankkonfigurationsfelder "Host", "Port" und "Name" dürfen nicht leer sein.',
@@ -1335,6 +1335,7 @@ $self->{texts} = {
'No file has been uploaded yet.' => 'Es wurde noch keine Datei hochgeladen.',
'No group has been selected, or the group does not exist anymore.' => 'Es wurde keine Gruppe ausgewählt, oder die Gruppe wurde in der Zwischenzeit gelöscht.',
'No groups have been added yet.' => 'Es wurden noch keine Gruppen angelegt.',
+ 'No groups have been created yet.' => 'Es wurden noch keine Gruppen angelegt.',
'No or an unknown authenticantion module specified in "config/kivitendo.conf".' => 'Es wurde kein oder ein unbekanntes Authentifizierungsmodul in "config/kivitendo.conf" angegeben.',
'No part was found matching the search parameters.' => 'Es wurde kein Artikel gefunden, auf den die Suchparameter zutreffen.',
'No payment term has been created yet.' => 'Es wurden noch keine Zahlungsbedingungen angelegt.',
@@ -1806,7 +1807,6 @@ $self->{texts} = {
'Set eMail text' => 'eMail Text eingeben',
'Settings' => 'Einstellungen',
'Setup Menu' => 'Menü-Variante',
- 'Setup Templates' => 'Vorlagen auswählen',
'Ship to' => 'Lieferadresse',
'Ship via' => 'Transportmittel',
'Shipping Address' => 'Lieferadresse',
@@ -1975,7 +1975,6 @@ $self->{texts} = {
'Templates' => 'Vorlagen',
'Terms missing in row ' => '+Tage fehlen in Zeile ',
'Test and preview' => 'Test und Vorschau',
- 'Test connection' => 'Verbindung testen',
'Text field' => 'Textfeld',
'Text field variables: \'WIDTH=w HEIGHT=h\' sets the width and height of the text field. They default to 30 and 5 respectively.' => 'Textfelder: \'WIDTH=w HEIGHT=h\' setzen die Breite und die Höhe des Textfeldes. Wenn nicht anders angegeben, so werden sie 30 Zeichen breit und fünf Zeichen hoch dargestellt.',
'Text variables: \'MAXLENGTH=n\' sets the maximum entry length to \'n\'.' => 'Textzeilen: \'MAXLENGTH=n\' setzt eine Maximallänge von n Zeichen.',
@@ -2085,6 +2084,8 @@ $self->{texts} = {
'The items are imported accoring do their number "X" regardless of the column order inside the file.' => 'Die Einträge werden in der Reihenfolge ihrer Indizes "X" unabhängig von der Spaltenreihenfolge in der Datei importiert.',
'The link target to add has been created from the existing record.' => 'Das auszuwählende Verknüpfungsziel wurde aus dem bestehenden Beleg erstellt.',
'The list has been printed.' => 'Die Liste wurde ausgedruckt.',
+ 'The login is missing.' => 'Der Loginname fehlt.',
+ 'The login is not unique.' => 'Der Loginname ist nicht eindeutig.',
'The long description is missing.' => 'Der Langtext fehlt.',
'The name in row %d has already been used before.' => 'Der Name in Zeile %d wurde vorher bereits benutzt.',
'The name is missing in row %d.' => 'Der Name fehlt in Zeile %d.',
@@ -2150,7 +2151,10 @@ $self->{texts} = {
'The unit in row %d has been used in the meantime and cannot be changed anymore.' => 'Die Einheit in Zeile %d wurde in der Zwischenzeit benutzt und kann nicht mehr geändert werden.',
'The units have been saved.' => 'Die Einheiten wurden gespeichert.',
'The user can chose which client to connect to during login.' => 'Bei der Anmeldung kann der Benutzer auswählen, welchen Mandanten er benutzen möchte.',
- 'The user is a member in the following group(s):' => 'Der Benutzer ist Mitglied in den folgenden Gruppen:',
+ 'The user could not be deleted.' => '',
+ 'The user has been created.' => 'Der Benutzer wurde angelegt.',
+ 'The user has been deleted.' => '',
+ 'The user has been saved.' => 'Der Benutzer wurde gespeichert.',
'The variable name must only consist of letters, numbers and underscores. It must begin with a letter. Example: send_christmas_present' => 'Der Variablenname darf nur aus Zeichen (keine Umlaute), Ziffern und Unterstrichen bestehen. Er muss mit einem Buchstaben beginnen. Beispiel: weihnachtsgruss_verschicken',
'The warehouse could not be deleted because it has already been used.' => 'Das Lager konnte nicht gelöscht werden, da es bereits in Benutzung war.',
'The warehouse does not contain any bins.' => 'Das Lager enthält keine Lagerplätze.',
@@ -2215,6 +2219,8 @@ $self->{texts} = {
'This update will change the nature the onhand of goods is tracked.' => 'Dieses update ändert die Art und Weise wie Lagermengen gezält werden.',
'This upgrade script tries to map all existing parts in the database to the newly created Buchungsgruppen.' => 'Dieses Upgradescript versucht, bei allen bestehenden Artikeln neu erstellte Buchungsgruppen zuzuordnen.',
'This upgrade script tries to map all existing units in the database to the newly created units.' => 'Dieses Update-Script versucht, alle bestehenden Einheiten automatisch in die neuen Einheiten umzuwandeln.',
+ 'This user is a member in the following groups' => 'Dieser Benutzer ist Mitglied in den folgenden Gruppen',
+ 'This user will have access to the following clients' => 'Dieser Benutzer wird Zugriff auf die folgenden Mandanten haben',
'This vendor number is already in use.' => 'Diese Lieferantennummer wird bereits verwendet.',
'Three Options:' => 'Drei Optionen:',
'Time period for the analysis:' => 'Analysezeitraum:',
@@ -2224,8 +2230,8 @@ $self->{texts} = {
'To (email)' => 'An',
'To (time)' => 'Bis',
'To Date' => 'Bis',
- 'To add a user to a group edit a name, change the login name and save. A new user with the same variables will then be saved under the new login name.' => 'Um einer Gruppe einen neuen Benutzer hinzuzufügen, ändern und speichern Sie am einfachsten einen bestehenden Benutzernamen. Unter dem neuen Namen wird dann ein Benutzer mit denselben Einstellungen angelegt.',
'To continue please change the taxkey 0 to another value.' => 'Um fortzufahren, ändern Sie bitte den Steuerschlüssel 0 auf einen anderen Wert.',
+ 'To user login' => 'Zum Benutzerlogin',
'Top' => 'Oben',
'Top (CSS)' => 'Oben (mit CSS)',
'Top (Javascript)' => 'Oben (mit Javascript)',
@@ -2318,22 +2324,17 @@ $self->{texts} = {
'Updating the client fields in the database "#1" on host "#2:#3" failed.' => 'Die Aktualisierung der Mandantenfelder in der Datenbank "#1" auf Host "#2:#3" schlug fehl.',
'Uploaded on #1, size #2 kB' => 'Am #1 hochgeladen, GröÃe #2 kB',
'Use As New' => 'Als neu verwenden',
- 'Use Templates' => 'Benutze Vorlagen',
'Use master default bin for Default Transfer, if no default bin for the part is configured' => 'Standardlagerplatz für Ein- / Auslagern über Standard-Lagerplatz, falls für die Ware kein expliziter Lagerplatz konfiguriert ist',
'User' => 'Benutzer',
'User Config' => 'Einstellungen',
- 'User Login' => 'Als Benutzer anmelden',
'User access' => 'Benutzerzugriff',
- 'User deleted!' => 'Benutzer gelöscht!',
'User list' => 'Benutzerliste',
'User login' => 'Benutzeranmeldung',
'User name' => 'Benutzername',
- 'User saved!' => 'Benutzer gespeichert!',
'Username' => 'Benutzername',
'Users in this group' => 'BenutzerInnen in dieser Gruppe',
'Users with access' => 'Benutzer mit Zugriff',
'Users with access to this client' => 'Benutzer mit Zugriff auf diesen Mandanten',
- 'Ust-IDNr' => 'USt-IdNr.',
'VAT ID' => 'UStdID-Nr',
'Valid' => 'Gültig',
'Valid from' => 'Gültig ab',
diff --git a/sql/Pg-upgrade2-auth/foreign_key_constraints_on_delete.sql b/sql/Pg-upgrade2-auth/foreign_key_constraints_on_delete.sql
new file mode 100644
index 000000000..197bd8e11
--- /dev/null
+++ b/sql/Pg-upgrade2-auth/foreign_key_constraints_on_delete.sql
@@ -0,0 +1,40 @@
+-- @tag: foreign_key_constraints_on_delete
+-- @description: Ãndert "FOREIGN KEY" constraints auf "ON DELETE CASCADE"
+-- @depends: clients
+-- @charset: utf-8
+
+-- auth.clients_groups
+ALTER TABLE auth.clients_groups DROP CONSTRAINT clients_groups_client_id_fkey;
+ALTER TABLE auth.clients_groups DROP CONSTRAINT clients_groups_group_id_fkey;
+
+ALTER TABLE auth.clients_groups ADD FOREIGN KEY (client_id) REFERENCES auth.clients (id) ON DELETE CASCADE;
+ALTER TABLE auth.clients_groups ADD FOREIGN KEY (group_id) REFERENCES auth."group" (id) ON DELETE CASCADE;
+
+-- auth.clients_users
+ALTER TABLE auth.clients_users DROP CONSTRAINT clients_users_client_id_fkey;
+ALTER TABLE auth.clients_users DROP CONSTRAINT clients_users_user_id_fkey;
+
+ALTER TABLE auth.clients_users ADD FOREIGN KEY (client_id) REFERENCES auth.clients (id) ON DELETE CASCADE;
+ALTER TABLE auth.clients_users ADD FOREIGN KEY (user_id) REFERENCES auth."user" (id) ON DELETE CASCADE;
+
+-- auth.group_rights
+ALTER TABLE auth.group_rights DROP CONSTRAINT group_rights_group_id_fkey;
+
+ALTER TABLE auth.group_rights ADD FOREIGN KEY (group_id) REFERENCES auth."group" (id) ON DELETE CASCADE;
+
+ -- auth.session_content
+ALTER TABLE auth.session_content DROP CONSTRAINT session_content_session_id_fkey;
+
+ALTER TABLE auth.session_content ADD FOREIGN KEY (session_id) REFERENCES auth.session (id) ON DELETE CASCADE;
+
+ -- auth.user_config
+ALTER TABLE auth.user_config DROP CONSTRAINT user_config_user_id_fkey;
+
+ALTER TABLE auth.user_config ADD FOREIGN KEY (user_id) REFERENCES auth."user" (id) ON DELETE CASCADE;
+
+-- auth.user_group
+ALTER TABLE auth.user_group DROP CONSTRAINT user_group_user_id_fkey;
+ALTER TABLE auth.user_group DROP CONSTRAINT user_group_group_id_fkey;
+
+ALTER TABLE auth.user_group ADD FOREIGN KEY (user_id) REFERENCES auth."user" (id) ON DELETE CASCADE;
+ALTER TABLE auth.user_group ADD FOREIGN KEY (group_id) REFERENCES auth."group" (id) ON DELETE CASCADE;
diff --git a/templates/webpages/admin/backup_dataset.html b/templates/webpages/admin/backup_dataset.html
index 4306460af..3df1c89a7 100644
--- a/templates/webpages/admin/backup_dataset.html
+++ b/templates/webpages/admin/backup_dataset.html
@@ -82,7 +82,7 @@
-
+
@@ -90,7 +90,7 @@
- [% 'Back' | $T8 %]
+ [% 'Back' | $T8 %]
diff --git a/templates/webpages/admin/backup_dataset_email_done.html b/templates/webpages/admin/backup_dataset_email_done.html
index a70a2ee96..e64b2a8d9 100644
--- a/templates/webpages/admin/backup_dataset_email_done.html
+++ b/templates/webpages/admin/backup_dataset_email_done.html
@@ -5,4 +5,4 @@
[% LxERP.t8('The dataset backup has been sent via email to #1.', to) | html %]
- [% L.link("controller.pl?action=Admin/list_clients_and_users", LxERP.t8("Continue")) %]
+
[% L.link("controller.pl?action=Admin/show", LxERP.t8("Continue")) %]
diff --git a/templates/webpages/admin/dbadmin.html b/templates/webpages/admin/dbadmin.html
index bccd51494..014a1150b 100644
--- a/templates/webpages/admin/dbadmin.html
+++ b/templates/webpages/admin/dbadmin.html
@@ -3,7 +3,7 @@
[% title %]
-
-
+
+[%- INCLUDE 'common/flash.html' %]
+
+[% title %]
+
+[% L.link(SELF.url_for(action="show"), LxERP.t8("Back")) %]
+
+
+ [% L.hidden_tag("user.id", SELF.user.id) %]
+ [% L.hidden_tag("action", "") %]
+ [%- SET props=SELF.user.config_values %]
+
+ [%- LxERP.t8("Settings") %]
+
+
+
+
+
+
+ [% LxERP.t8('Login Name') %]
+ [% L.input_tag("user.login", SELF.user.login) %]
+
+
+ [%- IF AUTH.can_change_password %]
+
+ [% LxERP.t8("New Password") %]
+ [% L.input_tag("new_password", "", type="password") %]
+
+ [%- END %]
+
+
+ [% LxERP.t8("Name") %]
+ [% L.input_tag("user.config_values.name", props.name) %]
+
+
+
+ [% LxERP.t8('E-mail') %]
+ [% L.input_tag("user.config_values.email", props.email) %]
+
+
+
+ [% LxERP.t8('Signature') %]
+ [% L.textarea_tag("user.config_values.signature", props.signature, rows=3, cols=35) %]
+
+
+
+ [% LxERP.t8('Phone') %]
+ [% L.input_tag("user.config_values.tel", props.tel) %]
+
+
+
+ [% LxERP.t8('Fax') %]
+ [% L.input_tag("user.config_values.fax", props.fax) %]
+
+
+
+
+
+
+
+ [% LxERP.t8("Date Format") %]
+ [% L.select_tag("user.config_values.dateformat", SELF.all_dateformats, default=props.dateformat) %]
+
+
+
+ [% LxERP.t8("Number Format") %]
+ [% L.select_tag("user.config_values.numberformat", SELF.all_numberformats, default=props.numberformat) %]
+
+
+
+ [% LxERP.t8("Dropdown Limit") %]
+ [% L.input_tag("user.config_values.vclimit", props.vclimit) %]
+
+
+
+ [% LxERP.t8("Language") %]
+ [% L.select_tag("user.config_values.countrycode", SELF.all_countrycodes, title_key="title", default=props.countrycode) %]
+
+
+
+ [% LxERP.t8("Stylesheet") %]
+ [% L.select_tag("user.config_values.stylesheet", SELF.all_stylesheets, default=props.stylesheet) %]
+
+
+
+ [% LxERP.t8("Setup Menu") %]
+ [% L.select_tag("user.config_values.menustyle", SELF.all_menustyles, title_key="title", default=props.menustyle) %]
+
+
+
+ [% LxERP.t8("Mandatory Departments") %]
+
+ [% L.radio_button_tag('user.config_values.mandatory_departments', value='0', id='user.config_values.mandatory_departments_0', label=LxERP.t8('No'), checked=!props.mandatory_departments) %]
+ [% L.radio_button_tag('user.config_values.mandatory_departments', value='1', id='user.config_values.mandatory_departments_1', label=LxERP.t8('Yes'), checked= props.mandatory_departments) %]
+
+
+
+
+
+
+
+ [%- LxERP.t8("Access to clients") %]
+
+[% IF SELF.all_clients.size %]
+
+ [%- LxERP.t8("This user will have access to the following clients") %]:
+
+
+
+ [% L.select_tag("user.clients[]", SELF.all_clients, id="user_clients", title_key="name", default=SELF.user.clients, default_key='id', multiple=1) %]
+ [% L.multiselect2side("user_clients", labelsx => LxERP.t8("All clients"), labeldx => LxERP.t8("Clients this user has access to")) %]
+
+
+[%- ELSE %]
+
+ [% LxERP.t8("No clients have been created yet.") %]
+
+[%- END %]
+
+ [%- LxERP.t8("Group membership") %]
+
+[% IF SELF.all_groups.size %]
+
+ [%- LxERP.t8("This user is a member in the following groups") %]:
+
+
+
+ [% L.select_tag("user.groups[]", SELF.all_groups, id="user_groups", title_key="name", default=SELF.user.groups, default_key='id', multiple=1) %]
+ [% L.multiselect2side("user_groups", labelsx => LxERP.t8("All groups"), labeldx => LxERP.t8("Groups this user is a member in")) %]
+
+
+[%- ELSE %]
+
+ [% LxERP.t8("No groups have been created yet.") %]
+
+[%- END %]
+
+
+
+
+ [% L.link(SELF.url_for(action="show"), LxERP.t8("Back")) %]
+
+ [% L.button_tag("submit_with_action('save_user')", LxERP.t8("Save")) %]
+ [% IF SELF.user.id %]
+ [% L.button_tag("save_as_new()", LxERP.t8("Save as new")) %]
+ [% L.button_tag("submit_with_action('delete_user')", LxERP.t8("Delete"), confirm=LxERP.t8("Are you sure?")) %]
+ [%- END %]
+
+
+
+
+
diff --git a/templates/webpages/admin/list_users.html b/templates/webpages/admin/list_users.html
deleted file mode 100644
index 0d75e6b0c..000000000
--- a/templates/webpages/admin/list_users.html
+++ /dev/null
@@ -1,115 +0,0 @@
-[%- USE T8 %]
-[%- USE HTML %][%- USE LxERP -%][%- USE L -%]
- [% title %]
-
-
-
-
-
- [% L.link(SELF.url_for(action="add_client"), LxERP.t8("Add Client")) %]
- |
- [% L.link(SELF.url_for(action="add_user"), LxERP.t8("Add User")) %]
- |
- [% L.link(SELF.url_for(action="edit_groups"), LxERP.t8("Edit groups")) %]
- |
- [% L.link(SELF.url_for(action="pg_database_administration", controller="admin.pl"), LxERP.t8("Pg Database Administration")) %]
- |
- [% L.link(SELF.url_for(action="printer_management", controller="admin.pl"), LxERP.t8("Printer Management")) %]
- |
- [% IF LOCKED %]
- [% L.link(SELF.url_for(action="unlock_system"), LxERP.t8("Unlock System")) %]
- [% ELSE %]
- [% L.link(SELF.url_for(action="lock_system"), LxERP.t8("Lock System")) %]
- [% END %]
- |
- [% L.link(SELF.url_for(action="logout"), LxERP.t8("Logout")) %]
-
-
-
[% 'Click on login name to edit!' | $T8 %]
-
[% 'To add a user to a group edit a name, change the login name and save. A new user with the same variables will then be saved under the new login name.' | $T8 %]
-
-
-
-
-
- [% 'User Login' | $T8 %]
-
-
-
-
-
-
-
diff --git a/templates/webpages/admin/restore_dataset_start_footer.html b/templates/webpages/admin/restore_dataset_start_footer.html
index fd414dc23..86ca7aeb5 100644
--- a/templates/webpages/admin/restore_dataset_start_footer.html
+++ b/templates/webpages/admin/restore_dataset_start_footer.html
@@ -10,5 +10,5 @@
- [% L.link("controller.pl?action=Admin/list_clients_and_users", LxERP.t8("Continue")) %]
+ [% L.link("controller.pl?action=Admin/show", LxERP.t8("Continue")) %]
diff --git a/templates/webpages/admin/show.html b/templates/webpages/admin/show.html
new file mode 100644
index 000000000..06d0e9176
--- /dev/null
+++ b/templates/webpages/admin/show.html
@@ -0,0 +1,95 @@
+[%- USE HTML %][%- USE LxERP -%][%- USE L -%]
+
+[% INCLUDE 'common/flash.html' %]
+
+[% title %]
+
+
+ [% LxERP.t8("Actions") %]:
+
+ [% L.link(SELF.url_for(action="new_client"), LxERP.t8("Add Client")) %]
+ |
+ [% L.link(SELF.url_for(action="new_user"), LxERP.t8("Add User")) %]
+ |
+ [% L.link(SELF.url_for(action="edit_groups"), LxERP.t8("Edit groups")) %]
+ |
+ [% L.link(SELF.url_for(action="pg_database_administration", controller="admin.pl"), LxERP.t8("Pg Database Administration")) %]
+ |
+ [% L.link(SELF.url_for(action="printer_management", controller="admin.pl"), LxERP.t8("Printer Management")) %]
+ |
+ [% IF LOCKED %]
+ [% L.link(SELF.url_for(action="unlock_system"), LxERP.t8("Unlock System")) %]
+ [% ELSE %]
+ [% L.link(SELF.url_for(action="lock_system"), LxERP.t8("Lock System")) %]
+ [% END %]
+ |
+ [% L.link(SELF.url_for(action="logout"), LxERP.t8("Logout")) %]
+ |
+ [% L.link(SELF.url_for(controller="LoginScreen", action="user_login"), LxERP.t8("To user login")) %]
+
+
+
+
+
diff --git a/templates/webpages/admin/update_dataset.html b/templates/webpages/admin/update_dataset.html
index fddf5c3dc..29a5e6226 100644
--- a/templates/webpages/admin/update_dataset.html
+++ b/templates/webpages/admin/update_dataset.html
@@ -39,7 +39,7 @@
-
+