Merge branch 'master' of git@vc.linet-services.de:public/lx-office-erp
authorHolger Lindemann <hli@lenny.hoch.ul>
Wed, 15 Feb 2012 11:17:18 +0000 (12:17 +0100)
committerHolger Lindemann <hli@lenny.hoch.ul>
Wed, 15 Feb 2012 11:17:18 +0000 (12:17 +0100)
40 files changed:
SL/Auth.pm
SL/Controller/Base.pm
SL/Controller/Employee.pm [new file with mode: 0644]
SL/DB/Employee.pm
SL/DB/Helper/Attr.pm
SL/Helper/Flash.pm
SL/RP.pm
bin/mozilla/amtemplates.pl
bin/mozilla/ar.pl
bin/mozilla/ct.pl
bin/mozilla/dn.pl
bin/mozilla/do.pl
bin/mozilla/fu.pl
bin/mozilla/gl.pl
bin/mozilla/ir.pl
bin/mozilla/is.pl
bin/mozilla/oe.pl
doc/UPGRADE
doc/release_management.txt
locale/de/all
menu.ini
t/007broken_links.t
t/helper/attr.t
templates/webpages/ar/search.html
templates/webpages/ct/form_header.html
templates/webpages/do/form_header.html
templates/webpages/do/search.html
templates/webpages/dunning/search.html
templates/webpages/dunning/show_dunning_bottom.html
templates/webpages/employee/_form.html [new file with mode: 0644]
templates/webpages/employee/_list.html [new file with mode: 0644]
templates/webpages/employee/edit.html [new file with mode: 0644]
templates/webpages/employee/list.html [new file with mode: 0644]
templates/webpages/gl/search.html
templates/webpages/ir/form_header.html
templates/webpages/is/form_header.html
templates/webpages/menu/menunew.html
templates/webpages/oe/form_header.html
templates/webpages/oe/search.html
templates/webpages/projects/project_form.html

index 870ead1..d855268 100644 (file)
@@ -979,6 +979,7 @@ sub all_rights_full {
     ["--others",                       $locale->text("Others")],
     ["email_bcc",                      $locale->text("May set the BCC field when sending emails")],
     ["config",                         $locale->text("Change Lx-Office installation settings (all menu entries beneath 'System')")],
+    ["admin",                          $locale->text("Administration (Used to access instance administration from user logins)")],
     );
 
   return @all_rights;
index 6d94347..285bfce 100644 (file)
@@ -30,6 +30,11 @@ sub redirect_to {
   my $self = shift;
   my $url  = $self->url_for(@_);
 
+  if ($self->delay_flash_on_redirect) {
+    require SL::Helper::Flash;
+    SL::Helper::Flash::delay_flash();
+  }
+
   print $::request->{cgi}->redirect($url);
 }
 
@@ -152,6 +157,14 @@ sub _run_hooks {
   }
 }
 
+#
+#  behaviour. override these
+#
+
+sub delay_flash_on_redirect {
+  0;
+}
+
 #
 # private functions -- for use in Base only
 #
@@ -482,6 +495,12 @@ action.
 
 The hook's return values are discarded.
 
+=item delay_flash_on_redirect
+
+May be overridden by a controller. If this method returns true, redirect_to
+will delay all flash messages for the current request. Defaults to false for
+compatibility reasons.
+
 =back
 
 =head2 PRIVATE FUNCTIONS
diff --git a/SL/Controller/Employee.pm b/SL/Controller/Employee.pm
new file mode 100644 (file)
index 0000000..265d1db
--- /dev/null
@@ -0,0 +1,71 @@
+package SL::Controller::Employee;
+
+use strict;
+use parent qw(SL::Controller::Base);
+
+use SL::DB::Employee;
+use SL::Helper::Flash;
+
+__PACKAGE__->run_before('check_auth');
+__PACKAGE__->run_before('load_all');
+__PACKAGE__->run_before('load_from_form');
+__PACKAGE__->run_before('assign_from_form');
+
+our @updatable_columns = qw(deleted);
+
+sub action_list {
+  my ($self, %params) = @_;
+
+  $self->render('employee/list', title => $::locale->text('Employees'));
+}
+
+sub action_edit {
+  my ($self, %params) = @_;
+
+  if ($self->{employee}) {
+    $self->render('employee/edit', title => $::locale->text('Edit Employee #1', $self->{employee}->safe_name));
+  } else {
+    flash('error', $::locale->text('Could not load employee'));
+    $self->redirect_to(action => 'list');
+  }
+}
+
+sub action_save {
+  my ($self, %params) = @_;
+
+  $self->{employee}->save;
+
+  flash('info', $::locale->text('Employee #1 saved!'));
+
+  $self->redirect_to(action => 'edit', 'employee.id' => $self->{employee}->id);
+}
+
+#################### private stuff ##########################
+
+sub check_auth {
+  $::auth->assert('admin');
+}
+
+sub load_all {
+  $_[0]{employees} = SL::DB::Manager::Employee->get_all;
+}
+
+sub load_from_form {
+  $_[0]{employee} = SL::DB::Manager::Employee->find_by(id => delete $::form->{employee}{id});
+}
+
+sub assign_from_form {
+  my %data = %{ $::form->{employee} || {} };
+
+  return 1 unless keys %data;
+
+  $_[0]{employee}->assign_attributes(map { $_ => $data{$_} } @updatable_columns);
+  return 1;
+}
+
+
+######################## behaviour ##########################
+
+sub delay_flash_on_redirect { 1 }
+
+1;
index 8688e82..9244455 100644 (file)
@@ -12,4 +12,10 @@ sub has_right {
   return $::auth->check_right($self->login, $right);
 }
 
+sub safe_name {
+  my ($self) = @_;
+
+  return $self->name || $self->login;
+}
+
 1;
