Presenter: Sub-Presenter auf Funktional geändert
authorSven Schöling <s.schoeling@linet-services.de>
Tue, 19 Dec 2017 14:23:35 +0000 (15:23 +0100)
committerSven Schöling <s.schoeling@linet-services.de>
Tue, 19 Dec 2017 14:37:19 +0000 (15:37 +0100)
1. alle Sub-Presenter aus dem SL::Presenter Namespace geschmissen

2. Keine ungefragten @EXPORTs mehr, nur noch @EXPORT_OK

3. Alle Methoden sind auf Funktionen umgeschrieben (kein $self mehr)

4. Die benötigten Methoden von Sub-Presentern müssen jetzt direkt importiert
   werden (betrifft vor allem SL::Presenter::EscapedText und SL::Presenter::Tag)

5. Da die Funktionen in Tag/Text/EscapedText recht häufig gebraucht werden,
   exportieren die ihre Funktionen stattdessen nach SL::Presenter::Simple, und
   die Proxies in SL::Template::Plugin::L und SL::Template::Plugin::P
   dispatchen auf diesen Namespace statt auf SL::Presenter.

6. Die Sub-Namespaces sind in SL::Presenter::ALL registriert, und dort liegt
   auch der Proxymechanismus um vom Objekt aus zu der Funktion zu dispatchen.
   Das ist nur in SL::Template::Plugin::P registriert, nicht mehr in L.

7. Für Funktionen deren exportierter name mit Namespace präfixt war, gibt es
   einen Alias angelegt der nicht exportiert wird.

25 files changed:
SL/DB/Helper/Presenter.pm
SL/Presenter.pm
SL/Presenter/ALL.pm [new file with mode: 0644]
SL/Presenter/BankAccount.pm
SL/Presenter/Chart.pm
SL/Presenter/CustomerVendor.pm
SL/Presenter/DeliveryOrder.pm
SL/Presenter/EscapedText.pm
SL/Presenter/GL.pm
SL/Presenter/Invoice.pm
SL/Presenter/Letter.pm
SL/Presenter/Order.pm
SL/Presenter/Part.pm
SL/Presenter/Project.pm
SL/Presenter/Record.pm
SL/Presenter/RequirementSpec.pm
SL/Presenter/RequirementSpecItem.pm
SL/Presenter/RequirementSpecTextBlock.pm
SL/Presenter/SepaExport.pm
SL/Presenter/ShopOrder.pm
SL/Presenter/Simple.pm [new file with mode: 0644]
SL/Presenter/Tag.pm
SL/Presenter/Text.pm
SL/Template/Plugin/L.pm
SL/Template/Plugin/P.pm

index 240d18f..9c9f8a2 100644 (file)
@@ -17,7 +17,9 @@ sub AUTOLOAD {
 
   return if $method eq 'DESTROY';
 
-  return $self->[0]->$method($self->[1], @args);
+  if (my $sub = $self->[0]->can($method)) {
+    return $sub->($self->[1], @args);
+  }
 }
 
 1;
index cd4f4e1..b319175 100644 (file)
@@ -7,25 +7,7 @@ use parent qw(Rose::Object);
 use Carp;
 use Template;
 
