sub authenticate {
$main::lxdebug->enter_sub();
- my $self = shift;
+ my ($self, $login, $password) = @_;
$main::lxdebug->leave_sub();
- my $result = $self->{authenticator}->authenticate(@_);
+ my $result = $login ? $self->{authenticator}->authenticate($login, $password) : ERR_USER;
return OK if $result eq OK;
sleep 5;
return $result;
$main::lxdebug->message(LXDebug->DEBUG1, "Auth::dbconnect DSN: $dsn");
- $self->{dbh} = DBI->connect($dsn, $cfg->{user}, $cfg->{password}, { pg_enable_utf8 => $::locale->is_utf8, AutoCommit => 0 });
+ $self->{dbh} = DBI->connect($dsn, $cfg->{user}, $cfg->{password}, { pg_enable_utf8 => $::locale->is_utf8, AutoCommit => 1 });
if (!$may_fail && !$self->{dbh}) {
$main::form->error($main::locale->text('The connection to the authentication database failed:') . "\n" . $DBI::errstr);
my ($sth, $query, $user_id);
+ $dbh->begin_work;
+
$query = qq|SELECT id FROM auth."user" WHERE login = ?|;
($user_id) = selectrow_query($form, $dbh, $query, $login);
my $form = $main::form;
my $dbh = $self->dbconnect();
+
+ $dbh->begin_work;
+
my $query = qq|SELECT id FROM auth."user" WHERE login = ?|;
my ($id) = selectrow_query($form, $dbh, $query, $login);
- return $main::lxdebug->leave_sub() if (!$id);
+ $dbh->rollback and return $main::lxdebug->leave_sub() if (!$id);
do_query($form, $dbh, qq|DELETE FROM auth.user_group WHERE user_id = ?|, $id);
do_query($form, $dbh, qq|DELETE FROM auth.user_config WHERE user_id = ?|, $id);
while (my $ref = $sth->fetchrow_hashref()) {
$self->{SESSION}->{$ref->{sess_key}} = $ref->{sess_value};
- $form->{$ref->{sess_key}} = $self->_load_value($ref->{sess_value}) if (!defined $form->{$ref->{sess_key}});
+ next if defined $form->{$ref->{sess_key}};
+
+ my $params = $self->_load_value($ref->{sess_value});
+ $form->{$ref->{sess_key}} = $params->{data} if $params->{auto_restore} || $params->{simple};
}
$sth->finish();
}
sub _load_value {
- return $_[1] if $_[1] !~ m/^---/;
+ my ($self, $value) = @_;
+
+ return { simple => 1, data => $value } if $value !~ m/^---/;
- my $value;
+ my %params = ( simple => 1 );
eval {
- $value = YAML::Load($_[1]);
+ my $data = YAML::Load($value);
+
+ if (ref $data eq 'HASH') {
+ map { $params{$_} = $data->{$_} } keys %{ $data };
+ $params{simple} = 0;
+
+ } else {
+ $params{data} = $data;
+ }
+
1;
- } or return $_[1];
+ } or $params{data} = $value;
- return $value;
+ return \%params;
}
sub destroy_session {
if ($session_id) {
my $dbh = $self->dbconnect();
+ $dbh->begin_work;
+
do_query($main::form, $dbh, qq|DELETE FROM auth.session_content WHERE session_id = ?|, $session_id);
do_query($main::form, $dbh, qq|DELETE FROM auth.session WHERE id = ?|, $session_id);
my $self = shift;
my $dbh = $self->dbconnect();
+
+ $dbh->begin_work;
+
my $query =
qq|DELETE FROM auth.session_content
WHERE session_id IN
$form = $main::form;
$dbh = $self->dbconnect();
+ $dbh->begin_work;
+ do_query($::form, $dbh, qq|LOCK auth.session_content|);
+
$query = qq|SELECT id FROM auth.session WHERE id = ?|;
($id) = selectrow_query($form, $dbh, $query, $session_id);
my $self = shift;
my $provided_dbh = shift;
- my $dbh = $provided_dbh || $self->dbconnect();
+ my $dbh = $provided_dbh || $self->dbconnect(1);
+
+ return unless $dbh;
+
+ $dbh->begin_work unless $provided_dbh;
+ do_query($::form, $dbh, qq|LOCK auth.session_content|);
do_query($::form, $dbh, qq|DELETE FROM auth.session_content WHERE session_id = ?|, $session_id);
if (%{ $self->{SESSION} }) {
$self->{SESSION} ||= { };
while (my ($key, $value) = each %params) {
- $self->{SESSION}->{ $key } = YAML::Dump($value);
+ $self->{SESSION}->{ $key } = YAML::Dump(ref($value) eq 'HASH' ? { data => $value } : $value);
}
$main::lxdebug->leave_sub();
sub get_session_value {
$main::lxdebug->enter_sub();
- my $self = shift;
- my $value = $self->{SESSION} ? $self->_load_value($self->{SESSION}->{ $_[0] }) : undef;
+ my $self = shift;
+ my $params = $self->{SESSION} ? $self->_load_value($self->{SESSION}->{ $_[0] }) : {};
$main::lxdebug->leave_sub();
- return $value;
+ return $params->{data};
+}
+
+sub create_unique_sesion_value {
+ my ($self, $value, %params) = @_;
+
+ $self->{SESSION} ||= { };
+
+ my @now = gettimeofday();
+ my $key = "$$-" . ($now[0] * 1000000 + $now[1]) . "-";
+ $self->{unique_counter} ||= 0;
+
+ $self->{unique_counter}++ while exists $self->{SESSION}->{$key . $self->{unique_counter}};
+ $self->{unique_counter}++;
+
+ $value = { expiration => $params{expiration} ? ($now[0] + $params{expiration}) * 1000000 + $now[1] : undef,
+ no_auto => !$params{auto_restore},
+ data => $value,
+ };
+
+ $self->{SESSION}->{$key . $self->{unique_counter}} = YAML::Dump($value);
+
+ return $key . $self->{unique_counter};
+}
+
+sub save_form_in_session {
+ my ($self, %params) = @_;
+
+ my $form = delete($params{form}) || $::form;
+ my $non_scalars = delete $params{non_scalars};
+ my $data = {};
+
+ my %skip_keys = map { ( $_ => 1 ) } (qw(login password stylesheet version titlebar), @{ $params{skip_keys} || [] });
+
+ foreach my $key (grep { !$skip_keys{$_} } keys %{ $form }) {
+ $data->{$key} = $form->{$key} if !ref($form->{$key}) || $non_scalars;
+ }
+
+ return $self->create_unique_sesion_value($data, %params);
+}
+
+sub restore_form_from_session {
+ my ($self, $key, %params) = @_;
+
+ my $data = $self->get_session_value($key);
+ return $self unless $data;
+
+ my $form = delete($params{form}) || $::form;
+ my $clobber = exists $params{clobber} ? $params{clobber} : 1;
+
+ map { $form->{$_} = $data->{$_} if $clobber || !exists $form->{$_} } keys %{ $data };
+
+ return $self;
+}
+
+sub expire_session_keys {
+ my ($self) = @_;
+
+ $self->{SESSION} ||= { };
+
+ my @now = gettimeofday();
+ my $now = $now[0] * 1000000 + $now[1];
+
+ $self->delete_session_value(map { $_->[0] }
+ grep { $_->[1]->{expiration} && ($now > $_->[1]->{expiration}) }
+ map { [ $_, $self->_load_value($self->{SESSION}->{$_}) ] }
+ keys %{ $self->{SESSION} });
+
+ return $self;
+}
+
+sub _has_expiration {
+ my ($value) = @_;
+ return (ref $value eq 'HASH') && exists($value->{expiration}) && $value->{data};
}
sub set_cookie_environment_variable {
my $form = $main::form;
my $dbh = $self->dbconnect();
+ $dbh->begin_work;
+
my ($query, $sth, $row, $rights);
if (!$group->{id}) {
my $form = $main::from;
my $dbh = $self->dbconnect();
+ $dbh->begin_work;
do_query($form, $dbh, qq|DELETE FROM auth.user_group WHERE group_id = ?|, $id);
do_query($form, $dbh, qq|DELETE FROM auth.group_rights WHERE group_id = ?|, $id);
}
1;
+__END__
+
+=pod
+
+=encoding utf8
+
+=head1 NAME
+
+SL::Auth - Authentication and session handling
+
+=head1 FUNCTIONS
+
+=over 4
+
+=item C<set_session_value %values>
+
+Store all key/value pairs in C<%values> in the session. All of these
+values are copied back into C<$::form> in the next request
+automatically.
+
+The values can be any Perl structure. They are stored as YAML dumps.
+
+=item C<get_session_value $key>
+
+Retrieve a value from the session. Returns C<undef> if the value
+doesn't exist.
+
+=item C<create_unique_sesion_value $value, %params>
+
+Create a unique key in the session and store C<$value>
+there.
+
+If C<$params{expiration}> is set then it is interpreted as a number of
+seconds after which the value is removed from the session. It will
+never expire if that parameter is falsish.
+
+If C<$params{auto_restore}> is trueish then the value will be copied
+into C<$::form> upon the next request automatically. It defaults to
+C<false> and has therefore different behaviour than
+L</set_session_value>.
+
+Returns the key created in the session.
+
+=item C<expire_session_keys>
+
+Removes all keys from the session that have an expiration time set and
+whose expiration time is in the past.
+
+=item C<save_session>
+
+Stores the session values in the database. This is the only function
+that actually stores stuff in the database. Neither the various
+setters nor the deleter access the database.
+
+=item <save_form_in_session %params>
+
+Stores the content of C<$params{form}> (default: C<$::form>) in the
+session using L</create_unique_sesion_value>.
+
+If C<$params{non_scalars}> is trueish then non-scalar values will be
+stored as well. Default is to only store scalar values.
+
+The following keys will never be saved: C<login>, C<password>,
+C<stylesheet>, C<titlebar>, C<version>. Additional keys not to save
+can be given as an array ref in C<$params{skip_keys}>.
+
+Returns the unique key under which the form is stored.
+
+=item <restore_form_from_session $key, %params>
+
+Restores the form from the session into C<$params{form}> (default:
+C<$::form>).
+
+If C<$params{clobber}> is falsish then existing values with the same
+key in C<$params{form}> will not be overwritten. C<$params{clobber}>
+is on by default.
+
+Returns C<$self>.
+
+=back
+
+=head1 BUGS
+
+Nothing here yet.
+
+=head1 AUTHOR
+
+Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
+
+=cut
ERR => [ qw(
ERR_PASSWORD
ERR_BACKEND
+ ERR_USER
) ],
SESSION => [ qw(
SESSION_OK
use constant OK => 0;
use constant ERR_PASSWORD => 1;
+use constant ERR_USER => 2;
use constant ERR_BACKEND => 100;
use constant SESSION_OK => 0;
package SL::Auth::DB;
+use strict;
+
+use Carp;
+
use SL::Auth::Constants qw(:all);
use SL::DBUtils;
-use strict;
-
sub new {
$main::lxdebug->enter_sub();
my $self = shift;
my $login = shift;
my $password = shift;
- my $is_crypted = shift;
my $dbh = $self->{auth}->dbconnect();
my $query = qq|SELECT password FROM auth."user" WHERE login = ?|;
my ($stored_password) = $dbh->selectrow_array($query, undef, $login);
- $password = crypt $password, substr($login, 0, 2) if (!$password || !$is_crypted);
- $stored_password = crypt $stored_password, substr($login, 0, 2) if (!$stored_password);
+ my ($algorithm, $algorithm2);
+
+ # Empty password hashes in the database mean just that -- empty
+ # passwords. Hash it for easier comparison.
+ $stored_password = $self->hash_password(password => $stored_password) unless $stored_password;
+ ($algorithm, $stored_password) = $self->parse_password_entry($stored_password);
+ ($algorithm2, $password) = $self->parse_password_entry($self->hash_password(password => $password, algorithm => $algorithm, login => $login));
$main::lxdebug->leave_sub();
return ERR_BACKEND;
}
- $password = crypt $password, substr($login, 0, 2) if (!$is_crypted);
+ $password = $self->hash_password(password => $password) unless $is_crypted;
do_query($main::form, $dbh, qq|UPDATE auth."user" SET password = ? WHERE login = ?|, $password, $login);
return 1;
}
+sub hash_password {
+ my ($self, %params) = @_;
+
+ if (!$params{algorithm}) {
+ $params{algorithm} = 'SHA1';
+ $params{fallback_algorithm} = 'MD5';
+ }
+
+ if ($params{algorithm} eq 'SHA1') {
+ if (eval { require Digest::SHA1; 1 }) {
+ return '{SHA1}' . Digest::SHA1::sha1_hex($params{password});
+
+ } elsif ($params{fallback_algorithm}) {
+ return $self->hash_password(%params, algorithm => $params{fallback_algorithm});
+
+ } else {
+ die 'Digest::SHA1 not available';
+ }
+
+ } elsif ($params{algorithm} eq 'MD5') {
+ require Digest::MD5;
+ return '{MD5}' . Digest::MD5::md5_hex($params{password});
+
+ } elsif ($params{algorithm} eq 'CRYPT') {
+ return '{CRYPT}' . crypt($params{password}, substr($params{login}, 0, 2));
+
+ } else {
+ croak 'Unsupported hash algorithm ' . $params{algorithm};
+ }
+}
+
+sub parse_password_entry {
+ my ($self, $password) = @_;
+
+ return ($1, $2) if $password =~ m/^\{ ([^\}]+) \} (.+)/x;
+ return ('CRYPT', $password);
+}
+
1;
my %params = ( %locals,
AUTH => $::auth,
+ FLASH => $::form->{FLASH},
FORM => $::form,
LOCALE => $::locale,
LXCONFIG => \%::lx_office_conf,
} elsif ($type eq 'LXOFFICE_AUTH') {
%connect_settings = ( driver => $::myconfig{dbdriver} || 'Pg',
database => $::auth->{DB_config}->{db},
- host => $::auth->{DB_config}->{host},
+ host => $::auth->{DB_config}->{host} || 'localhost',
port => $::auth->{DB_config}->{port} || 5432,
username => $::auth->{DB_config}->{user},
password => $::auth->{DB_config}->{password},
%connect_settings = ( driver => $::myconfig{dbdriver} || 'Pg',
database => $::myconfig{dbname},
- host => $::myconfig{dbhost},
+ host => $::myconfig{dbhost} || 'localhost',
port => $::myconfig{dbport} || 5432,
username => $::myconfig{dbuser},
password => $::myconfig{dbpasswd},
-# This file has been auto-generated only because it didn't exist.
-# Feel free to modify it at will; it will not be overwritten automatically.
-
package SL::DB::Buchungsgruppe;
use strict;
use SL::DB::MetaSetup::Buchungsgruppe;
-
-# Creates get_all, get_all_count, get_all_iterator, delete_all and update_all.
-__PACKAGE__->meta->make_manager_class;
+use SL::DB::Manager::Buchungsgruppe;
sub income_accno_id {
my ($self, $taxzone) = @_;
--- /dev/null
+package SL::DB::Manager::Buchungsgruppe;
+
+use strict;
+
+use SL::DB::Helper::Manager;
+use base qw(SL::DB::Helper::Manager);
+
+use SL::DB::Helper::Sorted;
+
+sub object_class { 'SL::DB::Buchungsgruppe' }
+
+__PACKAGE__->make_manager_methods;
+
+sub _sort_spec {
+ return ( default => [ 'sortkey', 1 ],
+ columns => { SIMPLE => 'ALL',
+ description => 'lower(description)',
+ });
+}
+
+1;
return $self;
}
+sub interface_type {
+ my ($self) = @_;
+ return $self->{interface} eq 'cgi' ? 'CGI' : 'FastCGI';
+}
+
sub pre_request_checks {
if (!$::auth->session_tables_present) {
if ($::form->{script} eq 'admin.pl') {
$::locale = Locale->new($::myconfig{countrycode});
- show_error('login/password_error', 'password') if SL::Auth::OK != $::auth->authenticate($::form->{login}, $::form->{password}, 0);
+ show_error('login/password_error', 'password') if SL::Auth::OK != $::auth->authenticate($::form->{login}, $::form->{password});
$::auth->set_session_value('login', $::form->{login}, 'password', $::form->{password});
$::auth->create_or_refresh_session;
- $::auth->delete_session_value('FLASH')->save_session();
+ $::auth->delete_session_value('FLASH');
delete $::form->{password};
if ($action) {
$::locale = undef;
$::form = undef;
$::myconfig = ();
- Form::disconnect_standard_dbh unless $self->_interface_is_fcgi;
+ Form::disconnect_standard_dbh;
+ $::auth->expire_session_keys->save_session;
+ $::auth->dbdisconnect;
$::lxdebug->end_request;
$::lxdebug->leave_sub;
my $self = shift;
my $input = shift;
+ my $uploads = {};
if (!$ENV{'CONTENT_TYPE'}
|| ($ENV{'CONTENT_TYPE'} !~ /multipart\/form-data\s*;\s*boundary\s*=\s*(.+)$/)) {
$self->_input_to_hash($input);
$main::lxdebug->leave_sub(2);
- return;
+ return $uploads;
}
my ($name, $filename, $headers_done, $content_type, $boundary_found, $need_cr, $previous);
substr $line, $-[0], $+[0] - $-[0], "";
}
- $previous = $self->_store_value($name, '') if ($name);
+ $previous = _store_value($uploads, $name, '') if ($name);
$self->{FILENAME} = $filename if ($filename);
next;
${ $previous } =~ s|\r?\n$|| if $previous;
$main::lxdebug->leave_sub(2);
+
+ return $uploads;
}
sub _recode_recursively {
$self->_input_to_hash($ENV{QUERY_STRING}) if $ENV{QUERY_STRING};
$self->_input_to_hash($ARGV[0]) if @ARGV && $ARGV[0];
+ my $uploads;
if ($ENV{CONTENT_LENGTH}) {
my $content;
read STDIN, $content, $ENV{CONTENT_LENGTH};
- $self->_request_to_hash($content);
+ $uploads = $self->_request_to_hash($content);
}
my $db_charset = $::lx_office_conf{system}->{dbcharset};
_recode_recursively(SL::Iconv->new($encoding, $db_charset), $self);
+ map { $self->{$_} = $uploads->{$_} } keys %{ $uploads } if $uploads;
+
#$self->{version} = "2.6.1"; # Old hardcoded but secure style
open VERSION_FILE, "VERSION"; # New but flexible code reads version from VERSION-file
$self->{version} = <VERSION_FILE>;
$additional_params->{"conf_latex_templates"} = $::lx_office_conf{print_templates}->{latex};
$additional_params->{"conf_opendocument_templates"} = $::lx_office_conf{print_templates}->{opendocument};
$additional_params->{"conf_vertreter"} = $::lx_office_conf{system}->{vertreter};
- $additional_params->{"conf_show_best_before"} = $::lx_office_conf{system}->{show_best_before};
+ $additional_params->{"conf_show_best_before"} = $::lx_office_conf{features}->{show_best_before};
$additional_params->{"conf_parts_image_css"} = $::lx_office_conf{features}->{parts_image_css};
$additional_params->{"conf_parts_listing_images"} = $::lx_office_conf{features}->{parts_listing_images};
$additional_params->{"conf_parts_show_image"} = $::lx_office_conf{features}->{parts_show_image};
}
sub flash_later {
- $::auth->set_session_value(FLASH => _store_flash($::auth->get_session_value('FLASH'), @_))->save_session();
+ $::auth->set_session_value(FLASH => _store_flash($::auth->get_session_value('FLASH'), @_));
}
sub render_flash {
my $value_title_sub = $options{value_title_sub};
- my %selected = map { ( $_ => 1 ) } @{ ref($options{default}) eq 'ARRAY' ? $options{default} : $options{default} ? [ $options{default} ] : [] };
+ my %selected = map { ( $_ => 1 ) } @{ ref($options{default}) eq 'ARRAY' ? $options{default} : defined($options{default}) ? [ $options{default} ] : [] };
my $access = sub {
my ($element, $index, $key, $sub) = @_;
my $code = '';
foreach my $result (@elements) {
my %attributes = ( value => $result->[0] );
- $attributes{selected} = 'selected' if $selected{ $result->[0] || '' };
+ $attributes{selected} = 'selected' if $selected{ defined($result->[0]) ? $result->[0] : '' };
$code .= $self->html_tag('option', _H($result->[1]), %attributes);
}
use DBI;
use CGI;
+use Encode;
use English qw(-no_match_vars);
use Fcntl;
use File::Copy;
$form->{favicon} = "favicon.ico";
if ($form->{action}) {
- if ($auth->authenticate_root($form->{rpw}, 0) != $auth->OK()) {
+ if ($auth->authenticate_root($form->{rpw}) != $auth->OK()) {
$form->{error_message} = $locale->text('Incorrect Password!');
adminlogin();
} else {
delete $members{"root login"};
- map { $_->{templates} =~ s|.*/||; } values %members;
+ for (values %members) {
+ $_->{templates} =~ s|.*/||;
+ $_->{login_url} = $::locale->is_utf8 ? Encode::encode('utf-8-strict', $_->{login}) : $_->{login_url};
+ }
$form->{title} = "Lx-Office ERP " . $locale->text('Administration');
$form->{LOCKED} = -e _nologin_file_name();
my $pinfo = $part_info_map{$request->{parts_id}};
my $binfo = $bin_info_map{$request->{bin_id}};
- if ($::lx_office_conf{system}->{show_best_before}) {
+ if ($::lx_office_conf{features}->{show_best_before}) {
push @{ $form->{ERRORS} }, $locale->text("There is not enough available of '#1' at warehouse '#2', bin '#3', #4, #5, for the transfer of #6.",
$pinfo->{description},
$binfo->{warehouse_description},
$form->{draft_description} = $description;
$form->{remove_draft} = 'checked';
}
-
+ # Ich vergesse bei Rechnungsentwürfe das Rechnungsdatum zu ändern. Dadurch entstehen
+ # ungültige Belege. Vielleicht geht es anderen ähnlich jan 19.2.2011
+ $form->{invdate} = $form->current_date(\%myconfig); # Aktuelles Rechnungsdatum ...
+ $form->{duedate} = $form->current_date(\%myconfig); # Aktuelles Fälligkeitsdatum ...
update();
$main::lxdebug->leave_sub();
$auth->assert('part_service_assembly_edit');
- my ($parts_id, %newform, $previousform, $amount, $callback);
+ my ($parts_id, %newform, $amount, $callback);
# check if there is a part number - commented out, cause there is an automatic allocation of numbers
# $form->isblank("partnumber", $locale->text(ucfirst $form->{item}." Part Number missing!"));
# save the new form variables before splitting previousform
map { $newform{$_} = $form->{$_} } keys %$form;
- $previousform = $form->unescape($form->{previousform});
-
# don't trample on previous variables
map { delete $form->{$_} } keys %newform;
my $ic_cvar_configs = CVar->get_configs(module => 'IC');
my @ic_cvar_fields = map { "cvar_$_->{name}" } @{ $ic_cvar_configs };
- # now take it apart and restore original values
- foreach my $item (split /&/, $previousform) {
- my ($key, $value) = split m/=/, $item, 2;
- $value =~ s/%26/&/g;
- $form->{$key} = $value;
- }
+ # restore original values
+ $::auth->restore_form_from_session($newform{previousform}, form => $form);
$form->{taxaccounts} = $newform{taxaccount2};
if ($form->{item} eq 'assembly') {
$form->{old_callback} = $form->escape($form->{callback}, 1);
$form->{callback} = $form->escape("$form->{script}?action=display_form", 1);
- # save all form variables except action in a previousform variable
- my $previousform = join '&', map { my $value = $form->{$_}; $value =~ s/&/%26/; "$_=$value" } grep { !/action/ } keys %$form;
+ # save all form variables except action in the session and keep the key in the previousform variable
+ my $previousform = $::auth->save_form_in_session(skip_keys => [ qw(action) ]);
my @HIDDENS;
- push @HIDDENS, { 'name' => 'previousform', 'value' => $form->escape($previousform, 1) };
+ push @HIDDENS, { 'name' => 'previousform', 'value' => $previousform };
push @HIDDENS, map +{ 'name' => $_, 'value' => $form->{$_} }, qw(rowcount vc);
push @HIDDENS, map +{ 'name' => $_, 'value' => $form->{"${_}_$form->{rowcount}"} }, qw(partnumber description unit);
push @HIDDENS, { 'name' => 'taxaccount2', 'value' => $form->{taxaccounts} };
$form->{title} = $locale->text('Add License');
- if (!$::lx_office_conf{system}->{lizenzen}) {
+ if (!$::lx_office_conf{features}->{lizenzen}) {
$form->error(
$locale->text(
'The licensing module has been deactivated in the configuration.')
$form->{title} = $locale->text('Licenses');
- if (!$::lx_office_conf{system}->{lizenzen}) {
+ if (!$::lx_office_conf{features}->{lizenzen}) {
$form->error(
$locale->text(
'The licensing module has been deactivated in the configuration.')
%::myconfig = $auth->read_user($form->{login}) if ($form->{login});
$::locale = Locale->new($::myconfig{countrycode}) if $::myconfig{countrycode};
- if (!$::myconfig{login} || (SL::Auth::OK != $auth->authenticate($form->{login}, $form->{password}, 0))) {
+ if (SL::Auth::OK != $auth->authenticate($::myconfig{login}, $form->{password})) {
$form->{error_message} = $::locale->text('Incorrect username or password!');
login_screen();
} else {
$form->{stylesheet} = $myconfig{stylesheet};
$form->{title} = $::locale->text('Lx-Office');
+ $form->{interface} = $::dispatcher->interface_type;
# create the logo screen
$form->header() unless $form->{noheader};
my $company_given = ($form->{company} ne '')
? qq|<h3>$form->{company}</h3>\n|
- : qq|<a href="am.pl?action=config|
- . qq|&level=Programm--Preferences">|
+ : qq|<a href="am.pl?action=config">|
. $locale->text('No Company Name given') . qq|!</a><br>|;
$form->error($locale->text('The warehouse or the bin is missing.'));
}
- if (!$::lx_office_conf{system}->{show_best_before}) {
+ if (!$::lx_office_conf{features}->{show_best_before}) {
$form->{bestbefore} = '';
}
[system]
# EUR: Einnahmen-Überschussrechnung (net income method). Set this to 1
-# if your company uses the net income method and to 0 for balacing.
+# if your company uses the net income method and to 0 for balancing.
eur = 1
# Set language for login and admin forms. Currently "de" (German),
--- /dev/null
+am.pl
\ No newline at end of file
border-style:solid;
border-width:thin;
}
+.message_error_label {
+ font-size: 0.8em;
+ padding:5px;
+ background-color: #FEE;
+ font-weight:normal;
+ text-align:left;
+ border-style:solid;
+ border-width:thin;
+}
/*
Überschriftsbalken
# Zugriff über cgi
Alias /web/path/to/lx-office-erp /path/to/lx-office-erp
- # Zugriff mit mod_fastcgi:
+ # Zugriff mit mod_fcgid:
AliasMatch ^/web/path/to/lx-office-erp-fcgid/[^/]+\.pl /path/to/lx-office-erp/dispatcher.fcgi
Alias /web/path/to/lx-office-erp-fcgid/ /path/to/lx-office-erp/
Alias /web/path/to/lx-office-erp-fastcgi/ /path/to/lx-office-erp/
Dann ist unter C</web/path/to/lx-office-erp/> die normale Version erreichbar,
-und unter C</web/opath/to/lx-office-erp-fcgid/> bzw.
-C</web/opath/to/lx-office-erp-fastcgi/> die FastCGI Version.
+und unter C</web/path/to/lx-office-erp-fcgid/> bzw.
+C</web/path/to/lx-office-erp-fastcgi/> die FastCGI Version.
Achtung:
=head2 Entwicklungsaspekte
Wenn Änderungen in der Konfiguration von Lx-Office gemacht werden, muss der
-Server neu gestartet werden.
+Webserver neu gestartet werden.
Bei der Entwicklung für FastCGI ist auf ein paar Fallstricke zu achten. Dadurch
dass das Programm in einer Endlosschleife läuft, müssen folgende Aspekte
kanonischen globalen Variablen listet und erklärt. Bitte keine anderen
einführen ohne das sauber zu dokumentieren.
-Datenbankverbindungen wird noch ein Guide verfasst werden, wie man sichergeht,
+Datenbankverbindungen wird noch ein Guide verfasst werden, wie man sicher geht,
dass man die richtige erwischt.
=head2 Performance und Statistiken
# Veränderungen von Lx-Office ERP #
###################################
+ ab Feb 2011 Release 2.7-unstable
+
+
+
+ Kleinere neue Features und Detailverbesserungen:
+
+ - Beim Laden von Rechnungsentwürfen, das Fälligkeits- und Rechnungsdatum duch
+ das Tagesdatum ersetzen
+
+
2011-02-02 - Release 2.6.2
Größere neue Features:
<td><code>serialnumber</code></td>
<td>Seriennummer</td>
</tr>
+ <tr>
+ <td><code>tax_rate</code></td>
+ <td>Steuersatz</td>
+ </tr>
<tr>
<td><code>transdate_oe</code></td>
<td>Auftragsdatum des Originalauftrags, wenn die Rechnung aus einem Sammelauftrag erstellt wurde</td>
'Warning' => 'Warnung',
'Warnings during template upgrade' => 'Warnungen bei Aktualisierung der Dokumentenvorlagen',
'WebDAV link' => 'WebDAV-Link',
+ 'Webserver interface' => 'Webserverschnittstelle',
'Weight' => 'Gewicht',
'Weight unit' => 'Gewichtseinheit',
'What <b>term</b> you are looking for?' => 'Nach welchem <b>Begriff</b> wollen Sie suchen?',
'Warning' => 'Warnung',
'Warnings during template upgrade' => 'Warnungen bei Aktualisierung der Dokumentenvorlagen',
'WebDAV link' => 'WebDAV-Link',
+ 'Webserver interface' => 'Webserver nutzt',
'Weight' => 'Gewicht',
'Weight unit' => 'Gewichtseinheit',
'What <b>term</b> you are looking for?' => 'Nach welchem <b>Begriff</b> wollen Sie suchen?',
'Warning' => '',
'Warnings during template upgrade' => '',
'WebDAV link' => '',
+ 'Webserver interface' => '',
'Weight' => '',
'Weight unit' => '',
'What <b>term</b> you are looking for?' => '',
[% FOREACH row = MEMBERS %]
<tr class="listrow[% loop.count % 2 %]">
- <td> <a href="admin.pl?action=edit&login=[% HTML.url(row.login) %]">[% HTML.escape(row.login) %]</a></td>
+ <td> <a href="admin.pl?action=edit&login=[% HTML.url(row.login_url) %]">[% HTML.escape(row.login) %]</a></td>
<td> [% HTML.escape(row.name) %]</td>
<td> [% HTML.escape(row.company) %]</td>
<td> [% HTML.escape(row.templates) %]</td>
[% USE HTML %]<body>
<div class="message_error">[% IF title_error %][% title_error %][% ELSE %][% 'Error!' | $T8 %][% END %]
- <p>[% label_error %]</p>
+ <p class="message_error_label">[% label_error %]</p>
</div>
<p style="text-align: left;"><input type="button" class="submit" onclick="history.back()" value="[% 'Back' | $T8 %]"></p>
[%- IF SHOW_BACK_BUTTON %]
<form>
<p>
- <!--- show back button always hack
+ <!--- TODO: show back button always hack
In which situation is it necessary to hide it?
<input type="button" onclick="history.back()" value="[% 'Back' | $T8 %]">
-->
<table border="0">
<tr>
- <th align="left"><a href="am.pl?action=config&level=Program--Preferences" title="[% 'Preferences' | $T8 %]">[% 'User' | $T8 %]</a></th>
+ <th align="left"><a href="am.pl?action=config" title="[% 'Preferences' | $T8 %]">[% 'User' | $T8 %]</a></th>
<td>[% HTML.escape(myconfig_name) %]</td>
</tr>
<tr>
- <th align="left"><a href="am.pl?action=config&level=Program--Preferences" title="[% 'Preferences' | $T8 %]">[% 'Language' | $T8 %]</a></th>
+ <th align="left"><a href="am.pl?action=config" title="[% 'Preferences' | $T8 %]">[% 'Language' | $T8 %]</a></th>
<td>[% HTML.escape(myconfig_countrycode) %]</td>
</tr>
<tr>
<th align="left">[% 'Database Host' | $T8 %]</th>
<td>[% HTML.escape(myconfig_dbhost) %]</td>
</tr>
+ <tr>
+ <th align="left">[% 'Webserver interface' | $T8 %]</th>
+ <td>[% HTML.escape(interface) %]</td>
+ </tr>
<tr class="nomobile">
<th align="left">[% 'Lx-Office Homepage' | $T8 %]:</th>
<td><a href="http://lx-office.org" target="_blank" title="[% 'Lx-Office Homepage' | $T8 %]">http://lx-office.org</a></td>