index 94b53dd..f7fabfe 100644 (file)
@@ -33,6 +33,7 @@ sub _make_by_type {
   _as_percent($package, $name, places =>  2) if $type =~ /numeric | real | float/xi;
   _as_number ($package, $name, places =>  0) if $type =~ /int/xi;
   _as_date   ($package, $name)               if $type =~ /date | timestamp/xi;
+  _as_bool_yn($package, $name)               if $type =~ /bool/xi;
 }
 
 sub _as_number {
@@ -104,6 +105,23 @@ sub _as_date {
   return 1;
 }
 
+sub _as_bool_yn {
+  my ($package, $attribute, %params) = @_;
+
+  no strict 'refs';
+  *{ $package . '::' . $attribute . '_as_bool_yn' } = sub {
+    my ($self) = @_;
+
+    if (@_ > 1) {
+      die 'not an accessor';
+    }
+
+    return !defined $self->$attribute ? ''
+         :          $self->$attribute ? $::locale->text('Yes')
+         :                              $::locale->text('No');
+  }
+}
+
 1;
 
 
index 78a94e3..07b22c0 100644 (file)
@@ -5,7 +5,12 @@ use strict;
 require Exporter;
 our @ISA       = qw(Exporter);
 our @EXPORT    = qw(flash flash_later);
-our @EXPORT_OK = qw(render_flash);
+our @EXPORT_OK = qw(render_flash delay_flash);
+
+my %valid_categories = (
+  map({$_ => 'info'} qw(information message)),
+  map({$_ => $_}     qw(info error warning)),
+);
 
 #
 # public functions
@@ -19,6 +24,11 @@ sub flash_later {
   $::auth->set_session_value({ key => "FLASH", value => _store_flash($::auth->get_session_value('FLASH'), @_), auto_restore => 1 });
 }
 
+sub delay_flash {
+  my $store = $::form->{FLASH} || { };
+  flash_later($_ => @{ $store->{$_} || [] }) for keys %$store;
+}
+
 sub render_flash {
   return $::form->parse_html_template('common/flash');
 }
@@ -29,16 +39,24 @@ sub render_flash {
 
 sub _store_flash {
   my $store    = shift || { };
-  my $category = shift;
-  $category    = 'info' if $category eq 'information';
+  my $category = _check_category(+shift);
 
-  $store                ||= { };
   $store->{ $category } ||= [ ];
   push @{ $store->{ $category } }, @_;
 
   return $store;
 }
 
+sub _check_category {
+  my ($c) = @_;
+  return $valid_categories{$c}
+    ||  do {
+      require Carp;
+      Carp->import;
+      croak("invalid category '$c' for flash");
+    };
+}
+
 1;
 
 __END__
@@ -95,6 +113,13 @@ file.
 
 This function is not exported by default.
 
+=item C<delay_flash>
+
+Delays flash, as if all flash messages in this request would have been
+C<flash_later>
+
+Not exported by default.
+
 =back
 
 =head1 AUTHOR
index 072c54b..4157d45 100644 (file)
--- a/SL/RP.pm
+++ b/SL/RP.pm
@@ -213,7 +213,7 @@ sub get_accounts {
 
   # if l_ob is selected l_cb is always ignored
   if ( $form->{l_ob} ) {
-    $where .= ' AND ac.ob_transaction is true  ' 
+    $where .= ' AND ac.ob_transaction is true  '
   } elsif ( not $form->{l_cb} ) {
     $where .= ' AND ac.cb_transaction is false ';
   };
@@ -1237,7 +1237,7 @@ sub aging {
     WHERE ((paid != amount) OR (datepaid > (date $todate) AND datepaid is not null))
       AND NOT COALESCE (${arap}.storno, 'f')
       AND (${arap}.${ct}_id = ${ct}.id)
-      $where_dpt 
+      $where_dpt
       AND (${ct}.id = ?)
       AND (transdate <= (date $todate) $fromwhere )
       $review_of_aging_list
index 1545f65..4bc6f01 100644 (file)
@@ -63,7 +63,7 @@ sub display_template {
 
   my $form     = $main::form;
 
-  $main::auth->assert('config');
+  $main::auth->assert('admin');
 
   $form->{edit} = 0;
   display_template_form();
@@ -76,7 +76,7 @@ sub edit_template {
 
   my $form     = $main::form;
 
-  $main::auth->assert('config');
+  $main::auth->assert('admin');
 
   $form->{edit} = 1;
   display_template_form();
@@ -91,7 +91,7 @@ sub save_template {
   my %myconfig = %main::myconfig;
   my $locale   = $main::locale;
 
-  $main::auth->assert('config');
+  $main::auth->assert('admin');
 
   $form->isblank("formname", $locale->text("You're not editing a file.")) unless ($form->{type} eq "stylesheet");
 
@@ -113,7 +113,7 @@ sub display_template_form {
   my %myconfig = %main::myconfig;
   my $locale   = $main::locale;
 
-  $main::auth->assert('config');
+  $main::auth->assert('admin');
 
   if ($form->{"formname"} =~ m|\.\.| || $form->{"formname"} =~ m|^/|) {
     $form->{"formname"} =~ s|.*/||;
index a6e1220..cd30053 100644 (file)
@@ -1346,16 +1346,13 @@ sub search {
   $form->get_lists("projects"       => { "key" => "ALL_PROJECTS", "all" => 1 },
                    "departments"    => "ALL_DEPARTMENTS",
                    "customers"      => "ALL_VC",
-                   "employees"    => "ALL_EMPLOYEES",
-                   "salesmen"     => "ALL_SALESMEN",
                    "business_types" => "ALL_BUSINESS_TYPES");
+  $form->{ALL_EMPLOYEES} = SL::DB::Manager::Employee->get_all(query => [ deleted => 0 ]);
   $form->{SHOW_BUSINESS_TYPES} = scalar @{ $form->{ALL_BUSINESS_TYPES} } > 0;
 
   # constants and subs for template
   $form->{jsscript}  = 1;
   $form->{vc_keys}   = sub { "$_[0]->{name}--$_[0]->{id}" };
-  $form->{employee_labels} = sub { $_[0]->{"name"} || $_[0]->{"login"} };
-  $form->{salesman_labels} = $form->{employee_labels};
 
   $form->header;
   print $form->parse_html_template('ar/search', { %myconfig });
index b525535..2eb6c24 100644 (file)
@@ -314,17 +314,17 @@ sub form_header {
   my %myconfig = %main::myconfig;
   my $locale   = $main::locale;
 
-  $form->get_lists(employees  => "ALL_EMPLOYEES",
-                   taxzones   => "ALL_TAXZONES",
+  $form->get_lists(taxzones   => "ALL_TAXZONES",
                    currencies => "ALL_CURRENCIES");
   $form->get_pricegroup(\%myconfig, { all => 1 });
 
   $form->get_lists(customers => { key => "ALL_SALESMAN_CUSTOMERS", business_is_salesman => 1 }) if $::lx_office_conf{features}->{vertreter};
+  $form->{ALL_EMPLOYEES}          = SL::DB::Manager::Employee->get_all(query => [ or => [ id => $::form->{FU_created_for_user},  deleted => 0 ] ]);
+  $form->{ALL_SALESMEN}           = SL::DB::Manager::Employee->get_all(query => [ or => [ id => $::form->{salesman_id},  deleted => 0 ] ]);
+  $form->{USER}                   = SL::DB::Manager::Employee->current;
 
-  $form->{ALL_SALESMEN}   = $form->{ALL_EMPLOYEES};
   $form->{taxincluded}    = ($form->{taxincluded}) ? "checked" : "";
   $form->{is_customer}    = $form->{db}     eq 'customer';
-  $form->{salesman_label} = sub { $_[0]->{name} ne "" ? $_[0]->{name} : $_[0]->{login} };
   $form->{shipto_label}   = \&_shipto_label;
   $form->{contacts_label} = \&_contacts_label;
   $form->{taxzone_id}     = 0                                                               if !$form->{id};
index 4b6210b..07b1000 100644 (file)
@@ -301,8 +301,8 @@ sub search {
   $main::auth->assert('dunning_edit');
 
   $form->get_lists("customers"   => "ALL_CUSTOMERS",
-                   "departments" => "ALL_DEPARTMENTS",
-                   "salesmen"     => "ALL_SALESMEN");
+                   "departments" => "ALL_DEPARTMENTS");
+  $form->{ALL_EMPLOYEES} = SL::DB::Manager::Employee->get_all(query => [ deleted => 0 ]);
 
   DN->get_config(\%myconfig, \%$form);
 
@@ -313,7 +313,6 @@ sub search {
   $form->{jsscript} = 1;
   $form->{title}    = $locale->text('Dunnings');
   $form->{fokus}    = "search.customer";
-  $form->{salesman_labels} = sub { $_[0]->{"name"} || $_[0]->{"login"} };
 
   $form->header();
 
index 39cba4a..e16286d 100644 (file)
@@ -272,13 +272,14 @@ sub form_header {
                      "all"          => 0,
                      "old_id"       => \@old_project_ids
                    },
-                   "employees"      => "ALL_EMPLOYEES",
-                   "salesmen"       => "ALL_SALESMEN",
                    $vc              => "ALL_VC",
                    "price_factors"  => "ALL_PRICE_FACTORS",
                    "departments"    => "ALL_DEPARTMENTS",
                    "business_types" => "ALL_BUSINESS_TYPES",
     );
+
+  $::form->{ALL_EMPLOYEES}         = SL::DB::Manager::Employee->get_all(query => [ or => [ id => $::form->{employee_id},  deleted => 0 ] ]);
+  $::form->{ALL_SALESMEN}          = SL::DB::Manager::Employee->get_all(query => [ or => [ id => $::form->{salesman_id},  deleted => 0 ] ]);
   $::form->{ALL_CONTACTS}          = SL::DB::Manager::Contact->get_all(query => [
     or => [
       cp_cv_id => $::form->{"$::form->{vc}_id"} * 1,
@@ -448,9 +449,8 @@ sub search {
 
   $form->get_lists("projects"     => { "key" => "ALL_PROJECTS",
                                        "all" => 1 },
-                   "employees"    => "ALL_EMPLOYEES",
-                   "salesmen"     => "ALL_SALESMEN",
                    "$form->{vc}s" => "ALL_VC");
+  $form->{ALL_EMPLOYEES} = SL::DB::Manager::Employee->get_all(query => [ deleted => 0 ]);
 
   $form->{SHOW_VC_DROP_DOWN} =  $myconfig{vclimit} > scalar @{ $form->{ALL_VC} };
   $form->{jsscript}          = 1;
index 8870c2b..b2e6037 100644 (file)
@@ -384,7 +384,7 @@ sub edit_access_rights {
 
   my $access = FU->retrieve_access_rights();
 
-  $form->get_lists("employees" => "EMPLOYEES");
+  $form->{EMPLOYEES} = SL::DB::Manager::Employee->get_all(query => [ deleted => 0 ]);
 
   map { $_->{access} = $access->{$_->{id}} } @{ $form->{EMPLOYEES} };
 
index a4f2d1d..ce1d1a9 100644 (file)
@@ -217,8 +217,8 @@ sub search {
   $::form->all_departments(\%::myconfig);
   $::form->get_lists(
     projects  => { key => "ALL_PROJECTS", all => 1 },
-    employees => "ALL_EMPLOYEES",
   );
+  $::form->{ALL_EMPLOYEES} = SL::DB::Manager::Employee->get_all(query => [ deleted => 0 ]);
 
   my $onload = "focus()"
              . qq|;setupDateFormat('|. $::myconfig{dateformat} . qq|', '| . $::locale->text("Falsches Datumsformat!") . qq|')|
index 810e06a..cd4591d 100644 (file)
@@ -287,13 +287,13 @@ sub form_header {
                    "projects"      => { "key"    => "ALL_PROJECTS",
                                         "all"    => 0,
                                         "old_id" => \@old_project_ids },
-                   "employees"     => "ALL_EMPLOYEES",
                    "taxzones"      => "ALL_TAXZONES",
                    "currencies"    => "ALL_CURRENCIES",
                    "vendors"       => "ALL_VENDORS",
                    "departments"   => "all_departments",
                    "price_factors" => "ALL_PRICE_FACTORS");
 
+  $TMPL_VAR{ALL_EMPLOYEES}         = SL::DB::Manager::Employee->get_all(query => [ or => [ id => $::form->{employee_id},  deleted => 0 ] ]);
   $TMPL_VAR{ALL_CONTACTS}          = SL::DB::Manager::Contact->get_all(query => [
     or => [
       cp_cv_id => $::form->{"$::form->{vc}_id"} * 1,
@@ -303,8 +303,6 @@ sub form_header {
       ]
     ]
   ]);
-  $TMPL_VAR{sales_employee_labels} = sub { $_[0]->{name} || $_[0]->{login} };
-#  $TMPL_VAR{shipto_labels}         = sub { join "; ", grep { $_ } map { $_[0]->{"shipto${_}" } } qw(name department_1 street city) };
   $TMPL_VAR{department_labels}     = sub { "$_[0]->{description}--$_[0]->{id}" };
 
   # customer
index 685e7b1..6f433a9 100644 (file)
@@ -304,14 +304,14 @@ sub form_header {
                    "projects"      => { "key"    => "ALL_PROJECTS",
                                         "all"    => 0,
                                         "old_id" => \@old_project_ids },
-                   "employees"     => "ALL_EMPLOYEES",
-                   "salesmen"      => "ALL_SALESMEN",
                    "taxzones"      => "ALL_TAXZONES",
                    "currencies"    => "ALL_CURRENCIES",
                    "customers"     => "ALL_CUSTOMERS",
                    "departments"   => "all_departments",
                    "price_factors" => "ALL_PRICE_FACTORS");
 
+  $TMPL_VAR{ALL_EMPLOYEES}         = SL::DB::Manager::Employee->get_all(query => [ or => [ id => $::form->{employee_id},  deleted => 0 ] ]);
+  $TMPL_VAR{ALL_SALESMEN}          = SL::DB::Manager::Employee->get_all(query => [ or => [ id => $::form->{salesman_id},  deleted => 0 ] ]);
   $TMPL_VAR{ALL_CONTACTS}          = SL::DB::Manager::Contact->get_all(query => [
     or => [
       cp_cv_id => $::form->{"$::form->{vc}_id"} * 1,
@@ -321,7 +321,6 @@ sub form_header {
       ]
     ]
   ]);
-  $TMPL_VAR{sales_employee_labels} = sub { $_[0]->{name} || $_[0]->{login} };
   $TMPL_VAR{shipto_labels}         = sub { join "; ", grep { $_ } map { $_[0]->{"shipto${_}" } } qw(name department_1 street city) };
   $TMPL_VAR{department_labels}     = sub { "$_[0]->{description}--$_[0]->{id}" };
 
index 1123151..3b62071 100644 (file)
@@ -331,8 +331,6 @@ sub form_header {
                    "projects"      => { "key"      => "ALL_PROJECTS",
                                         "all"      => 0,
                                         "old_id"   => \@old_project_ids },
-                   "employees"     => "ALL_EMPLOYEES",
-                   "salesmen"      => "ALL_SALESMEN",
                    "taxzones"      => "ALL_TAXZONES",
                    "payments"      => "ALL_PAYMENTS",
                    "currencies"    => "ALL_CURRENCIES",
@@ -342,6 +340,8 @@ sub form_header {
                    "price_factors" => "ALL_PRICE_FACTORS");
 
   # label subs
+  $TMPL_VAR{ALL_EMPLOYEES}         = SL::DB::Manager::Employee->get_all(query => [ or => [ id => $::form->{employee_id},  deleted => 0 ] ]);
+  $TMPL_VAR{ALL_SALESMEN}          = SL::DB::Manager::Employee->get_all(query => [ or => [ id => $::form->{salesman_id},  deleted => 0 ] ]);
   $TMPL_VAR{ALL_CONTACTS}          = SL::DB::Manager::Contact->get_all(query => [
     or => [
       cp_cv_id => $::form->{"$::form->{vc}_id"} * 1,
@@ -708,16 +708,13 @@ sub search {
   # setup vendor / customer data
   $form->all_vc(\%myconfig, $form->{vc}, ($form->{vc} eq 'customer') ? "AR" : "AP");
   $form->get_lists("projects"     => { "key" => "ALL_PROJECTS", "all" => 1 },
-                   "employees"    => "ALL_EMPLOYEES",
-                   "salesmen"     => "ALL_SALESMEN",
                    "departments"  => "ALL_DEPARTMENTS",
                    "$form->{vc}s" => "ALL_VC");
+  $form->{ALL_EMPLOYEES} = SL::DB::Manager::Employee->get_all(query => [ deleted => 0 ]);
 
   # constants and subs for template
   $form->{jsscript}        = 1;
-  $form->{employee_labels} = sub { $_[0]->{"name"} || $_[0]->{"login"} };
   $form->{vc_keys}         = sub { "$_[0]->{name}--$_[0]->{id}" };
-  $form->{salesman_labels} = $form->{employee_labels};
 
   $form->header();
 
index 2082788..192e0cb 100644 (file)
@@ -7,33 +7,51 @@ Wichtige Hinweise zum Upgrade von Ã¤lteren Versionen
 Upgrade auf v2.7.0
 ==================
 
-1. Templateumstellungen
+* In der Version 2.7.0 wird das XUL Menü entfernt. Alle Benutzer die das XUL
+  Menü noch eingestellt haben, werden beim ersten Einloggen auf ein anderes
+  Menü gesetzt.
 
-Das Druckvorlagensystem wurde umgestellt, dadurch ist der Name "print" für
-Druckvorlagen jetzt reserviert. Wenn eine Ihrer Vorlagensätze "print" heisst,
-benennen Sie ihn um bevor Sie das Update starten.
+* Das Lizenzenfeature wurde ersatzlos entfernt.
 
-2. Neue Abhängigkeiten
+* In den LaTeX Vorlagen wurde der Befehl "pagebreak" und die dazugehörigen
+  "sumcarriedforward" und "lastpage" entfernt. Alle Vorlagen die auf diesen
+  Informationen basieren müssen Ã¼berarbeitet werden. Die Standardvorlagen sind
+  entsprechend angepasst worden, und müssen in der Administration neu angelegt
+  werden.
 
-Wie immer bitte vor dem ersten Aufrufen einmal die Pakete Ã¼berprüfen:
+* Das Druckvorlagensystem wurde umgestellt, dadurch ist der Name "print" für
+  Druckvorlagen jetzt reserviert. Wenn eine Ihrer Vorlagensätze "print" heisst,
+  benennen Sie ihn um bevor Sie das Update starten.
 
-$ scripts/installation_check.pl
+* Neue Abhängigkeiten
 
-Sollten Module als fehlend markiert sein, folgen Sie bitte den Anweisungen in
-der Installationsanweisung.
+  * JSON
+  * String::ShellQuote
+
+  Wie immer bitte vor dem ersten Aufrufen einmal die Pakete Ã¼berprüfen:
+
+  $ scripts/installation_check.pl
 
-Es sind zwei neue Abhängigkeiten dazugekommen.
+* CSV-Import wurde neu in Perl implementiert
 
-* JSON
-* String::ShellQuote
+  Der PHP-Code wurde entfernt. Automatische Skripte, die per Aufruf von
+  lxo-import/partsB.php?cron=1 die Datei parts.csv importiert haben,
+  funktionieren nicht mehr.  Stattdessen kann scripts/csv-import-from-shell.sh
+  benutzt werden. Im Unterschied zur PHP-Version werden unbekannte Warengruppen
+  nicht mehr automatisch angelegt, stattdessen bricht das Skript ab.
 
-3. CSV-Import wurde neu in Perl implementiert
+* Rechteverwaltung
 
-Der PHP-Code wurde entfernt. Automatische Skripte, die per Aufruf von
-lxo-import/partsB.php?cron=1 die Datei parts.csv importiert haben,
-funktionieren nicht mehr.  Stattdessen kann scripts/csv-import-from-shell.sh
-benutzt werden. Im Unterschied zur PHP-Version werden unbekannte Warengruppen
-nicht mehr automatisch angelegt, stattdessen bricht das Skript ab.
+  * Das Recht "Kunden und Lieferanten bearbeiten" wurde aufgespalten in zwei
+    einzelne Rechte. Ein Updatescript passt bestehende Gruppenaentsprechend an.
+  * Das Recht "Preise nd Rabatte bearbeiten" wurde neu eingeführt und ist
+    notwendig um in Belegen Preise Ã¤ndern zu können. Es wird beim Upgrade
+    automatisch allen Benutzern erteilt.
+  * Das Recht "Administration" wurde neu eingeführt, und ist dazu da
+    administrative Tätigkeiten an der Mandantendatenbank aus einm Benutzerlogin
+    heraus durchzuführen. Es ist standardmäßig NICHT vergeben.
+  * Der Vorlageneditor wurde unter das Recht Administration gestellt, warvorher
+    Konfiguration.
 
 
 Upgrade auf v2.6.3
index 078f92b..ea637d4 100644 (file)
@@ -13,7 +13,7 @@ als freundliche Checkliste zum ausdrucken und erweitern.
 
 * Etwa einen Monat vor dem Release wird eine Beta herausgegeben.
 
-* (TODO: Reease Candidates Zeitplan).
+* (TODO: Release Candidates Zeitplan).
 
 
 
@@ -119,6 +119,11 @@ als freundliche Checkliste zum ausdrucken und erweitern.
   # listet die entsprechenden Diffs:
   $ scripts/rose_auto_create_model.pl --user=<login> --diff -n --all
 
+* Locales auf Vollständigkeit prüfen
+
+  $ scripts/locales.pl de
+  $ scripts/locales.pl de_DE
+
 * SL::DB::Helper::ALL auf Vollständigkeit prüfen
 
   (TODO: Mag da einer ein Script für schreiben?
@@ -192,3 +197,8 @@ als freundliche Checkliste zum ausdrucken und erweitern.
 * Alle Releasemessages von mindestens einer Person Korrektur lesen lassen
 
 * Webseite aktualisieren, Releasemessages auf freshmeat und Mailinglisten posten
+
+
+3. POST RELEASE
+
+* Im Bugzilla die aktuelle Version ergänzen, damit dafür Bugs eingespielt werden können.
index 4228c58..4a921f2 100644 (file)
@@ -152,6 +152,7 @@ $self->{texts} = {
   'Add unit'                    => 'Einheit hinzuf&uuml;gen',
   'Address'                     => 'Adresse',
   'Administration'              => 'Administration',
+  'Administration (Used to access instance administration from user logins)' => 'Administration (Für die Verwaltung der aktuellen Instanz aus einem Userlogin heraus)',
   'Administration area'         => 'Administration',
   'Advance turnover tax return' => 'Umsatzsteuervoranmeldung',
   'Aktion'                      => 'Aktion',
@@ -429,6 +430,7 @@ $self->{texts} = {
   'Corrections'                 => 'Korrekturen',
   'Costs'                       => 'Kosten',
   'Could not copy %s to %s. Reason: %s' => 'Die Datei &quot;%s&quot; konnte nicht nach &quot;%s&quot; kopiert werden. Grund: %s',
+  'Could not load employee'     => 'Konnte Benutzer nicht laden',
   'Could not open the file users/members.' => 'Die Datei &quot;users/members&quot; konnte nicht ge&ouml;ffnet werden.',
   'Could not open the old memberfile.' => 'Die Datei mit den Benutzerdaten konnte nicht ge&ouml;ffnet werden.',
   'Could not print dunning.'    => 'Die Mahnungen konnten nicht gedruckt werden.',
@@ -577,6 +579,7 @@ $self->{texts} = {
   'Delete group'                => 'Gruppe l&ouml;schen',
   'Delete profile'              => 'Profil löschen',
   'Delete transaction'          => 'Buchung löschen',
+  'Deleted'                     => 'Gelöscht',
   'Delivered'                   => 'Geliefert',
   'Delivery Date'               => 'Lieferdatum',
   'Delivery Order'              => 'Lieferschein',
@@ -692,6 +695,7 @@ $self->{texts} = {
   'Edit Customer'               => 'Kunde editieren',
   'Edit Dunning'                => 'Mahnungen konfigurieren',
   'Edit Dunning Process Config' => 'Mahnwesenkonfiguration bearbeiten',
+  'Edit Employee #1'            => 'Benutzer #1 bearbeiten',
   'Edit Follow-Up'              => 'Wiedervorlage bearbeiten',
   'Edit Follow-Up for #1'       => 'Wiedervorlage f&uuml;r #1 bearbeiten',
   'Edit General Ledger Transaction' => 'Buchung im Hauptbuch bearbeiten',
@@ -748,6 +752,8 @@ $self->{texts} = {
   'Either there are no open invoices, or you have already initiated bank transfers with the open amounts for those that are still open.' => 'Entweder gibt es keine offenen Rechnungen, oder es wurden bereits Ãœberweisungen Ã¼ber die offenen Beträge aller offenen Rechnungen erstellt.',
   'Element disabled'            => 'Element deaktiviert',
   'Employee'                    => 'Bearbeiter',
+  'Employee #1 saved!'          => 'Benutzer #1 gespeichert!',
+  'Employees'                   => 'Benutzer',
   'Empty transaction!'          => 'Buchung ist leer!',
   'End date'                    => 'Enddatum',
   'Enter a description for this new draft.' => 'Geben Sie eine Beschreibung f&uuml;r diesen Entwurf ein.',
@@ -1077,6 +1083,7 @@ $self->{texts} = {
   'Login'                       => 'Anmelden',
   'Login Name'                  => 'Benutzer',
   'Login name missing!'         => 'Benutzer - Feld darf nicht leer sein!',
+  'Login of User'               => 'Login',
   'Logout'                      => 'Abmelden',
   'Logout now'                  => 'Lx-Office jetzt verlassen',
   'Long Dates'                  => 'Lange Monatsnamen',
index 4277162..5d39fd6 100644 (file)
--- a/menu.ini
+++ b/menu.ini
@@ -750,6 +750,7 @@ profile.type=parts
 
 
 [System--Templates]
+ACCESS=admin
 module=menu.pl
 action=acc_menu
 target=acc_menu
@@ -790,6 +791,11 @@ action=audit_control
 module=am.pl
 action=show_history_search
 
+[System--Employees]
+ACCESS=admin
+module=controller.pl
+action=Employee/list
+
 
 [Program]
 
index 5ac03bc..93148f4 100644 (file)
@@ -8,9 +8,13 @@
 
 use strict;
 use File::Find;
-use LWP::Simple;
-use Test::More tests => 1;
-use URI::Find;
+use Test::More;
+
+if (eval " use LWP::Simple; use URI::Find; 1 ") {
+  plan tests => 1;
+} else {
+  plan skip_all => "LWP::Simple or URI::Find not installed";
+}
 
 my @fails;
 
index 3fc0607..5d6238e 100644 (file)
@@ -1,4 +1,4 @@
-use Test::More tests => 29;
+use Test::More tests => 32;
 
 use lib 't';
 
@@ -50,3 +50,13 @@ $i->amount(12);
 $i->netamount(10.34);
 is($i->taxamount_as_number, '1,66');
 
+$o->closed(1);
+is $o->closed_as_bool_yn, 'Ja', 'bool 1';
+$o->closed(0);
+is $o->closed_as_bool_yn, 'Nein', 'bool 2';
+
+# undef test: this only works for columns without default, rose will set
+# defaults according to the database
+$i->taxincluded(undef);
+is $i->taxincluded_as_bool_yn, '', 'bool 3';
+
index a70816e..d0f529d 100644 (file)
@@ -1,4 +1,5 @@
 [%- USE T8 %]
+[%- USE L %]
 <body>
 
  <form method=post name="search" action=[% script %]>
      </tr>
      <tr>
       <th align="right">[% 'Employee' | $T8 %]</th>
-      <td>
-             [%- INCLUDE 'generic/multibox.html'
-                  name          = 'employee_id',
-                  style         = 'width: 250px',
-                  DATA          =  ALL_EMPLOYEES,
-                  id_key        = 'id',
-                  label_sub     = 'employee_labels',
-                  limit         = vclimit,
-                  show_empty    = 1,
-                  allow_textbox = 0,
-                  default       = ' ',
-             -%]
-      </td>
+      <td>[% L.select_tag('employee_id', L.options_for_select(ALL_EMPLOYEES, title='safe_name', with_empty=1), style='width:250px') %]</td>
      </tr>
     <tr>
      <th align="right">[% 'Salesman' | $T8 %]</th>
-     <td>
-            [%- INCLUDE 'generic/multibox.html'
-                 name          = 'salesman_id',
-                 style         = 'width: 250px',
-                 DATA          =  ALL_SALESMEN,
-                 id_key        = 'id',
-                 label_sub     = 'salesman_labels',
-                 limit         = vclimit,
-                 show_empty    = 1,
-                 allow_textbox = 0,
-            -%]
-     </td>
+     <td>[% L.select_tag('salesman_id', L.options_for_select(ALL_EMPLOYEES, title='safe_name', with_empty=1), style='width:250px') %]</td>
      </tr>
      <tr>
       <th align=right nowrap>[% 'Transaction description' | $T8 %]</th>
index 4d6ee5a..501ebaa 100644 (file)
       </td>
       [%- IF is_customer && !conf_vertreter %]
       <th align="right">[% 'Salesman' | $T8 %]</th>
-      <td>
-       [%- INCLUDE generic/multibox.html
-             name       = 'salesman_id',
-             DATA       = ALL_SALESMEN,
-             show_empty = 1,
-             id_key     = 'id',
-             label_sub  = 'salesman_label',
-       -%]
-      </td>
+      <td>[% L.select_tag('salesman_id', L.options_for_select(ALL_SALESMEN, default=salesman_id, show_empty=1, title='safe_name')) %]</td>
       [%- END %]
      </tr>
     </table>
         <input name="FU_date" id="FU_date" value="[% HTML.escape(FU_date) %]" size="12">
         <input type="button" name="FU_date_button" id="FU_date_trigger" value="?">
         [% 'for' | $T8 %]
-        <select name="FU_created_for_user">
-         [%- FOREACH row = ALL_EMPLOYEES %]
-         <option value="[% HTML.escape(row.id) %]"[% IF (NOTE_id && (row.id == FU_created_for_user)) || (row.login == login) %] selected[% END %]>
-          [%- IF row.name %][%- HTML.escape(row.name) %] ([% HTML.escape(row.login) %])[% ELSE %][% HTML.escape(row.login) %][% END %]
-         </option>
-         [%- END %]
-        </select>
+        [% L.select_tag('FU_created_for_user', L.options_for_select(ALL_EMPLOYEES, default=(FU_created_for_user ? FU_created_for_user : USER.id), title='safe_name')) %]
        </td>
       </tr>
 
index 3ed1be2..b195637 100644 (file)
          [% IF row.id == employee_id %][%- IF row.name %][%- HTML.escape(row.name) %][%- ELSE %][% HTML.escape(row.login) %][%- END %][% END %]
          [%- END %]
          [%- ELSE %]
-         <select name="employee_id">
-          [%- FOREACH row = ALL_EMPLOYEES %]
-          <option value="[% HTML.escape(row.id) %]"[% IF row.id == employee_id %] selected[% END %]>
-           [%- IF row.name %][%- HTML.escape(row.name) %][%- ELSE %][% HTML.escape(row.login) %][%- END %]
-          </option>
-          [%- END %]
-         </select>
+           [% L.select_tag('employee_id', L.options_for_select(ALL_EMPLOYEES, default=employee_id, title='safe_name')) %]
          [%- END %]
         </td>
        </tr>
          [% IF row.id == the_salesman_id %][%- IF row.name %][%- HTML.escape(row.name) %][%- ELSE %][% HTML.escape(row.login) %][%- END %][% END %]
          [%- END %]
          [%- ELSE %]
-         <select name="salesman_id">
-          [%- FOREACH row = ALL_SALESMEN %]
-          <option value="[% HTML.escape(row.id) %]"[% IF row.id == the_salesman_id %] selected[% END %]>
-           [%- IF row.name %][%- HTML.escape(row.name) %][%- ELSE %][% HTML.escape(row.login) %][%- END %]
-          </option>
-          [%- END %]
-         </select>
+          [% L.select_tag('salesman_id', L.options_for_select(ALL_SALESMEN, default=(salesman_id ? salesman_id : employee_id), title='safe_name')) %]
          [%- END %]
         </td>
        </tr>
index 811384c..b2ad170 100644 (file)
@@ -1,4 +1,5 @@
 [%- USE T8 %]
+[%- USE L %]
 [% USE HTML %][% USE LxERP %]<body onload="on_load();">
 
  [%- IF vc == 'customer' %]
 
     <tr>
      <th align="right">[% 'Employee' | $T8 %]</th>
-     <td>
-      <select name="employee_id" class="fixed_width">
-       <option></option>
-       [%- FOREACH row = ALL_EMPLOYEES %]
-       <option value="[% HTML.escape(row.id) %]">[% IF row.name %][% HTML.escape(row.name) %][% ELSE %][% HTML.escape(row.login) %][% END %]</option>
-       [%- END %]
-      </select>
-     </td>
+     <td>[% L.select_tag('employee_id', L.options_for_select(ALL_EMPLOYEES, title='safe_name', with_empty=1), class='fixed_width') %]</td>
     </tr>
 
     [%- IF is_customer %]
     <tr>
      <th align="right">[% 'Salesman' | $T8 %]</th>
-     <td>
-      <select name="salesman_id" class="fixed_width">
-       <option></option>
-       [%- FOREACH row = ALL_SALESMEN %]
-       <option value="[% HTML.escape(row.id) %]">[% IF row.name %][% HTML.escape(row.name) %][% ELSE %][% HTML.escape(row.login) %][% END %]</option>
-       [%- END %]
-      </select>
-     </td>
+     <td>[% L.select_tag('salesman_id', L.options_for_select(ALL_EMPLOYEES, title='safe_name', with_empty=1), class='fixed_width') %]</td>
     </tr>
     [%- END %]
 
index 6611df2..c95829c 100644 (file)
@@ -1,5 +1,7 @@
 [%- USE T8 %]
-[% USE HTML %]<body onLoad="[% onload %]">
+[%- USE HTML %]
+[%- USE L %]
+<body onLoad="[% onload %]">
 
  <script type="text/javascript" src="js/common.js"></script>
 
       </tr>
       <tr>
        <th align="right">[% 'Salesman' | $T8 %]</th>
-       <td>
-        [%- INCLUDE 'generic/multibox.html'
-            name          = 'salesman_id',
-            style         = 'width: 250px',
-            DATA          =  ALL_SALESMEN,
-            id_key        = 'id',
-            label_sub     = 'salesman_labels',
-            limit         = vclimit,
-            show_empty    = 1,
-            allow_textbox = 0,
-         -%]
-       </td>
+       <td>[% L.select_tag('salesman_id', L.options_for_select(ALL_EMPLOYEES, title='safe_name', with_empty=1), style='width:250px') %]</td>
       </tr>
      </table>
     </td>
index f3eca31..2f05d15 100644 (file)
@@ -3,7 +3,7 @@
 
   <p>
     <input type="checkbox" name="force_lang" size="6" value="1" onclick="document.getElementsByName('language_id')[0].disabled = !document.getElementsByName('force_lang')[0].checked;">
-    [% 'Override invoice language' | T8 %]
+    [% 'Override invoice language' | $T8 %]
     [% PRINT_OPTIONS %]
   </p>
 
diff --git a/templates/webpages/employee/_form.html b/templates/webpages/employee/_form.html
new file mode 100644 (file)
index 0000000..841cdc5
--- /dev/null
@@ -0,0 +1,29 @@
+[%- USE HTML %]
+[%- USE LxERP %]
+[%- USE T8 %]
+[%- USE L %]
+
+<form action='controller.pl' method='POST'>
+
+<table>
+<tr>
+ <td align='right' class=''>[% 'Login of User' | $T8 %]:</td>
+ <td>[% employee.login | html %]</td>
+</tr>
+<tr>
+ <td align='right'>[% 'Name' | $T8 %]:</td>
+ <td>[% employee.name | html %]</td>
+</tr>
+<tr>
+ <td align='right'>[% 'Deleted' | $T8 %]:</td>
+ <td> [% L.radio_button_tag('employee.deleted', value=1, checked=employee.deleted, label=LxERP.t8('Yes')) %]
+      [% L.radio_button_tag('employee.deleted', value=0, checked=!employee.deleted, label=LxERP.t8('No')) %]
+ </td>
+</tr>
+</table>
+
+[%- L.hidden_tag('employee.id', employee.id) %]
+[%- L.hidden_tag('action',  'Employee/dispatch')  %]
+[%- L.submit_tag('action_save',  LxERP.t8('Save'))  %]
+</form>
+
diff --git a/templates/webpages/employee/_list.html b/templates/webpages/employee/_list.html
new file mode 100644 (file)
index 0000000..5c92d44
--- /dev/null
@@ -0,0 +1,15 @@
+[%- USE T8 %]
+<table>
+ <tr class='listheading'>
+  <th>[% 'Login of User' | $T8 %]</th>
+  <th>[% 'Name' | $T8 %]</th>
+  <th>[% 'Deleted' | $T8 %]</th>
+ </tr>
+[%- FOREACH row IN SELF.employees %]
+ <tr class='listrow[% loop.count % 2 %]'>
+  <td><a href='[% SELF.url_for(action="edit", "employee.id"=row.id) %]'>[% row.login | html %]</a></td>
+  <td>[% row.name | html %]</td>
+  <td>[% row.deleted_as_bool_yn | html %]</td>
+ </tr>
+[%- END %]
+ </table>
diff --git a/templates/webpages/employee/edit.html b/templates/webpages/employee/edit.html
new file mode 100644 (file)
index 0000000..d4b3ed8
--- /dev/null
@@ -0,0 +1,9 @@
+<h1>[% title | html %]</h1>
+
+[% PROCESS 'common/flash.html' %]
+
+[% PROCESS 'employee/_form.html' employee=SELF.employee %]
+
+<hr>
+
+[% PROCESS 'employee/_list.html' %]
diff --git a/templates/webpages/employee/list.html b/templates/webpages/employee/list.html
new file mode 100644 (file)
index 0000000..056a828
--- /dev/null
@@ -0,0 +1,5 @@
+<h1>[% title | html %]</h1>
+
+[% PROCESS 'common/flash.html' %]
+
+[% PROCESS 'employee/_list.html' %]
index 0d3a155..6c5b7d7 100644 (file)
@@ -44,7 +44,7 @@
         </tr>
  <tr>
     <th align=right>[% 'Employee' | $T8 %]</th>
-    <td colspan=3>[% L.select_tag('employee', L.options_for_select(ALL_EMPLOYEES, value_sub=\employee_label, title='name', with_empty=1)) %]</td>
+    <td colspan=3>[% L.select_tag('employee', L.options_for_select(ALL_EMPLOYEES, title='safe_name', with_empty=1)) %]</td>
   </tr>
   <tr>
     <th align=right>[% 'Filter date by' | $T8 %]</th>
index 697e3e5..d8a44e1 100644 (file)
       <table>
         <tr>
           <th align="right">[% 'Employee' | $T8 %]</th>
-          <td>
-            [%- INCLUDE 'generic/multibox.html'
-                 name       = 'employee_id',
-                 DATA       = ALL_EMPLOYEES,
-                 id_key     = 'id',
-                 label_sub  = 'sales_employee_labels' -%]
-          </td>
+          <td>[% L.select_tag('employee_id', L.options_for_select(ALL_EMPLOYEES, default=employee_id, title='safe_name')) %]</td>
         </tr>
 
 [%- IF is_type_credit_note %]
index c322a77..6b25e8a 100644 (file)
         <tr>
           <th align="right">[% 'Employee' | $T8 %]</th>
           <td>
-            [% L.select_tag('employee_id', L.options_for_select(ALL_EMPLOYEES, default=employee_id, title_sub=\sales_employee_labels)) %]
+            [% L.select_tag('employee_id', L.options_for_select(ALL_EMPLOYEES, default=employee_id, title='safe_name')) %]
           </td>
         </tr>
 [%- IF ALL_SALESMEN.size %]
         <tr>
           <th align="right">[% 'Salesman' | $T8 %]</th>
           <td>
-            [% L.select_tag('salesman_id', L.options_for_select(ALL_SALESMEN, default=(salesman_id ? salesman_id : employee_id), title_sub=\sales_employee_labels)) %]
+            [% L.select_tag('salesman_id', L.options_for_select(ALL_SALESMEN, default=(salesman_id ? salesman_id : employee_id), title='safe_name')) %]
           </td>
         </tr>
 [%- END %]
index 89a897e..45b529b 100644 (file)
@@ -20,7 +20,7 @@ window.onload=clockon
   <tr>
    <td style="color:white; font-family:verdana,arial,sans-serif; font-size: 12px;">
     &nbsp;
-    [<a href="menuv3.pl?action=display" target="_blank">[% 'new Window' | $T8 %]</a>]
+    [<a href="menunew.pl?action=display" target="_blank">[% 'new Window' | $T8 %]</a>]
     &nbsp;
     [<a href="JavaScript:top.main_window.print()">[% 'print' | $T8 %]</a>]
    </td>
index 3c17033..68c2286 100644 (file)
                   <tr>
                     <th align="right">[% 'Employee' | $T8 %]</th>
                     <td>
-                      [% L.select_tag('employee_id', L.options_for_select(ALL_EMPLOYEES, default=employee_id, title_sub=\sales_employee_labels)) %]
+                      [% L.select_tag('employee_id', L.options_for_select(ALL_EMPLOYEES, default=employee_id, title='safe_name')) %]
                     </td>
                   </tr>
 [%- IF is_sales and ALL_SALESMEN.size %]
                   <tr>
                     <th align="right">[% 'Salesman' | $T8 %]</th>
                     <td>
-                      [% L.select_tag('salesman_id', L.options_for_select(ALL_SALESMEN, default=(salesman_id ? salesman_id : employee_id), title_sub=\sales_employee_labels)) %]
+                      [% L.select_tag('salesman_id', L.options_for_select(ALL_SALESMEN, default=(salesman_id ? salesman_id : employee_id), title='safe_name')) %]
                     </td>
                   </tr>
 [%- END %]
index 22d83b0..8f39ea2 100644 (file)
     </tr>
     <tr>
      <th align="right">[% 'Employee' | $T8 %]</th>
-     <td>
-            [%- INCLUDE 'generic/multibox.html'
-                 name          = 'employee_id',
-                 style         = 'width: 250px',
-                 DATA          =  ALL_EMPLOYEES,
-                 id_key        = 'id',
-                 label_sub     = 'employee_labels',
-                 limit         = vclimit,
-                 show_empty    = 1,
-                 allow_textbox = 0,
-                 default       = ' ',
-            -%]
-     </td>
+     <td>[% L.select_tag('employee_id', L.options_for_select(ALL_EMPLOYEES, title='safe_name', with_empty=1), style='width:250px') %]</td>
     </tr>
     <tr>
      <th align="right">[% 'Salesman' | $T8 %]</th>
-     <td>
-            [%- INCLUDE 'generic/multibox.html'
-                 name          = 'salesman_id',
-                 style         = 'width: 250px',
-                 DATA          =  ALL_SALESMEN,
-                 id_key        = 'id',
-                 label_sub     = 'salesman_labels',
-                 limit         = vclimit,
-                 show_empty    = 1,
-                 allow_textbox = 0,
-            -%]
-     </td>
+     <td>[% L.select_tag('salesman_id', L.options_for_select(ALL_EMPLOYEES, title='safe_name', with_empty=1), style='width:250px') %]</td>
     </tr>
     <tr>
      <th align="right">[% 'Transaction description' | $T8 %]</th>
index ad19c13..bd79216 100644 (file)
@@ -37,7 +37,7 @@
       <td>
        [%- SET rows = LxERP.numtextrows(project.description, 60) %]
        [%- IF rows > 1 %]
-       <textarea name="project.description" rows="rows" cols="60" style="width: 100%" wrap="soft">[% HTML.escape(project.description) %]</textarea>
+       <textarea name="project.description" rows="[% rows %]" cols="60" style="width: 100%" wrap="soft">[% HTML.escape(project.description) %]</textarea>
        [%- ELSE %]
        <input name="project.description" size="60" value="[% HTML.escape(project.description) %]">
        [%- END %]