-use SL::Presenter::Chart;
-use SL::Presenter::CustomerVendor;
-use SL::Presenter::DeliveryOrder;
-use SL::Presenter::EscapedText;
-use SL::Presenter::Invoice;
-use SL::Presenter::GL;
-use SL::Presenter::Letter;
-use SL::Presenter::Order;
-use SL::Presenter::Part;
-use SL::Presenter::Project;
-use SL::Presenter::Record;
-use SL::Presenter::RequirementSpec;
-use SL::Presenter::RequirementSpecItem;
-use SL::Presenter::RequirementSpecTextBlock;
-use SL::Presenter::SepaExport;
-use SL::Presenter::ShopOrder;
-use SL::Presenter::Text;
-use SL::Presenter::Tag;
-use SL::Presenter::BankAccount;
+use SL::Presenter::EscapedText qw(is_escaped);
 
 use Rose::Object::MakeMethods::Generic (
   scalar => [ qw(need_reinit_widgets) ],
@@ -80,15 +62,15 @@ sub render {
   if (!$options->{process}) {
     # If $template is a reference then don't try to read a file.
     my $ref = ref $template;
-    return $template                                                                if $ref eq 'SL::Presenter::EscapedText';
-    return SL::Presenter::EscapedText->new(text => ${ $template }, is_escaped => 1) if $ref eq 'SCALAR';
+    return $template                  if $ref eq 'SL::Presenter::EscapedText';
+    return is_escaped(${ $template }) if $ref eq 'SCALAR';
 
     # Otherwise return the file's content.
     my $file    = IO::File->new($source, "r") || croak("Template file ${source} could not be read");
     my $content = do { local $/ = ''; <$file> };
     $file->close;
 
-    return SL::Presenter::EscapedText->new(text => $content, is_escaped => 1);
+    return is_escaped($content);
   }
 
   # Processing was requested. Set up all variables.
@@ -108,7 +90,7 @@ sub render {
   my $parser = $self->get_template;
   $parser->process($source, \%params, \$output) || croak $parser->error;
 
-  return SL::Presenter::EscapedText->new(text => $output, is_escaped => 1);
+  return is_escaped($output);
 }
 
 sub get_template {
@@ -132,28 +114,6 @@ sub get_template {
   return $self->{template};
 }
 
-sub escape {
-  my ($self, $text) = @_;
-
-  return SL::Presenter::EscapedText->new(text => $text);
-}
-
-sub escaped_text {
-  my ($self, $text) = @_;
-
-  return SL::Presenter::EscapedText->new(text => $text, is_escaped => 1);
-}
-
-sub escape_js {
-  my ($self, $text) = @_;
-
-  $text =~ s|\\|\\\\|g;
-  $text =~ s|\"|\\\"|g;
-  $text =~ s|\n|\\n|g;
-
-  return SL::Presenter::EscapedText->new(text => $text, is_escaped => 1);
-}
-
 1;
 
 __END__
@@ -176,14 +136,15 @@ SL::Presenter - presentation layer class
   # Higher-level rendering of certain objects:
   use SL::DB::Customer;
 
-  my $linked_customer_name = $presenter->customer($customer, display => 'table-cell');
+  my $linked_customer_name = $customer->presenter->customer(display => 'table-cell');
 
   # Render a list of links to sales/purchase records:
   use SL::DB::Order;
+  use SL::Presenter::Record qw(grouped_record_list);
 
   my $quotation = SL::DB::Manager::Order->get_first(where => { quotation => 1 });
   my $records   = $quotation->linked_records(direction => 'to');
-  my $html      = $presenter->grouped_record_list($records);
+  my $html      = grouped_record_list($records);
 
 =head1 CLASS FUNCTIONS
 
@@ -309,35 +270,6 @@ it at all:
     { type => 'json', process => 0 }
   );
 
-=item C<escape $text>
-
-Returns an HTML-escaped version of C<$text>. Instead of a string an
-instance of the thin proxy-object L<SL::Presenter::EscapedText> is
-returned.
-
-It is safe to call C<escape> on an instance of
-L<SL::Presenter::EscapedText>. This is a no-op (the same instance will
-be returned).
-
-=item C<escaped_text $text>
-
-Returns an instance of L<SL::Presenter::EscapedText>. C<$text> is
-assumed to be a string that has already been HTML-escaped.
-
-It is safe to call C<escaped_text> on an instance of
-L<SL::Presenter::EscapedText>. This is a no-op (the same instance will
-be returned).
-
-=item C<escape_js $text>
-
-Returns a JavaScript-escaped version of C<$text>. Instead of a string
-an instance of the thin proxy-object L<SL::Presenter::EscapedText> is
-returned.
-
-It is safe to call C<escape> on an instance of
-L<SL::Presenter::EscapedText>. This is a no-op (the same instance will
-be returned).
-
 =item C<get_template>
 
 Returns the global instance of L<Template> and creates it if it
diff --git a/SL/Presenter/ALL.pm b/SL/Presenter/ALL.pm
new file mode 100644 (file)
index 0000000..73511b5
--- /dev/null
@@ -0,0 +1,70 @@
+package SL::Presenter::ALL;
+
+use strict;
+
+use SL::Presenter::Chart;
+use SL::Presenter::CustomerVendor;
+use SL::Presenter::DeliveryOrder;
+use SL::Presenter::EscapedText;
+use SL::Presenter::Invoice;
+use SL::Presenter::GL;
+use SL::Presenter::Letter;
+use SL::Presenter::Order;
+use SL::Presenter::Part;
+use SL::Presenter::Project;
+use SL::Presenter::Record;
+use SL::Presenter::RequirementSpec;
+use SL::Presenter::RequirementSpecItem;
+use SL::Presenter::RequirementSpecTextBlock;
+use SL::Presenter::SepaExport;
+use SL::Presenter::ShopOrder;
+use SL::Presenter::Text;
+use SL::Presenter::Tag;
+use SL::Presenter::BankAccount;
+
+our %presenters = (
+  chart                       => 'SL::Presenter::Chart',
+  customer_vendor             => 'SL::Presenter::CustomerVendor',
+  delivery_order              => 'SL::Presenter::DeliveryOrder',
+  escaped_text                => 'SL::Presenter::EscapedText',
+  invoice                     => 'SL::Presenter::Invoice',
+  gl                          => 'SL::Presenter::GL',
+  letter                      => 'SL::Presenter::Letter',
+  order                       => 'SL::Presenter::Order',
+  part                        => 'SL::Presenter::Part',
+  project                     => 'SL::Presenter::Project',
+  record                      => 'SL::Presenter::Record',
+  requirement_spec            => 'SL::Presenter::RequirementSpec',
+  requirement_spec_item       => 'SL::Presenter::RequirementSpecItem',
+  requirement_spec_text_block => 'SL::Presenter::RequirementSpecTextBlock',
+  sepa_export                 => 'SL::Presenter::SepaExport',
+  shop_order                  => 'SL::Presenter::ShopOrder',
+  text                        => 'SL::Presenter::Text',
+  tag                         => 'SL::Presenter::Tag',
+  bank_account                => 'SL::Presenter::BankAccount',
+);
+
+sub wrap {
+  bless [ $_[0] ], 'SL::Presenter::ALL::Wrapper';
+}
+
+package SL::Presenter::ALL::Wrapper;
+
+sub AUTOLOAD {
+  our $AUTOLOAD;
+
+  my ($self, @args) = @_;
+
+  my $method = $AUTOLOAD;
+  $method    =~ s/.*:://;
+
+  return if $method eq 'DESTROY';
+
+  splice @args, -1, 1, %{ $args[-1] } if @args && (ref($args[-1]) eq 'HASH');
+
+  if (my $sub = $self->[0]->can($method)) {
+    return $sub->(@args);
+  }
+}
+
+1;
index 13a8cb2..925dd97 100644 (file)
@@ -2,21 +2,21 @@ package SL::Presenter::BankAccount;
 
 use strict;
 
-use parent qw(Exporter);
+use SL::Presenter::EscapedText qw(escape);
 
 use Exporter qw(import);
-our @EXPORT = qw(account_number bank_code);
+our @EXPORT_OK = qw(account_number bank_code);
 
 use Carp;
 
 sub account_number {
-  my ($self, $bank_account) = @_;
-  return $self->escaped_text($bank_account->account_number);
+  my ($bank_account) = @_;
+  escape($bank_account->account_number);
 }
 
 sub bank_code {
-  my ($self, $bank_account) = @_;
-  return $self->escaped_text($bank_account->bank_code);
+  my ($bank_account) = @_;
+  escape($bank_account->bank_code);
 }
 
 1;
index 58c6b85..3a4f483 100644 (file)
@@ -5,31 +5,33 @@ use strict;
 use SL::DB::Chart;
 
 use Exporter qw(import);
-use Data::Dumper;
-our @EXPORT = qw(chart_picker chart);
+our @EXPORT_OK = qw(chart_picker chart);
 
 use Carp;
+use Data::Dumper;
+use SL::Presenter::EscapedText qw(escape is_escaped);
+use SL::Presenter::Tag qw(input_tag name_to_id html_tag);
 
 sub chart {
-  my ($self, $chart, %params) = @_;
+  my ($chart, %params) = @_;
 
   $params{display} ||= 'inline';
 
   croak "Unknown display type '$params{display}'" unless $params{display} =~ m/^(?:inline|table-cell)$/;
 
   my $text = join '', (
-    $params{no_link} ? '' : '<a href="am.pl?action=edit_account&id=' . $self->escape($chart->id) . '">',
-    $self->escape($chart->accno),
+    $params{no_link} ? '' : '<a href="am.pl?action=edit_account&id=' . escape($chart->id) . '">',
+    escape($chart->accno),
     $params{no_link} ? '' : '</a>',
   );
-  return $self->escaped_text($text);
+  is_escaped($text);
 }
 
 sub chart_picker {
-  my ($self, $name, $value, %params) = @_;
+  my ($name, $value, %params) = @_;
 
   $value = SL::DB::Manager::Chart->find_by(id => $value) if $value && !ref $value;
-  my $id = delete($params{id}) || $self->name_to_id($name);
+  my $id = delete($params{id}) || name_to_id($name);
   my $fat_set_item = delete $params{fat_set_item};
 
   my @classes = $params{class} ? ($params{class}) : ();
@@ -37,16 +39,18 @@ sub chart_picker {
   push @classes, 'chartpicker_fat_set_item' if $fat_set_item;
 
   my $ret =
-    $self->input_tag($name, (ref $value && $value->can('id') ? $value->id : ''), class => "@classes", type => 'hidden', id => $id) .
-    join('', map { $params{$_} ? $self->input_tag("", delete $params{$_}, id => "${id}_${_}", type => 'hidden') : '' } qw(type category choose booked)) .
-    $self->input_tag("", (ref $value && $value->can('displayable_name')) ? $value->displayable_name : '', id => "${id}_name", %params);
+    input_tag($name, (ref $value && $value->can('id') ? $value->id : ''), class => "@classes", type => 'hidden', id => $id) .
+    join('', map { $params{$_} ? input_tag("", delete $params{$_}, id => "${id}_${_}", type => 'hidden') : '' } qw(type category choose booked)) .
+    input_tag("", (ref $value && $value->can('displayable_name')) ? $value->displayable_name : '', id => "${id}_name", %params);
 
   $::request->layout->add_javascripts('autocomplete_chart.js');
   $::request->presenter->need_reinit_widgets($id);
 
-  $self->html_tag('span', $ret, class => 'chart_picker');
+  html_tag('span', $ret, class => 'chart_picker');
 }
 
+sub picker { goto &chart_picker }
+
 1;
 
 __END__
@@ -61,7 +65,7 @@ SL::Presenter::Chart - Chart related presenter stuff
 
   # Create an html link for editing/opening a chart
   my $object = SL::DB::Manager::Chart->get_first;
-  my $html   = SL::Presenter->get->chart($object, display => 'inline');
+  my $html   = SL::Presenter::Chart::chart($object, display => 'inline');
 
 see also L<SL::Presenter>
 
index d00c54d..80b85f9 100644 (file)
@@ -2,30 +2,31 @@ package SL::Presenter::CustomerVendor;
 
 use strict;
 
-use parent qw(Exporter);
+use SL::Presenter::EscapedText qw(escape is_escaped);
+use SL::Presenter::Tag qw(input_tag html_tag name_to_id select_tag);
 
 use Exporter qw(import);
-our @EXPORT = qw(customer_vendor customer vendor customer_vendor_picker);
+our @EXPORT_OK = qw(customer_vendor customer vendor customer_vendor_picker);
 
 use Carp;
 
 sub customer_vendor {
-  my ($self, $customer_vendor, %params) = @_;
-  return _customer_vendor($self, $customer_vendor, ref($customer_vendor) eq 'SL::DB::Customer' ? 'customer' : 'vendor', %params);
+  my ($customer_vendor, %params) = @_;
+  return _customer_vendor($customer_vendor, ref($customer_vendor) eq 'SL::DB::Customer' ? 'customer' : 'vendor', %params);
 }
 
 sub customer {
-  my ($self, $customer, %params) = @_;
-  return _customer_vendor($self, $customer, 'customer', %params);
+  my ($customer, %params) = @_;
+  return _customer_vendor($customer, 'customer', %params);
 }
 
 sub vendor {
-  my ($self, $vendor, %params) = @_;
-  return _customer_vendor($self, $vendor, 'vendor', %params);
+  my ($vendor, %params) = @_;
+  return _customer_vendor($vendor, 'vendor', %params);
 }
 
 sub _customer_vendor {
-  my ($self, $cv, $type, %params) = @_;
+  my ($cv, $type, %params) = @_;
 
   $params{display} ||= 'inline';
 
@@ -34,15 +35,16 @@ sub _customer_vendor {
   my $callback = $params{callback} ? '&callback=' . $::form->escape($params{callback}) : '';
 
   my $text = join '', (
-    $params{no_link} ? '' : '<a href="controller.pl?action=CustomerVendor/edit&amp;db=' . $type . '&amp;id=' . $self->escape($cv->id) . '">',
-    $self->escape($cv->name),
+    $params{no_link} ? '' : '<a href="controller.pl?action=CustomerVendor/edit&amp;db=' . $type . '&amp;id=' . escape($cv->id) . '">',
+    escape($cv->name),
     $params{no_link} ? '' : '</a>',
   );
-  return $self->escaped_text($text);
+
+  is_escaped($text);
 }
 
 sub customer_vendor_picker {
-  my ($self, $name, $value, %params) = @_;
+  my ($name, $value, %params) = @_;
 
   croak 'Unknown "type" parameter' unless $params{type} =~ m{^(?:customer|vendor)$};
   croak 'Unknown value class'      if     $value && ref($value) && (ref($value) !~ m{^SL::DB::(?:Customer|Vendor)$});
@@ -52,23 +54,25 @@ sub customer_vendor_picker {
     $value    = $class->find_by(id => $value);
   }
 
-  my $id = delete($params{id}) || $self->name_to_id($name);
+  my $id = delete($params{id}) || name_to_id($name);
 
   my @classes = $params{class} ? ($params{class}) : ();
   push @classes, 'customer_vendor_autocomplete';
 
   my $ret =
-    $self->input_tag($name, (ref $value && $value->can('id') ? $value->id : ''), class => "@classes", type => 'hidden', id => $id,
+    input_tag($name, (ref $value && $value->can('id') ? $value->id : ''), class => "@classes", type => 'hidden', id => $id,
       'data-customer-vendor-picker-data' => JSON::to_json(\%params),
     ) .
-    $self->input_tag("", ref $value  ? $value->displayable_name : '', id => "${id}_name", %params);
+    input_tag("", ref $value  ? $value->displayable_name : '', id => "${id}_name", %params);
 
   $::request->layout->add_javascripts('kivi.CustomerVendor.js');
   $::request->presenter->need_reinit_widgets($id);
 
-  $self->html_tag('span', $ret, class => 'customer_vendor_picker');
+  html_tag('span', $ret, class => 'customer_vendor_picker');
 }
 
+sub picker { goto &customer_vendor_picker }
+
 1;
 
 __END__
@@ -86,11 +90,11 @@ vendor Rose::DB objects
 
   # Customers:
   my $customer = SL::DB::Manager::Customer->get_first;
-  my $html     = SL::Presenter->get->customer($customer, display => 'inline');
+  my $html     = SL::Presenter::CustomerVendor::customer($customer, display => 'inline');
 
   # Vendors:
   my $vendor = SL::DB::Manager::Vendor->get_first;
-  my $html   = SL::Presenter->get->vendor($customer, display => 'inline');
+  my $html   = SL::Presenter::Customer::Vendor::vendor($customer, display => 'inline');
 
 =head1 FUNCTIONS
 
index 1abc835..79123b1 100644 (file)
@@ -2,38 +2,38 @@ package SL::Presenter::DeliveryOrder;
 
 use strict;
 
-use parent qw(Exporter);
+use SL::Presenter::EscapedText qw(escape is_escaped);
 
 use Exporter qw(import);
-our @EXPORT = qw(sales_delivery_order purchase_delivery_order);
+our @EXPORT_OK = qw(sales_delivery_order purchase_delivery_order);
 
 use Carp;
 
 sub sales_delivery_order {
-  my ($self, $delivery_order, %params) = @_;
+  my ($delivery_order, %params) = @_;
 
-  return _do_record($self, $delivery_order, 'sales_delivery_order', %params);
+  return _do_record($delivery_order, 'sales_delivery_order', %params);
 }
 
 sub purchase_delivery_order {
-  my ($self, $delivery_order, %params) = @_;
+  my ($delivery_order, %params) = @_;
 
-  return _do_record($self, $delivery_order, 'purchase_delivery_order', %params);
+  return _do_record($delivery_order, 'purchase_delivery_order', %params);
 }
 
 sub _do_record {
-  my ($self, $delivery_order, $type, %params) = @_;
+  my ($delivery_order, $type, %params) = @_;
 
   $params{display} ||= 'inline';
 
   croak "Unknown display type '$params{display}'" unless $params{display} =~ m/^(?:inline|table-cell)$/;
 
   my $text = join '', (
-    $params{no_link} ? '' : '<a href="do.pl?action=edit&amp;type=' . $type . '&amp;id=' . $self->escape($delivery_order->id) . '">',
-    $self->escape($delivery_order->donumber),
+    $params{no_link} ? '' : '<a href="do.pl?action=edit&amp;type=' . $type . '&amp;id=' . escape($delivery_order->id) . '">',
+    escape($delivery_order->donumber),
     $params{no_link} ? '' : '</a>',
   );
-  return $self->escaped_text($text);
+  is_escaped($text);
 }
 
 1;
@@ -53,11 +53,11 @@ for sales and purchase delivery orders
 
   # Sales delivery orders:
   my $object = SL::DB::Manager::DeliveryOrder->get_first(where => [ is_sales => 1 ]);
-  my $html   = SL::Presenter->get->sales_delivery_order($object, display => 'inline');
+  my $html   = SL::Presenter::DeliveryOrder::sales_delivery_order($object, display => 'inline');
 
   # Purchase delivery orders:
   my $object = SL::DB::Manager::DeliveryOrder->get_first(where => [ or => [ is_sales => undef, is_sales => 0 ]]);
-  my $html   = SL::Presenter->get->purchase_delivery_order($object, display => 'inline');
+  my $html   = SL::Presenter::DeliveryOrder::purchase_delivery_order($object, display => 'inline');
 
 =head1 FUNCTIONS
 
index c8c78a0..2fc04fd 100644 (file)
@@ -1,11 +1,16 @@
 package SL::Presenter::EscapedText;
 
 use strict;
+use Exporter qw(import);
+
+our @EXPORT_OK = qw(escape is_escaped escape_js);
+our %EXPORT_TAGS = (ALL => \@EXPORT_OK);
 
 use JSON ();
 
-use overload '""' => \&escaped;
+use overload '""' => \&escaped_text;
 
+# static constructors
 sub new {
   my ($class, %params) = @_;
 
@@ -17,13 +22,32 @@ sub new {
   return $self;
 }
 
-sub escaped {
+sub escape {
+  __PACKAGE__->new(text => $_[0]);
+}
+
+sub is_escaped {
+  __PACKAGE__->new(text => $_[0], is_escaped => 1);
+}
+
+sub escape_js {
+  my ($text) = @_;
+
+  $text =~ s|\\|\\\\|g;
+  $text =~ s|\"|\\\"|g;
+  $text =~ s|\n|\\n|g;
+
+  __PACKAGE__->new(text => $text, is_escaped => 1);
+}
+
+# internal magic
+sub escaped_text {
   my ($self) = @_;
   return $self->{text};
 }
 
 sub TO_JSON {
-  goto &escaped;
+  goto &escaped_text;
 }
 
 1;
@@ -35,15 +59,18 @@ __END__
 
 =head1 NAME
 
-SL::Presenter::EscapedText - Thin proxy object around HTML-escaped strings
+SL::Presenter::EscapedText - Thin proxy object to invert the burden of escaping HTML output
 
 =head1 SYNOPSIS
 
-  use SL::Presenter::EscapedText;
+  use SL::Presenter::EscapedText qw(escape is_escaped escape_js);
 
   sub blackbox {
     my ($text) = @_;
     return SL::Presenter::EscapedText->new(text => $text);
+
+    # or shorter:
+    # return escape($text);
   }
 
   sub build_output {
@@ -67,7 +94,7 @@ But higher functions should not have to care if the output is already
 escaped -- they should be able to simply escape it again. Without
 producing stuff like '&amp;amp;'.
 
-Stringification is overloaded. It will return the same as L<escaped>.
+Stringification is overloaded. It will return the same as L<escaped_text>.
 
 This works together with the template plugin
 L<SL::Template::Plugin::P> and its C<escape> method.
@@ -88,7 +115,25 @@ Otherwise C<text> is HTML-escaped and stored in the new instance. This
 can be overridden by setting C<$params{is_escaped}> to a trueish
 value.
 
-=item C<escaped>
+=item C<escape $text>
+
+Static constructor, can be exported. Equivalent to calling C<< new(text => $text) >>.
+
+=item C<is_escaped $text>
+
+Static constructor, can be exported. Equivalent to calling C<< new(text => $text, escaped => 1) >>.
+
+=item C<escape_js $text>
+
+Static constructor, can be exported. Like C<escape> but also escapes Javascript.
+
+=back
+
+=head1 METHODS
+
+=over 4
+
+=item C<escaped_text>
 
 Returns the escaped string (not an instance of C<EscapedText> but an
 actual string).
index 5d67161..cf0998b 100644 (file)
@@ -2,26 +2,27 @@ package SL::Presenter::GL;
 
 use strict;
 
-use parent qw(Exporter);
+use SL::Presenter::EscapedText qw(escape is_escaped);
 
 use Exporter qw(import);
-our @EXPORT = qw(gl_transaction);
+our @EXPORT_OK = qw(gl_transaction);
 
 use Carp;
 
 sub gl_transaction {
-  my ($self, $gl_transaction, %params) = @_;
+  my ($gl_transaction, %params) = @_;
 
   $params{display} ||= 'inline';
 
   croak "Unknown display type '$params{display}'" unless $params{display} =~ m/^(?:inline|table-cell)$/;
 
   my $text = join '', (
-    $params{no_link} ? '' : '<a href="gl.pl?action=edit&amp;id=' . $self->escape($gl_transaction->id) . '">',
-    $self->escape($gl_transaction->reference),
+    $params{no_link} ? '' : '<a href="gl.pl?action=edit&amp;id=' . escape($gl_transaction->id) . '">',
+    escape($gl_transaction->reference),
     $params{no_link} ? '' : '</a>',
   );
-  return $self->escaped_text($text);
+
+  is_escaped($text);
 }
 
 1;
@@ -39,7 +40,7 @@ SL::Presenter::GL - Presenter module for GL transaction
 =head1 SYNOPSIS
 
   my $object = SL::DB::Manager::GLTransaction->get_first();
-  my $html   = SL::Presenter->get->gl_transaction($object, display => 'inline');
+  my $html   = SL::Presenter::GL::gl_transaction($object, display => 'inline');
 
 =head1 FUNCTIONS
 
index ebde34f..ba662ed 100644 (file)
@@ -2,68 +2,69 @@ package SL::Presenter::Invoice;
 
 use strict;
 
-use parent qw(Exporter);
+use SL::Presenter::EscapedText qw(escape is_escaped);
 
 use Exporter qw(import);
-our @EXPORT = qw(invoice sales_invoice ar_transaction purchase_invoice ap_transaction);
+our @EXPORT_OK = qw(invoice sales_invoice ar_transaction purchase_invoice ap_transaction);
 
 use Carp;
 
 sub invoice {
-  my ($self, $invoice, %params) = @_;
+  my ($invoice, %params) = @_;
 
   if ( $invoice->is_sales ) {
     if ( $invoice->invoice ) {
-      return _is_ir_record($self, $invoice, 'is', %params);
+      return _is_ir_record($invoice, 'is', %params);
     } else {
-      return _is_ir_record($self, $invoice, 'ar', %params);
+      return _is_ir_record($invoice, 'ar', %params);
     }
   } else {
     if ( $invoice->invoice ) {
-      return _is_ir_record($self, $invoice, 'ir', %params);
+      return _is_ir_record($invoice, 'ir', %params);
     } else {
-      return _is_ir_record($self, $invoice, 'ap', %params);
+      return _is_ir_record($invoice, 'ap', %params);
     }
   };
 };
 
 sub sales_invoice {
-  my ($self, $invoice, %params) = @_;
+  my ($invoice, %params) = @_;
 
-  return _is_ir_record($self, $invoice, 'is', %params);
+  _is_ir_record($invoice, 'is', %params);
 }
 
 sub ar_transaction {
-  my ($self, $invoice, %params) = @_;
+  my ($invoice, %params) = @_;
 
-  return _is_ir_record($self, $invoice, 'ar', %params);
+  _is_ir_record($invoice, 'ar', %params);
 }
 
 sub purchase_invoice {
-  my ($self, $invoice, %params) = @_;
+  my ($invoice, %params) = @_;
 
-  return _is_ir_record($self, $invoice, 'ir', %params);
+  _is_ir_record($invoice, 'ir', %params);
 }
 
 sub ap_transaction {
-  my ($self, $invoice, %params) = @_;
+  my ($invoice, %params) = @_;
 
-  return _is_ir_record($self, $invoice, 'ap', %params);
+  _is_ir_record($invoice, 'ap', %params);
 }
 
 sub _is_ir_record {
-  my ($self, $invoice, $controller, %params) = @_;
+  my ($invoice, $controller, %params) = @_;
 
   $params{display} ||= 'inline';
 
   croak "Unknown display type '$params{display}'" unless $params{display} =~ m/^(?:inline|table-cell)$/;
 
   my $text = join '', (
-    $params{no_link} ? '' : '<a href="' . $controller . '.pl?action=edit&amp;type=invoice&amp;id=' . $self->escape($invoice->id) . '">',
-    $self->escape($invoice->invnumber),
+    $params{no_link} ? '' : '<a href="' . $controller . '.pl?action=edit&amp;type=invoice&amp;id=' . escape($invoice->id) . '">',
+    escape($invoice->invnumber),
     $params{no_link} ? '' : '</a>',
   );
-  return $self->escaped_text($text);
+
+  is_escaped($text);
 }
 
 1;
@@ -83,22 +84,22 @@ transaction, purchase invoice and AP transaction Rose::DB objects
 
   # Sales invoices:
   my $object = SL::DB::Manager::Invoice->get_first(where => [ invoice => 1 ]);
-  my $html   = SL::Presenter->get->sales_invoice($object, display => 'inline');
+  my $html   = SL::Presenter::Invoice::sales_invoice($object, display => 'inline');
 
   # AR transactions:
   my $object = SL::DB::Manager::Invoice->get_first(where => [ or => [ invoice => undef, invoice => 0 ]]);
-  my $html   = SL::Presenter->get->ar_transaction($object, display => 'inline');
+  my $html   = SL::Presenter::Invoice::ar_transaction($object, display => 'inline');
 
   # Purchase invoices:
   my $object = SL::DB::Manager::PurchaseInvoice->get_first(where => [ invoice => 1 ]);
-  my $html   = SL::Presenter->get->purchase_invoice($object, display => 'inline');
+  my $html   = SL::Presenter::Invoice::purchase_invoice($object, display => 'inline');
 
   # AP transactions:
   my $object = SL::DB::Manager::PurchaseInvoice->get_first(where => [ or => [ invoice => undef, invoice => 0 ]]);
-  my $html   = SL::Presenter->get->ar_transaction($object, display => 'inline');
+  my $html   = SL::Presenter::Invoice::ar_transaction($object, display => 'inline');
 
   # use with any of the above ar/ap/is/ir types:
-  my $html   = SL::Presenter->get->invoice($object, display => 'inline');
+  my $html   = SL::Presenter::Invoice::invoice($object, display => 'inline');
 
 =head1 FUNCTIONS
 
index 28de4b2..0747bfd 100644 (file)
@@ -2,27 +2,27 @@ package SL::Presenter::Letter;
 
 use strict;
 
-use parent qw(Exporter);
+use SL::Presenter::EscapedText qw(escape is_escaped);
 
 use Exporter qw(import);
-our @EXPORT = qw(letter);
+our @EXPORT_OK = qw(letter);
 
 use Carp;
 
 sub letter {
-  my ($self, $letter, %params) = @_;
+  my ($letter, %params) = @_;
 
   $params{display} ||= 'inline';
 
   croak "Unknown display type '$params{display}'" unless $params{display} =~ m/^(?:inline|table-cell)$/;
 
   my $text = join '', (
-    $params{no_link} ? '' : '<a href="controller.pl?action=Letter/edit&amp;letter.id=' . $self->escape($letter->id) . '">',
-    $self->escape($letter->letternumber),
+    $params{no_link} ? '' : '<a href="controller.pl?action=Letter/edit&amp;letter.id=' . escape($letter->id) . '">',
+    escape($letter->letternumber),
     $params{no_link} ? '' : '</a>',
   );
 
-  return $self->escaped_text($text);
+  is_escaped($text);
 }
 
 1;
@@ -40,7 +40,7 @@ SL::Presenter::Letter - Presenter module for letter objects
 =head1 SYNOPSIS
 
   my $letter = SL::DB::Manager::Letter->get_first(where => [ … ]);
-  my $html   = SL::Presenter->get->letter($letter, display => 'inline');
+  my $html   = SL::Presenter::Letter::letter($letter, display => 'inline');
 
 =head1 FUNCTIONS
 
index 19d8483..92e666c 100644 (file)
@@ -2,39 +2,39 @@ package SL::Presenter::Order;
 
 use strict;
 
-use parent qw(Exporter);
+use SL::Presenter::EscapedText qw(escape is_escaped);
 
 use Exporter qw(import);
-our @EXPORT = qw(sales_quotation sales_order request_quotation purchase_order);
+our @EXPORT_OK = qw(sales_quotation sales_order request_quotation purchase_order);
 
 use Carp;
 
 sub sales_quotation {
-  my ($self, $order, %params) = @_;
+  my ($order, %params) = @_;
 
-  return _oe_record($self, $order, 'sales_quotation', %params);
+  return _oe_record($order, 'sales_quotation', %params);
 }
 
 sub sales_order {
-  my ($self, $order, %params) = @_;
+  my ($order, %params) = @_;
 
-  return _oe_record($self, $order, 'sales_order', %params);
+  return _oe_record($order, 'sales_order', %params);
 }
 
 sub request_quotation {
-  my ($self, $order, %params) = @_;
+  my ($order, %params) = @_;
 
-  return _oe_record($self, $order, 'request_quotation', %params);
+  return _oe_record($order, 'request_quotation', %params);
 }
 
 sub purchase_order {
-  my ($self, $order, %params) = @_;
+  my ($order, %params) = @_;
 
-  return _oe_record($self, $order, 'purchase_order', %params);
+  return _oe_record($order, 'purchase_order', %params);
 }
 
 sub _oe_record {
-  my ($self, $order, $type, %params) = @_;
+  my ($order, $type, %params) = @_;
 
   $params{display} ||= 'inline';
 
@@ -43,11 +43,12 @@ sub _oe_record {
   my $number_method = $order->quotation ? 'quonumber' : 'ordnumber';
 
   my $text = join '', (
-    $params{no_link} ? '' : '<a href="oe.pl?action=edit&amp;type=' . $type . '&amp;id=' . $self->escape($order->id) . '">',
-    $self->escape($order->$number_method),
+    $params{no_link} ? '' : '<a href="oe.pl?action=edit&amp;type=' . $type . '&amp;id=' . escape($order->id) . '">',
+    escape($order->$number_method),
     $params{no_link} ? '' : '</a>',
   );
-  return $self->escaped_text($text);
+
+  is_escaped($text);
 }
 
 1;
@@ -68,19 +69,19 @@ quotations, sales orders, requests for quotations and purchase orders
 
   # Sales quotations:
   my $object = SL::DB::Manager::Order->get_first(where => [ SL::DB::Manager::Order->type_filter('sales_quotation') ]);
-  my $html   = SL::Presenter->get->sales_quotation($object, display => 'inline');
+  my $html   = SL::Presenter::Order::sales_quotation($object, display => 'inline');
 
   # Sales orders:
   my $object = SL::DB::Manager::Order->get_first(where => [ SL::DB::Manager::Order->type_filter('sales_order') ]);
-  my $html   = SL::Presenter->get->sales_order($object, display => 'inline');
+  my $html   = SL::Presenter::Order::sales_order($object, display => 'inline');
 
   # Requests for quotations:
   my $object = SL::DB::Manager::Order->get_first(where => [ SL::DB::Manager::Order->type_filter('request_quotation') ]);
-  my $html   = SL::Presenter->get->request_quotation($object, display => 'inline');
+  my $html   = SL::Presenter::Order::request_quotation($object, display => 'inline');
 
   # Purchase orders:
   my $object = SL::DB::Manager::Order->get_first(where => [ SL::DB::Manager::Order->type_filter('purchase_order') ]);
-  my $html   = SL::Presenter->get->purchase_order($object, display => 'inline');
+  my $html   = SL::Presenter::Order::purchase_order($object, display => 'inline');
 
 =head1 FUNCTIONS
 
index 8f13aee..8131eff 100644 (file)
@@ -5,53 +5,62 @@ use strict;
 use SL::DB::Part;
 use SL::DB::PartClassification;
 use SL::Locale::String qw(t8);
+use SL::Presenter::EscapedText qw(escape is_escaped);
+use SL::Presenter::Tag qw(input_tag html_tag name_to_id select_tag);
 
 use Exporter qw(import);
-our @EXPORT = qw(part_picker part select_classification classification_abbreviation type_abbreviation separate_abbreviation typeclass_abbreviation);
+our @EXPORT_OK = qw(
+  part_picker part select_classification classification_abbreviation
+  type_abbreviation separate_abbreviation typeclass_abbreviation
+);
+our %EXPORT_TAGS = (ALL => \@EXPORT_OK);
 
 use Carp;
 
 sub part {
-  my ($self, $part, %params) = @_;
+  my ($part, %params) = @_;
 
   $params{display} ||= 'inline';
 
   croak "Unknown display type '$params{display}'" unless $params{display} =~ m/^(?:inline|table-cell)$/;
 
   my $text = join '', (
-    $params{no_link} ? '' : '<a href="controller.pl?action=Part/edit&part.id=' . $self->escape($part->id) . '">',
-    $self->escape($part->partnumber),
+    $params{no_link} ? '' : '<a href="controller.pl?action=Part/edit&part.id=' . escape($part->id) . '">',
+    escape($part->partnumber),
     $params{no_link} ? '' : '</a>',
   );
-  return $self->escaped_text($text);
+
+  is_escaped($text);
 }
 
 sub part_picker {
-  my ($self, $name, $value, %params) = @_;
+  my ($name, $value, %params) = @_;
 
   $value = SL::DB::Manager::Part->find_by(id => $value) if $value && !ref $value;
-  my $id = $params{id} || $self->name_to_id($name);
+  my $id = $params{id} || name_to_id($name);
 
   my @classes = $params{class} ? ($params{class}) : ();
   push @classes, 'part_autocomplete';
 
   my $ret =
-    $self->input_tag($name, (ref $value && $value->can('id') ? $value->id : ''), class => "@classes", type => 'hidden', id => $id,
+    input_tag($name, (ref $value && $value->can('id') ? $value->id : ''), class => "@classes", type => 'hidden', id => $id,
       'data-part-picker-data' => JSON::to_json(\%params),
     ) .
-    $self->input_tag("", ref $value ? $value->displayable_name : '', id => "${id}_name", %params);
+    input_tag("", ref $value ? $value->displayable_name : '', id => "${id}_name", %params);
 
   $::request->layout->add_javascripts('kivi.Part.js');
   $::request->presenter->need_reinit_widgets($id);
 
-  $self->html_tag('span', $ret, class => 'part_picker');
+  html_tag('span', $ret, class => 'part_picker');
 }
 
+sub picker { goto &part_picker }
+
 #
 # shortcut for article type
 #
 sub type_abbreviation {
-  my ($self, $part_type) = @_;
+  my ($part_type) = @_;
   return $::locale->text('Assembly (typeabbreviation)')   if $part_type eq 'assembly';
   return $::locale->text('Part (typeabbreviation)')       if $part_type eq 'part';
   return $::locale->text('Assortment (typeabbreviation)') if $part_type eq 'assortment';
@@ -77,23 +86,23 @@ sub type_abbreviation {
 # shortcut for article type
 #
 sub classification_abbreviation {
-  my ($self, $id) = @_;
+  my ($id) = @_;
   SL::DB::Manager::PartClassification->cache_all();
   my $obj = SL::DB::PartClassification->load_cached($id);
   $obj && $obj->abbreviation ? t8($obj->abbreviation) : '';
 }
 
 sub typeclass_abbreviation {
-  my ($self, $part) = @_;
+  my ($part) = @_;
   return '' if !$part || !$part->isa('SL::DB::Part');
-  return $self->type_abbreviation($part->part_type).$self->classification_abbreviation($part->classification_id);
+  return type_abbreviation($part->part_type) . classification_abbreviation($part->classification_id);
 }
 
 #
 # shortcut for article type
 #
 sub separate_abbreviation {
-  my ($self, $id) = @_;
+  my ($id) = @_;
   SL::DB::Manager::PartClassification->cache_all();
   my $obj = SL::DB::PartClassification->load_cached($id);
   $obj && $obj->abbreviation && $obj->report_separate ? t8($obj->abbreviation) : '';
@@ -103,7 +112,7 @@ sub separate_abbreviation {
 # generate selection tag
 #
 sub select_classification {
-  my ($self, $name, %attributes) = @_;
+  my ($name, %attributes) = @_;
 
   $attributes{value_key} = 'id';
   $attributes{title_key} = 'description';
@@ -112,7 +121,7 @@ sub select_classification {
 
   my $collection = SL::DB::Manager::PartClassification->get_all_sorted( where => $classification_type_filter );
   $_->description($::locale->text($_->description)) for @{ $collection };
-  return $self->select_tag( $name, $collection, %attributes );
+  select_tag( $name, $collection, %attributes );
 }
 
 1;
index 5f39771..1be7a63 100644 (file)
@@ -2,15 +2,16 @@ package SL::Presenter::Project;
 
 use strict;
 
-use parent qw(Exporter);
+use SL::Presenter::EscapedText qw(escape is_escaped);
+use SL::Presenter::Tag qw(input_tag html_tag name_to_id select_tag);
 
 use Exporter qw(import);
-our @EXPORT = qw(project project_picker);
+our @EXPORT_OK = qw(project project_picker);
 
 use Carp;
 
 sub project {
-  my ($self, $project, %params) = @_;
+  my ($project, %params) = @_;
 
   return '' unless $project;
 
@@ -22,32 +23,34 @@ sub project {
   my $callback    = $params{callback} ? '&callback=' . $::form->escape($params{callback}) : '';
 
   my $text = join '', (
-    $params{no_link} ? '' : '<a href="controller.pl?action=Project/edit&amp;id=' . $self->escape($project->id) . $callback . '">',
-    $self->escape($description),
+    $params{no_link} ? '' : '<a href="controller.pl?action=Project/edit&amp;id=' . escape($project->id) . $callback . '">',
+    escape($description),
     $params{no_link} ? '' : '</a>',
   );
-  return $self->escaped_text($text);
+  is_escaped($text);
 }
 
 sub project_picker {
-  my ($self, $name, $value, %params) = @_;
+  my ($name, $value, %params) = @_;
 
   $value      = SL::DB::Manager::Project->find_by(id => $value) if $value && !ref $value;
-  my $id      = delete($params{id}) || $self->name_to_id($name);
+  my $id      = delete($params{id}) || name_to_id($name);
   my @classes = $params{class} ? ($params{class}) : ();
   push @classes, 'project_autocomplete';
 
   my $ret =
-    $self->input_tag($name, (ref $value && $value->can('id') ? $value->id : ''), class => "@classes", type => 'hidden', id => $id) .
-    join('', map { $params{$_} ? $self->input_tag("", delete $params{$_}, id => "${id}_${_}", type => 'hidden') : '' } qw(customer_id)) .
-    $self->input_tag("", ref $value ? $value->displayable_name : '', id => "${id}_name", %params);
+    input_tag($name, (ref $value && $value->can('id') ? $value->id : ''), class => "@classes", type => 'hidden', id => $id) .
+    join('', map { $params{$_} ? input_tag("", delete $params{$_}, id => "${id}_${_}", type => 'hidden') : '' } qw(customer_id)) .
+    input_tag("", ref $value ? $value->displayable_name : '', id => "${id}_name", %params);
 
   $::request->layout->add_javascripts('autocomplete_project.js');
   $::request->presenter->need_reinit_widgets($id);
 
-  $self->html_tag('span', $ret, class => 'project_picker');
+  html_tag('span', $ret, class => 'project_picker');
 }
 
+sub picker { goto &project_picker };
+
 1;
 
 __END__
@@ -63,7 +66,7 @@ SL::Presenter::Project - Presenter module for project Rose::DB objects
 =head1 SYNOPSIS
 
   my $project = SL::DB::Manager::Project->get_first;
-  my $html    = SL::Presenter->get->project($project, display => 'inline');
+  my $html    = SL::Presenter::Project->project($project, display => 'inline');
 
 =head1 FUNCTIONS
 
index ae2164e..d1d7c3b 100644 (file)
@@ -2,10 +2,11 @@ package SL::Presenter::Record;
 
 use strict;
 
-use parent qw(Exporter);
+use SL::Presenter;
+use SL::Presenter::EscapedText qw(escape is_escaped);
 
 use Exporter qw(import);
-our @EXPORT = qw(grouped_record_list empty_record_list record_list record);
+our @EXPORT_OK = qw(grouped_record_list empty_record_list record_list record);
 
 use SL::Util;
 
@@ -20,63 +21,67 @@ sub _arrayify {
 }
 
 sub record {
-  my ($self, $record, %params) = @_;
+  my ($record, %params) = @_;
 
   my %grouped = _group_records( [ $record ] ); # pass $record as arrayref
   my $type    = (keys %grouped)[0];
 
-  return $self->sales_invoice(   $record, %params) if $type eq 'sales_invoices';
-  return $self->purchase_invoice($record, %params) if $type eq 'purchase_invoices';
-  return $self->ar_transaction(  $record, %params) if $type eq 'ar_transactions';
-  return $self->ap_transaction(  $record, %params) if $type eq 'ap_transactions';
-  return $self->gl_transaction(  $record, %params) if $type eq 'gl_transactions';
+  $record->presenter->sales_invoice(   $record, %params) if $type eq 'sales_invoices';
+  $record->presenter->purchase_invoice($record, %params) if $type eq 'purchase_invoices';
+  $record->presenter->ar_transaction(  $record, %params) if $type eq 'ar_transactions';
+  $record->presenter->ap_transaction(  $record, %params) if $type eq 'ap_transactions';
+  $record->presenter->gl_transaction(  $record, %params) if $type eq 'gl_transactions';
 
   return '';
 }
 
 sub grouped_record_list {
-  my ($self, $list, %params) = @_;
+  my ($list, %params) = @_;
 
   %params    = map { exists $params{$_} ? ($_ => $params{$_}) : () } qw(edit_record_links with_columns object_id object_model);
 
   my %groups = _sort_grouped_lists(_group_records($list));
   my $output = '';
 
-  $output .= _requirement_spec_list(       $self, $groups{requirement_specs},        %params) if $groups{requirement_specs};
-  $output .= _shop_order_list(             $self, $groups{shop_orders},              %params) if $groups{shop_orders};
-  $output .= _sales_quotation_list(        $self, $groups{sales_quotations},         %params) if $groups{sales_quotations};
-  $output .= _sales_order_list(            $self, $groups{sales_orders},             %params) if $groups{sales_orders};
-  $output .= _sales_delivery_order_list(   $self, $groups{sales_delivery_orders},    %params) if $groups{sales_delivery_orders};
-  $output .= _sales_invoice_list(          $self, $groups{sales_invoices},           %params) if $groups{sales_invoices};
-  $output .= _ar_transaction_list(         $self, $groups{ar_transactions},          %params) if $groups{ar_transactions};
+  $output .= _requirement_spec_list(       $groups{requirement_specs},        %params) if $groups{requirement_specs};
+  $output .= _shop_order_list(             $groups{shop_orders},              %params) if $groups{shop_orders};
+  $output .= _sales_quotation_list(        $groups{sales_quotations},         %params) if $groups{sales_quotations};
+  $output .= _sales_order_list(            $groups{sales_orders},             %params) if $groups{sales_orders};
+  $output .= _sales_delivery_order_list(   $groups{sales_delivery_orders},    %params) if $groups{sales_delivery_orders};
+  $output .= _sales_invoice_list(          $groups{sales_invoices},           %params) if $groups{sales_invoices};
+  $output .= _ar_transaction_list(         $groups{ar_transactions},          %params) if $groups{ar_transactions};
 
-  $output .= _request_quotation_list(      $self, $groups{purchase_quotations},      %params) if $groups{purchase_quotations};
-  $output .= _purchase_order_list(         $self, $groups{purchase_orders},          %params) if $groups{purchase_orders};
-  $output .= _purchase_delivery_order_list($self, $groups{purchase_delivery_orders}, %params) if $groups{purchase_delivery_orders};
-  $output .= _purchase_invoice_list(       $self, $groups{purchase_invoices},        %params) if $groups{purchase_invoices};
-  $output .= _ap_transaction_list(         $self, $groups{ap_transactions},          %params) if $groups{ap_transactions};
+  $output .= _request_quotation_list(      $groups{purchase_quotations},      %params) if $groups{purchase_quotations};
+  $output .= _purchase_order_list(         $groups{purchase_orders},          %params) if $groups{purchase_orders};
+  $output .= _purchase_delivery_order_list($groups{purchase_delivery_orders}, %params) if $groups{purchase_delivery_orders};
+  $output .= _purchase_invoice_list(       $groups{purchase_invoices},        %params) if $groups{purchase_invoices};
+  $output .= _ap_transaction_list(         $groups{ap_transactions},          %params) if $groups{ap_transactions};
 
-  $output .= _gl_transaction_list(         $self, $groups{gl_transactions},          %params) if $groups{gl_transactions};
+  $output .= _gl_transaction_list(         $groups{gl_transactions},          %params) if $groups{gl_transactions};
 
-  $output .= _bank_transactions(           $self, $groups{bank_transactions},        %params) if $groups{bank_transactions};
+  $output .= _bank_transactions(           $groups{bank_transactions},        %params) if $groups{bank_transactions};
 
-  $output .= _sepa_collection_list(        $self, $groups{sepa_collections},         %params) if $groups{sepa_collections};
-  $output .= _sepa_transfer_list(          $self, $groups{sepa_transfers},           %params) if $groups{sepa_transfers};
+  $output .= _sepa_collection_list(        $groups{sepa_collections},         %params) if $groups{sepa_collections};
+  $output .= _sepa_transfer_list(          $groups{sepa_transfers},           %params) if $groups{sepa_transfers};
 
-  $output .= _letter_list(                 $self, $groups{letters},                  %params) if $groups{letters};
+  $output .= _letter_list(                 $groups{letters},                  %params) if $groups{letters};
 
-  $output  = $self->render('presenter/record/grouped_record_list', %params, output => $output);
+  $output  = SL::Presenter->get->render('presenter/record/grouped_record_list', %params, output => $output);
 
   return $output;
 }
 
+sub grouped_list { goto &grouped_record_list }
+
 sub empty_record_list {
-  my ($self, %params) = @_;
-  return $self->grouped_record_list([], %params);
+  my (%params) = @_;
+  return grouped_record_list([], %params);
 }
 
+sub empty_list { goto &empty_record_list }
+
 sub record_list {
-  my ($self, $list, %params) = @_;
+  my ($list, %params) = @_;
 
   my @columns;
 
@@ -133,7 +138,7 @@ sub record_list {
         $cell{value} = $spec->{data}->($obj);
 
       } else {
-        $cell{value} = $rel_type && $self->can($rel_type)                                       ? $self->$rel_type($obj->$method, display => 'table-cell')
+        $cell{value} = ref $obj->$method && $obj->$method->isa('SL::DB::Object') && $obj->$method->presenter->can($rel_type) ? $obj->$method->presenter->$rel_type(display => 'table-cell')
                      : $type eq 'Rose::DB::Object::Metadata::Column::Date'                      ? $call->($obj, $method . '_as_date')
                      : $type =~ m/^Rose::DB::Object::Metadata::Column::(?:Float|Numeric|Real)$/ ? $::form->format_amount(\%::myconfig, $call->($obj, $method), 2)
                      : $type eq 'Rose::DB::Object::Metadata::Column::Boolean'                   ? $call->($obj, $method . '_as_bool_yn')
@@ -154,7 +159,7 @@ sub record_list {
            alignment => $data[0]->{columns}->[$_]->{alignment},
          }, (0..scalar(@columns) - 1);
 
-  return $self->render(
+  return SL::Presenter->get->render(
     'presenter/record/record_list',
     %params,
     TABLE_HEADER => \@header,
@@ -162,6 +167,8 @@ sub record_list {
   );
 }
 
+sub list { goto &record_list }
+
 #
 # private methods
 #
@@ -215,14 +222,14 @@ sub _sort_grouped_lists {
 }
 
 sub _requirement_spec_list {
-  my ($self, $list, %params) = @_;
+  my ($list, %params) = @_;
 
-  return $self->record_list(
+  return record_list(
     $list,
     title   => $::locale->text('Requirement specs'),
     type    => 'requirement_spec',
     columns => [
-      [ $::locale->text('Requirement spec number'), sub { $self->requirement_spec($_[0], display => 'table-cell') } ],
+      [ $::locale->text('Requirement spec number'), sub { $_[0]->presenter->requirement_spec(display => 'table-cell') } ],
       [ $::locale->text('Customer'),                'customer'                                                      ],
       [ $::locale->text('Title'),                   'title'                                                         ],
       [ $::locale->text('Project'),                 'project',                                                      ],
@@ -233,15 +240,15 @@ sub _requirement_spec_list {
 }
 
 sub _shop_order_list {
-  my ($self, $list, %params) = @_;
+  my ($list, %params) = @_;
 
-  return $self->record_list(
+  return record_list(
     $list,
     title   => $::locale->text('Shop Orders'),
     type    => 'shop_order',
     columns => [
       [ $::locale->text('Shop Order Date'),         sub { $_[0]->order_date->to_kivitendo }                         ],
-      [ $::locale->text('Shop Order Number'),       sub { $self->shop_order($_[0], display => 'table-cell') }       ],
+      [ $::locale->text('Shop Order Number'),       sub { $_[0]->presenter->shop_order(display => 'table-cell') }   ],
       [ $::locale->text('Transfer Date'),           'transfer_date'                                                 ],
       [ $::locale->text('Amount'),                  'amount'                                                        ],
     ],
@@ -250,15 +257,15 @@ sub _shop_order_list {
 }
 
 sub _sales_quotation_list {
-  my ($self, $list, %params) = @_;
+  my ($list, %params) = @_;
 
-  return $self->record_list(
+  return record_list(
     $list,
     title   => $::locale->text('Sales Quotations'),
     type    => 'sales_quotation',
     columns => [
       [ $::locale->text('Quotation Date'),          'transdate'                                                                ],
-      [ $::locale->text('Quotation Number'),        sub { $self->sales_quotation($_[0], display => 'table-cell') }   ],
+      [ $::locale->text('Quotation Number'),        sub { $_[0]->presenter->sales_quotation(display => 'table-cell') }         ],
       [ $::locale->text('Customer'),                'customer'                                                                 ],
       [ $::locale->text('Net amount'),              'netamount'                                                                ],
       [ $::locale->text('Transaction description'), 'transaction_description'                                                  ],
@@ -270,15 +277,15 @@ sub _sales_quotation_list {
 }
 
 sub _request_quotation_list {
-  my ($self, $list, %params) = @_;
+  my ($list, %params) = @_;
 
-  return $self->record_list(
+  return record_list(
     $list,
     title   => $::locale->text('Request Quotations'),
     type    => 'request_quotation',
     columns => [
       [ $::locale->text('Quotation Date'),          'transdate'                                                                ],
-      [ $::locale->text('Quotation Number'),        sub { $self->request_quotation($_[0], display => 'table-cell') }   ],
+      [ $::locale->text('Quotation Number'),        sub { $_[0]->presenter->request_quotation(display => 'table-cell') }       ],
       [ $::locale->text('Vendor'),                  'vendor'                                                                   ],
       [ $::locale->text('Net amount'),              'netamount'                                                                ],
       [ $::locale->text('Transaction description'), 'transaction_description'                                                  ],
@@ -290,15 +297,15 @@ sub _request_quotation_list {
 }
 
 sub _sales_order_list {
-  my ($self, $list, %params) = @_;
+  my ($list, %params) = @_;
 
-  return $self->record_list(
+  return record_list(
     $list,
     title   => $::locale->text('Sales Orders'),
     type    => 'sales_order',
     columns => [
       [ $::locale->text('Order Date'),              'transdate'                                                                ],
-      [ $::locale->text('Order Number'),            sub { $self->sales_order($_[0], display => 'table-cell') }   ],
+      [ $::locale->text('Order Number'),            sub { $_[0]->presenter->sales_order(display => 'table-cell') }             ],
       [ $::locale->text('Quotation'),               'quonumber' ],
       [ $::locale->text('Customer'),                'customer'                                                                 ],
       [ $::locale->text('Net amount'),              'netamount'                                                                ],
@@ -311,15 +318,15 @@ sub _sales_order_list {
 }
 
 sub _purchase_order_list {
-  my ($self, $list, %params) = @_;
+  my ($list, %params) = @_;
 
-  return $self->record_list(
+  return record_list(
     $list,
     title   => $::locale->text('Purchase Orders'),
     type    => 'purchase_order',
     columns => [
       [ $::locale->text('Order Date'),              'transdate'                                                                ],
-      [ $::locale->text('Order Number'),            sub { $self->purchase_order($_[0], display => 'table-cell') }   ],
+      [ $::locale->text('Order Number'),            sub { $_[0]->presenter->purchase_order(display => 'table-cell') }          ],
       [ $::locale->text('Request for Quotation'),   'quonumber' ],
       [ $::locale->text('Vendor'),                  'vendor'                                                                 ],
       [ $::locale->text('Net amount'),              'netamount'                                                                ],
@@ -332,15 +339,15 @@ sub _purchase_order_list {
 }
 
 sub _sales_delivery_order_list {
-  my ($self, $list, %params) = @_;
+  my ($list, %params) = @_;
 
-  return $self->record_list(
+  return record_list(
     $list,
     title   => $::locale->text('Sales Delivery Orders'),
     type    => 'sales_delivery_order',
     columns => [
       [ $::locale->text('Delivery Order Date'),     'transdate'                                                                ],
-      [ $::locale->text('Delivery Order Number'),   sub { $self->sales_delivery_order($_[0], display => 'table-cell') } ],
+      [ $::locale->text('Delivery Order Number'),   sub { $_[0]->presenter->sales_delivery_order(display => 'table-cell') }    ],
       [ $::locale->text('Order Number'),            'ordnumber' ],
       [ $::locale->text('Customer'),                'customer'                                                                 ],
       [ $::locale->text('Transaction description'), 'transaction_description'                                                  ],
@@ -353,15 +360,15 @@ sub _sales_delivery_order_list {
 }
 
 sub _purchase_delivery_order_list {
-  my ($self, $list, %params) = @_;
+  my ($list, %params) = @_;
 
-  return $self->record_list(
+  return record_list(
     $list,
     title   => $::locale->text('Purchase Delivery Orders'),
     type    => 'purchase_delivery_order',
     columns => [
       [ $::locale->text('Delivery Order Date'),     'transdate'                                                                ],
-      [ $::locale->text('Delivery Order Number'),   sub { $self->purchase_delivery_order($_[0], display => 'table-cell') } ],
+      [ $::locale->text('Delivery Order Number'),   sub { $_[0]->presenter->purchase_delivery_order(display => 'table-cell') } ],
       [ $::locale->text('Order Number'),            'ordnumber' ],
       [ $::locale->text('Vendor'),                  'vendor'                                                                 ],
       [ $::locale->text('Transaction description'), 'transaction_description'                                                  ],
@@ -374,16 +381,16 @@ sub _purchase_delivery_order_list {
 }
 
 sub _sales_invoice_list {
-  my ($self, $list, %params) = @_;
+  my ($list, %params) = @_;
 
-  return $self->record_list(
+  return record_list(
     $list,
     title   => $::locale->text('Sales Invoices'),
     type    => 'sales_invoice',
     columns => [
       [ $::locale->text('Invoice Date'),            'transdate'               ],
       [ $::locale->text('Type'),                    sub { $_[0]->displayable_type } ],
-      [ $::locale->text('Invoice Number'),          sub { $self->sales_invoice($_[0], display => 'table-cell') } ],
+      [ $::locale->text('Invoice Number'),          sub { $_[0]->presenter->sales_invoice(display => 'table-cell') } ],
       [ $::locale->text('Quotation Number'),        'quonumber' ],
       [ $::locale->text('Order Number'),            'ordnumber' ],
       [ $::locale->text('Customer'),                'customer'                ],
@@ -396,15 +403,15 @@ sub _sales_invoice_list {
 }
 
 sub _purchase_invoice_list {
-  my ($self, $list, %params) = @_;
+  my ($list, %params) = @_;
 
-  return $self->record_list(
+  return record_list(
     $list,
     title   => $::locale->text('Purchase Invoices'),
     type    => 'purchase_invoice',
     columns => [
       [ $::locale->text('Invoice Date'),                 'transdate'               ],
-      [ $::locale->text('Invoice Number'),               sub { $self->purchase_invoice($_[0], display => 'table-cell') } ],
+      [ $::locale->text('Invoice Number'),               sub { $_[0]->presenter->purchase_invoice(display => 'table-cell') } ],
       [ $::locale->text('Request for Quotation Number'), 'quonumber' ],
       [ $::locale->text('Order Number'),                 'ordnumber' ],
       [ $::locale->text('Vendor'),                       'vendor'                 ],
@@ -417,16 +424,16 @@ sub _purchase_invoice_list {
 }
 
 sub _ar_transaction_list {
-  my ($self, $list, %params) = @_;
+  my ($list, %params) = @_;
 
-  return $self->record_list(
+  return record_list(
     $list,
     title   => $::locale->text('AR Transactions'),
     type    => 'ar_transaction',
     columns => [
       [ $::locale->text('Invoice Date'),            'transdate'               ],
       [ $::locale->text('Type'),                    sub { $_[0]->displayable_type } ],
-      [ $::locale->text('Invoice Number'),          sub { $self->ar_transaction($_[0], display => 'table-cell') } ],
+      [ $::locale->text('Invoice Number'),          sub { $_[0]->presenter->ar_transaction(display => 'table-cell') } ],
       [ $::locale->text('Customer'),                'customer'                ],
       [ $::locale->text('Net amount'),              'netamount'               ],
       [ $::locale->text('Paid'),                    'paid'                    ],
@@ -437,15 +444,15 @@ sub _ar_transaction_list {
 }
 
 sub _ap_transaction_list {
-  my ($self, $list, %params) = @_;
+  my ($list, %params) = @_;
 
-  return $self->record_list(
+  return record_list(
     $list,
     title   => $::locale->text('AP Transactions'),
     type    => 'ap_transaction',
     columns => [
       [ $::locale->text('Invoice Date'),            'transdate'                      ],
-      [ $::locale->text('Invoice Number'),          sub { $self->ap_transaction($_[0 ], display => 'table-cell') } ],
+      [ $::locale->text('Invoice Number'),          sub { $_[0]->presenter->ap_transaction(display => 'table-cell') } ],
       [ $::locale->text('Vendor'),                  'vendor'                         ],
       [ $::locale->text('Net amount'),              'netamount'                      ],
       [ $::locale->text('Paid'),                    'paid'                           ],
@@ -456,32 +463,32 @@ sub _ap_transaction_list {
 }
 
 sub _gl_transaction_list {
-  my ($self, $list, %params) = @_;
+  my ($list, %params) = @_;
 
-  return $self->record_list(
+  return record_list(
     $list,
     title   => $::locale->text('GL Transactions'),
     type    => 'gl_transaction',
     columns => [
       [ $::locale->text('Transdate'),        'transdate'                                                    ],
       [ $::locale->text('Reference'),   'reference'                                                    ],
-      [ $::locale->text('Description'), sub { $self->gl_transaction($_[0 ], display => 'table-cell') } ],
+      [ $::locale->text('Description'), sub { $_[0]->presenter->gl_transaction(display => 'table-cell') } ],
     ],
     %params,
   );
 }
 
 sub _bank_transactions {
-  my ($self, $list, %params) = @_;
+  my ($list, %params) = @_;
 
-  return $self->record_list(
+  return record_list(
     $list,
     title   => $::locale->text('Bank transactions'),
     type    => 'bank_transactions',
     columns => [
       [ $::locale->text('Transdate'),            'transdate'                      ],
-      [ $::locale->text('Local Bank Code'),      sub { $self->bank_code($_[0]->local_bank_account) }  ],
-      [ $::locale->text('Local account number'), sub { $self->account_number($_[0]->local_bank_account) }  ],
+      [ $::locale->text('Local Bank Code'),      sub { $_[0]->local_bank_account->presenter->bank_code }  ],
+      [ $::locale->text('Local account number'), sub { $_[0]->local_bank_account->presenter->account_number }  ],
       [ $::locale->text('Remote Bank Code'),     'remote_bank_code' ],
       [ $::locale->text('Remote account number'),'remote_account_number' ],
       [ $::locale->text('Valutadate'),           'valutadate' ],
@@ -495,7 +502,7 @@ sub _bank_transactions {
 }
 
 sub _sepa_export_list {
-  my ($self, $list, %params) = @_;
+  my ($list, %params) = @_;
 
   my ($source, $destination) = $params{type} eq 'sepa_transfer' ? qw(our vc)                                 : qw(vc our);
   $params{title}             = $params{type} eq 'sepa_transfer' ? $::locale->text('Bank transfers via SEPA') : $::locale->text('Bank collections via SEPA');
@@ -503,7 +510,7 @@ sub _sepa_export_list {
 
   delete $params{edit_record_links};
 
-  return $self->record_list(
+  return record_list(
     $list,
     columns => [
       [ $::locale->text('Export Number'),    'sepa_export',                                  ],
@@ -520,25 +527,25 @@ sub _sepa_export_list {
 }
 
 sub _sepa_transfer_list {
-  my ($self, $list, %params) = @_;
-  _sepa_export_list($self, $list, %params, type => 'sepa_transfer');
+  my ($list, %params) = @_;
+  _sepa_export_list($list, %params, type => 'sepa_transfer');
 }
 
 sub _sepa_collection_list {
-  my ($self, $list, %params) = @_;
-  _sepa_export_list($self, $list, %params, type => 'sepa_collection');
+  my ($list, %params) = @_;
+  _sepa_export_list($list, %params, type => 'sepa_collection');
 }
 
 sub _letter_list {
-  my ($self, $list, %params) = @_;
+  my ($list, %params) = @_;
 
-  return $self->record_list(
+  return record_list(
     $list,
     title   => $::locale->text('Letters'),
     type    => 'letter',
     columns => [
       [ $::locale->text('Date'),         'date'                                                ],
-      [ $::locale->text('Letternumber'), sub { $self->letter($_[0], display => 'table-cell') } ],
+      [ $::locale->text('Letternumber'), sub { $_[0]->presenter->letter(display => 'table-cell') } ],
       [ $::locale->text('Customer'),     'customer'                                            ],
       [ $::locale->text('Reference'),    'reference'                                           ],
       [ $::locale->text('Subject'),      'subject'                                             ],
index 5cca5a8..9df9851 100644 (file)
@@ -2,26 +2,27 @@ package SL::Presenter::RequirementSpec;
 
 use strict;
 
-use parent qw(Exporter);
+use SL::Presenter::EscapedText qw(escape is_escaped);
 
 use Exporter qw(import);
-our @EXPORT = qw(requirement_spec);
+our @EXPORT_OK = qw(requirement_spec);
 
 use Carp;
 
 sub requirement_spec {
-  my ($self, $requirement_spec, %params) = @_;
+  my ($requirement_spec, %params) = @_;
 
   $params{display} ||= 'inline';
 
   croak "Unknown display type '$params{display}'" unless $params{display} =~ m/^(?:inline|table-cell)$/;
 
   my $text = join '', (
-    $params{no_link} ? '' : '<a href="controller.pl?action=RequirementSpec/show&amp;id=' . $self->escape($requirement_spec->id) . '">',
-    $self->escape($requirement_spec->id),
+    $params{no_link} ? '' : '<a href="controller.pl?action=RequirementSpec/show&amp;id=' . escape($requirement_spec->id) . '">',
+    escape($requirement_spec->id),
     $params{no_link} ? '' : '</a>',
   );
-  return $self->escaped_text($text);
+
+  is_scaped($text);
 }
 
 1;
index faafd26..58be195 100644 (file)
@@ -2,39 +2,45 @@ package SL::Presenter::RequirementSpecItem;
 
 use strict;
 
-use parent qw(Exporter);
+use SL::Presenter::Text qw(truncate);
 
 use Exporter qw(import);
-our @EXPORT = qw(requirement_spec_item_tree_node_title requirement_spec_item_jstree_data requirement_spec_item_dependency_list);
+our @EXPORT_OK = qw(requirement_spec_item_tree_node_title requirement_spec_item_jstree_data requirement_spec_item_dependency_list);
 
 use Carp;
 
 sub requirement_spec_item_tree_node_title {
-  my ($self, $item) = @_;
+  my ($item) = @_;
 
-  return join(' ', map { $_ || '' } ($item->fb_number, $self->truncate($item->parent_id ? $item->description_as_stripped_html : $item->title, at => 30)));
+  return join(' ', map { $_ || '' } ($item->fb_number, truncate($item->parent_id ? $item->description_as_stripped_html : $item->title, at => 30)));
 }
 
+sub tree_node_title { goto &requirement_spec_item_tree_node_title }
+
 sub requirement_spec_item_jstree_data {
-  my ($self, $item, %params) = @_;
+  my ($item, %params) = @_;
 
-  my @children = map { $self->requirement_spec_item_jstree_data($_, %params) } @{ $item->children_sorted };
+  my @children = map { requirement_spec_item_jstree_data($_, %params) } @{ $item->children_sorted };
   my $type     = !$item->parent_id ? 'section' : 'function-block';
   my $class    = $type . '-context-menu tooltip';
   $class      .= ' flagged' if $item->is_flagged;
 
   return {
-    data     => $self->requirement_spec_item_tree_node_title($item),
+    data     => requirement_spec_item_tree_node_title($item),
     metadata => { id =>         $item->id, type => $type },
     attr     => { id => "fb-" . $item->id, href => $params{href} || '#', class => $class, title => $item->content_excerpt },
     children => \@children,
   };
 }
 
+sub jstree_data { goto &requirement_spec_item_jstree_data }
+
 sub requirement_spec_item_dependency_list {
-  my ($self, $item) = @_;
+  my ($item) = @_;
 
   $::locale->language_join([ map { $_->fb_number } @{ $item->dependencies } ]);
 }
 
+sub dependency_list { goto &requirement_spec_item_dependency_list }
+
 1;
index 8a209d3..0f8c561 100644 (file)
@@ -2,17 +2,15 @@ package SL::Presenter::RequirementSpecTextBlock;
 
 use strict;
 
-use parent qw(Exporter);
-
 use Exporter qw(import);
-our @EXPORT = qw(requirement_spec_text_block_jstree_data);
+our @EXPORT_OK = qw(requirement_spec_text_block_jstree_data);
 
 use Carp;
 
 use SL::JSON;
 
 sub requirement_spec_text_block_jstree_data {
-  my ($self, $text_block, %params) = @_;
+  my ($text_block, %params) = @_;
 
   my $class  = 'text-block-context-menu tooltip';
   $class    .= ' flagged' if $text_block->is_flagged;
@@ -24,4 +22,6 @@ sub requirement_spec_text_block_jstree_data {
   };
 }
 
+sub jstree_data { goto &requirement_spec_text_block_jstree_data }
+
 1;
index ceb483f..e2b5423 100644 (file)
@@ -2,26 +2,26 @@ package SL::Presenter::SepaExport;
 
 use strict;
 
-use parent qw(Exporter);
+use SL::Presenter::EscapedText qw(escape is_escaped);
 
 use Exporter qw(import);
-our @EXPORT = qw(sepa_export);
+our @EXPORT_OK = qw(sepa_export);
 
 use Carp;
 
 sub sepa_export {
-  my ($self, $sepa_export, %params) = @_;
+  my ($sepa_export, %params) = @_;
 
   $params{display} ||= 'inline';
 
   croak "Unknown display type '$params{display}'" unless $params{display} =~ m/^(?:inline|table-cell)$/;
 
   my $text = join '', (
-    $params{no_link} ? '' : '<a href="sepa.pl?action=bank_transfer_edit&amp;vc=' . $self->escape($sepa_export->vc) . '&amp;id=' . $self->escape($sepa_export->id) . '">',
-    $self->escape($sepa_export->id),
+    $params{no_link} ? '' : '<a href="sepa.pl?action=bank_transfer_edit&amp;vc=' . escape($sepa_export->vc) . '&amp;id=' . escape($sepa_export->id) . '">',
+    escape($sepa_export->id),
     $params{no_link} ? '' : '</a>',
   );
-  return $self->escaped_text($text);
+  is_escaped($text);
 }
 
 1;
index 77cc86f..9c0bfee 100644 (file)
@@ -2,25 +2,26 @@ package SL::Presenter::ShopOrder;
 
 use strict;
 
-use parent qw(Exporter);
+use SL::Presenter::EscapedText qw(escape is_escaped);
 
 use Exporter qw(import);
-our @EXPORT = qw(shop_order);
+our @EXPORT_OK = qw(shop_order);
 
 use Carp;
 
 sub shop_order {
-  my ($self, $shop_order, $type, %params) = @_;
+  my ($shop_order, $type, %params) = @_;
 
   $params{display} ||= 'inline';
 
   croak "Unknown display type '$params{display}'" unless $params{display} =~ m/^(?:inline|table-cell)$/;
 
   my $text = join '', (
-    $params{no_link} ? '' : '<a href="controller.pl?action=ShopOrder/show&amp;id='.$self->escape($shop_order->id).'">',
-    $self->escape($shop_order->shop_ordernumber),
+    $params{no_link} ? '' : '<a href="controller.pl?action=ShopOrder/show&amp;id='. escape($shop_order->id) .'">',
+    escape($shop_order->shop_ordernumber),
     $params{no_link} ? '' : '</a>',
   );
-  return $self->escaped_text($text);
+
+  is_escaped($text);
 }
 1;
diff --git a/SL/Presenter/Simple.pm b/SL/Presenter/Simple.pm
new file mode 100644 (file)
index 0000000..0bd7cb4
--- /dev/null
@@ -0,0 +1,10 @@
+package SL::Presenter::Simple;
+
+use strict;
+
+use SL::Presenter::Tag qw(:ALL);
+use SL::Presenter::Text qw(:ALL);
+use SL::Presenter::EscapedText qw(:ALL);
+
+1;
+
index ace040a..4ed1d92 100644 (file)
@@ -3,11 +3,15 @@ package SL::Presenter::Tag;
 use strict;
 
 use SL::HTML::Restrict;
-
-use parent qw(Exporter);
+use SL::Presenter::EscapedText qw(escape);
 
 use Exporter qw(import);
-our @EXPORT = qw(html_tag input_tag hidden_tag javascript man_days_tag name_to_id select_tag checkbox_tag button_tag submit_tag ajax_submit_tag input_number_tag stringify_attributes restricted_html link);
+our @EXPORT_OK = qw(
+  html_tag input_tag hidden_tag javascript man_days_tag name_to_id select_tag
+  checkbox_tag button_tag submit_tag ajax_submit_tag input_number_tag
+  stringify_attributes restricted_html link
+);
+our %EXPORT_TAGS = (ALL => \@EXPORT_OK);
 
 use Carp;
 
@@ -37,57 +41,57 @@ sub _J {
 }
 
 sub stringify_attributes {
-  my ($self, %params) = @_;
+  my (%params) = @_;
 
   my @result = ();
   while (my ($name, $value) = each %params) {
     next unless $name;
     next if $_valueless_attributes{$name} && !$value;
     $value = '' if !defined($value);
-    push @result, $_valueless_attributes{$name} ? $self->escape($name) : $self->escape($name) . '="' . $self->escape($value) . '"';
+    push @result, $_valueless_attributes{$name} ? escape($name) : escape($name) . '="' . escape($value) . '"';
   }
 
   return @result ? ' ' . join(' ', @result) : '';
 }
 
 sub html_tag {
-  my ($self, $tag, $content, %params) = @_;
-  my $attributes = $self->stringify_attributes(%params);
+  my ($tag, $content, %params) = @_;
+  my $attributes = stringify_attributes(%params);
 
   return "<${tag}${attributes}>" unless defined($content);
   return "<${tag}${attributes}>${content}</${tag}>";
 }
 
 sub input_tag {
-  my ($self, $name, $value, %attributes) = @_;
+  my ($name, $value, %attributes) = @_;
 
   _set_id_attribute(\%attributes, $name);
   $attributes{type} ||= 'text';
 
-  return $self->html_tag('input', undef, %attributes, name => $name, value => $value);
+  html_tag('input', undef, %attributes, name => $name, value => $value);
 }
 
 sub hidden_tag {
-  my ($self, $name, $value, %attributes) = @_;
-  return $self->input_tag($name, $value, %attributes, type => 'hidden');
+  my ($name, $value, %attributes) = @_;
+  input_tag($name, $value, %attributes, type => 'hidden');
 }
 
 sub man_days_tag {
-  my ($self, $name, $object, %attributes) = @_;
+  my ($name, $object, %attributes) = @_;
 
   my $size           =  delete($attributes{size})   || 5;
   my $method         =  $name;
   $method            =~ s/^.*\.//;
 
-  my $time_selection =  $self->input_tag( "${name}_as_man_days_string", _call_on($object, "${method}_as_man_days_string"), %attributes, size => $size);
-  my $unit_selection =  $self->select_tag("${name}_as_man_days_unit",   [[ 'h', $::locale->text('h') ], [ 'man_day', $::locale->text('MD') ]],
+  my $time_selection = input_tag("${name}_as_man_days_string", _call_on($object, "${method}_as_man_days_string"), %attributes, size => $size);
+  my $unit_selection = select_tag("${name}_as_man_days_unit",   [[ 'h', $::locale->text('h') ], [ 'man_day', $::locale->text('MD') ]],
                                           %attributes, default => _call_on($object, "${method}_as_man_days_unit"));
 
   return $time_selection . $unit_selection;
 }
 
 sub name_to_id {
-  my ($self, $name) = @_;
+  my ($name) = @_;
 
   $name =~ s/\[\+?\]/ _id() /ge; # give constructs with [] or [+] unique ids
   $name =~ s/[^\w_]/_/g;
@@ -97,7 +101,7 @@ sub name_to_id {
 }
 
 sub select_tag {
-  my ($self, $name, $collection, %attributes) = @_;
+  my ($name, $collection, %attributes) = @_;
 
   _set_id_attribute(\%attributes, $name);
 
@@ -180,11 +184,11 @@ sub select_tag {
       push(@options, [$value, $title, $selected{$value} || $default]);
     }
 
-    return join '', map { $self->html_tag('option', $self->escape($_->[1]), value => $_->[0], selected => $_->[2]) } @options;
+    return join '', map { html_tag('option', escape($_->[1]), value => $_->[0], selected => $_->[2]) } @options;
   };
 
   my $code  = '';
-  $code    .= $self->html_tag('option', $self->escape($empty_title || ''), value => '') if $with_empty;
+  $code    .= html_tag('option', escape($empty_title || ''), value => '') if $with_empty;
 
   if (!$with_optgroups) {
     $code .= $list_to_code->($collection);
@@ -192,15 +196,15 @@ sub select_tag {
   } else {
     $code .= join '', map {
       my ($optgroup_title, $sub_collection) = @{ $_ };
-      $self->html_tag('optgroup', $list_to_code->($sub_collection), label => $optgroup_title)
+      html_tag('optgroup', $list_to_code->($sub_collection), label => $optgroup_title)
     } @{ $collection };
   }
 
-  return $self->html_tag('select', $code, %attributes, name => $name);
+  html_tag('select', $code, %attributes, name => $name);
 }
 
 sub checkbox_tag {
-  my ($self, $name, %attributes) = @_;
+  my ($name, %attributes) = @_;
 
   _set_id_attribute(\%attributes, $name);
 
@@ -216,27 +220,27 @@ sub checkbox_tag {
   }
 
   my $code  = '';
-  $code    .= $self->hidden_tag($name, 0, %attributes, id => $attributes{id} . '_hidden') if $for_submit;
-  $code    .= $self->html_tag('input', undef,  %attributes, name => $name, type => 'checkbox');
-  $code    .= $self->html_tag('label', $label, for => $attributes{id}) if $label;
-  $code    .= $self->javascript(qq|\$('#$attributes{id}').checkall('$checkall');|) if $checkall;
+  $code    .= hidden_tag($name, 0, %attributes, id => $attributes{id} . '_hidden') if $for_submit;
+  $code    .= html_tag('input', undef,  %attributes, name => $name, type => 'checkbox');
+  $code    .= html_tag('label', $label, for => $attributes{id}) if $label;
+  $code    .= javascript(qq|\$('#$attributes{id}').checkall('$checkall');|) if $checkall;
 
   return $code;
 }
 
 sub button_tag {
-  my ($self, $onclick, $value, %attributes) = @_;
+  my ($onclick, $value, %attributes) = @_;
 
   _set_id_attribute(\%attributes, $attributes{name}) if $attributes{name};
   $attributes{type} ||= 'button';
 
   $onclick = 'if (!confirm("'. _J(delete($attributes{confirm})) .'")) return false; ' . $onclick if $attributes{confirm};
 
-  return $self->html_tag('input', undef, %attributes, value => $value, onclick => $onclick);
+  html_tag('input', undef, %attributes, value => $value, onclick => $onclick);
 }
 
 sub submit_tag {
-  my ($self, $name, $value, %attributes) = @_;
+  my ($name, $value, %attributes) = @_;
 
   _set_id_attribute(\%attributes, $attributes{name}) if $attributes{name};
 
@@ -244,21 +248,21 @@ sub submit_tag {
     $attributes{onclick} = 'return confirm("'. _J(delete($attributes{confirm})) .'");';
   }
 
-  return $self->input_tag($name, $value, %attributes, type => 'submit', class => 'submit');
+  input_tag($name, $value, %attributes, type => 'submit', class => 'submit');
 }
 
 sub ajax_submit_tag {
-  my ($self, $url, $form_selector, $text, %attributes) = @_;
+  my ($url, $form_selector, $text, %attributes) = @_;
 
   $url           = _J($url);
   $form_selector = _J($form_selector);
   my $onclick    = qq|kivi.submit_ajax_form('${url}', '${form_selector}')|;
 
-  return $self->button_tag($onclick, $text, %attributes);
+  button_tag($onclick, $text, %attributes);
 }
 
 sub input_number_tag {
-  my ($self, $name, $value, %params) = @_;
+  my ($name, $value, %params) = @_;
 
   _set_id_attribute(\%params, $name);
   my @onchange = $params{onchange} ? (onChange => delete $params{onchange}) : ();
@@ -269,7 +273,7 @@ sub input_number_tag {
   $::request->layout->add_javascripts('kivi.Validator.js');
   $::request->presenter->need_reinit_widgets($params{id});
 
-  return $self->input_tag(
+  input_tag(
     $name, $::form->foramt_amount(\%::myconfig, $value),
     "data-validate" => "number",
     %params,
@@ -279,36 +283,36 @@ sub input_number_tag {
 
 
 sub javascript {
-  my ($self, $data) = @_;
-  return $self->html_tag('script', $data, type => 'text/javascript');
+  my ($data) = @_;
+  html_tag('script', $data, type => 'text/javascript');
 }
 
 sub _set_id_attribute {
   my ($attributes, $name, $unique) = @_;
 
   if (!delete($attributes->{no_id}) && !$attributes->{id}) {
-    $attributes->{id}  = name_to_id(undef, $name);
+    $attributes->{id}  = name_to_id($name);
     $attributes->{id} .= '_' . $attributes->{value} if $unique;
   }
 
-  return %{ $attributes };
+  %{ $attributes };
 }
 
 my $html_restricter;
 
 sub restricted_html {
-  my ($self, $value) = @_;
+  my ($value) = @_;
 
   $html_restricter ||= SL::HTML::Restrict->create;
   return $html_restricter->process($value);
 }
 
 sub link {
-  my ($self, $href, $content, %params) = @_;
+  my ($href, $content, %params) = @_;
 
   $href ||= '#';
 
-  return $self->html_tag('a', $content, %params, href => $href);
+  html_tag('a', $content, %params, href => $href);
 }
 
 1;
index af37323..539f380 100644 (file)
@@ -2,21 +2,22 @@ package SL::Presenter::Text;
 
 use strict;
 
-use parent qw(Exporter);
+use SL::Presenter::EscapedText qw(escape);
 
 use Exporter qw(import);
-our @EXPORT = qw(format_man_days simple_format truncate);
+our @EXPORT_OK = qw(format_man_days simple_format truncate);
+our %EXPORT_TAGS = (ALL => \@EXPORT_OK);
 
 use Carp;
 
 sub truncate {
-  my ($self, $text, %params) = @_;
+  my ($text, %params) = @_;
 
-  return Common::truncate($text, %params);
+  escape(Common::truncate($text, %params));
 }
 
 sub simple_format {
-  my ($self, $text, %params) = @_;
+  my ($text, %params) = @_;
 
   $text =  $::locale->quote_special_chars('HTML', $text || '');
 
@@ -28,18 +29,18 @@ sub simple_format {
 }
 
 sub format_man_days {
-  my ($self, $value, %params) = @_;
+  my ($value, %params) = @_;
 
   return '---' if $params{skip_zero} && !$value;
 
-  return $self->escape($::locale->text('#1 h', $::form->format_amount(\%::myconfig, $value, 2))) if 8.0 > $value;
+  return escape($::locale->text('#1 h', $::form->format_amount(\%::myconfig, $value, 2))) if 8.0 > $value;
 
   $value     /= 8.0;
   my $output  = $::locale->text('#1 MD', int($value));
   my $rest    = ($value - int($value)) * 8.0;
   $output    .= ' ' . $::locale->text('#1 h', $::form->format_amount(\%::myconfig, $rest)) if $rest > 0.0;
 
-  return $self->escape($output);
+  escape($output);
 }
 
 1;
index 940c42b..8931541 100644 (file)
@@ -8,6 +8,8 @@ use List::Util qw(max);
 use Scalar::Util qw(blessed);
 
 use SL::Presenter;
+use SL::Presenter::ALL;
+use SL::Presenter::Simple;
 use SL::Util qw(_hashify);
 
 use strict;
@@ -50,14 +52,18 @@ sub _call_presenter {
 
   my $presenter              = $::request->presenter;
 
-  if (!$presenter->can($method)) {
-    $::lxdebug->message(LXDebug::WARN(), "SL::Presenter has no method named '$method'!");
-    return '';
+  splice @args, -1, 1, %{ $args[-1] } if @args && (ref($args[-1]) eq 'HASH');
+
+  if (my $sub = SL::Presenter::Simple->can($method)) {
+    return $sub->(@args);
   }
 
-  splice @args, -1, 1, %{ $args[-1] } if @args && (ref($args[-1]) eq 'HASH');
+  if ($presenter->can($method)) {
+    return $presenter->$method(@args);
+  }
 
-  $presenter->$method(@args);
+  $::lxdebug->message(LXDebug::WARN(), "SL::Presenter has no method named '$method'!");
+  return;
 }
 
 sub name_to_id    { return _call_presenter('name_to_id',    @_); }
@@ -69,10 +75,6 @@ sub input_tag     { return _call_presenter('input_tag',     @_); }
 sub javascript    { return _call_presenter('javascript',    @_); }
 sub truncate      { return _call_presenter('truncate',      @_); }
 sub simple_format { return _call_presenter('simple_format', @_); }
-sub part_picker   { return _call_presenter('part_picker',   @_); }
-sub chart_picker  { return _call_presenter('chart_picker',  @_); }
-sub customer_vendor_picker   { return _call_presenter('customer_vendor_picker',   @_); }
-sub project_picker           { return _call_presenter('project_picker',           @_); }
 sub button_tag               { return _call_presenter('button_tag',               @_); }
 sub submit_tag               { return _call_presenter('submit_tag',               @_); }
 sub ajax_submit_tag          { return _call_presenter('ajax_submit_tag',          @_); }
index 44eeb43..9862577 100644 (file)
@@ -3,6 +3,8 @@ package SL::Template::Plugin::P;
 use base qw( Template::Plugin );
 
 use SL::Presenter;
+use SL::Presenter::ALL;
+use SL::Presenter::Simple;
 use SL::Presenter::EscapedText;
 
 use strict;
@@ -24,21 +26,28 @@ sub AUTOLOAD {
   our $AUTOLOAD;
 
   my ($self, @args) = @_;
-
   my $presenter     = SL::Presenter->get;
   my $method        =  $AUTOLOAD;
   $method           =~ s/.*:://;
 
   return '' if $method eq 'DESTROY';
 
-  if (!$presenter->can($method)) {
-    $::lxdebug->message(LXDebug::WARN(), "SL::Presenter has no method named '$method'!");
-    return '';
+  splice @args, -1, 1, %{ $args[-1] } if @args && (ref($args[-1]) eq 'HASH');
+
+  if ($SL::Presenter::ALL::presenters{$method}) {
+    return SL::Presenter::ALL::wrap($SL::Presenter::ALL::presenters{$method});
   }
 
-  splice @args, -1, 1, %{ $args[-1] } if @args && (ref($args[-1]) eq 'HASH');
+  if (my $sub = SL::Presenter::Simple->can($method)) {
+    return $sub->(@args);
+  }
+
+  if ($presenter->can($method)) {
+    return $presenter->$method(@args);
+  }
 
-  $presenter->$method(@args);
+  $::lxdebug->message(LXDebug::WARN(), "SL::Presenter has no method named '$method'!");
+  return;
 }
 
 1;
@@ -57,10 +66,10 @@ SL::Template::Plugin::P - Template plugin for the presentation layer
 
   [% USE P %]
 
-  Customer: [% P.customer(customer) %]
+  Customer: [% customer.presenter.customer %]
 
   Linked records:
-  [% P.grouped_record_list(RECORDS) %]
+  [% P.record.grouped_list(RECORDS) %]
 
 =head1 FUNCTIONS