Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision d4458803

Von Moritz Bunkus vor fast 12 Jahren hinzugefügt

  • ID d445880375bce1462b8f9a8b1a502b34c296d41f
  • Vorgänger 2bc66c62
  • Nachfolger 5b5dbec0

SL::Presenter -- die neue Präsentationsschicht

Unterschiede anzeigen:

SL/Controller/Base.pm
9 9
use List::Util qw(first);
10 10
use SL::Request qw(flatten);
11 11
use SL::MoreCommon qw(uri_encode);
12
use SL::Presenter;
12 13

  
13 14
use Rose::Object::MakeMethods::Generic
14 15
(
......
62 63
  $options->{type}       = lc($options->{type} || 'html');
63 64
  $options->{no_layout}  = 1 if $options->{type} eq 'js';
64 65

  
65
  my $source;
66
  if ($options->{inline}) {
67
    $source = \$template;
68

  
69
  } elsif($options->{raw}) {
70
    $source =  $template;
71

  
72
  } else {
73
    $source = "templates/webpages/${template}." . $options->{type};
74
    croak "Template file ${source} not found" unless -f $source;
75
  }
76

  
77 66
  if (!$options->{partial} && !$options->{inline} && !$::form->{header}) {
78 67
    if ($options->{no_layout}) {
79 68
      $::form->{header} = 1;
......
88 77
    }
89 78
  }
90 79

  
91
  my %params = ( %locals,
92
                 AUTH          => $::auth,
93
                 FLASH         => $::form->{FLASH},
94
                 FORM          => $::form,
95
                 INSTANCE_CONF => $::instance_conf,
96
                 LOCALE        => $::locale,
97
                 LXCONFIG      => \%::lx_office_conf,
98
                 LXDEBUG       => $::lxdebug,
99
                 MYCONFIG      => \%::myconfig,
100
                 SELF          => $self,
101
               );
102

  
103 80
  my $output;
104
  if (!$options->{raw}) {
105
    my $parser = $self->_template_obj;
106
    $parser->process($source, \%params, \$output) || croak $parser->error;
81
  if ($options->{raw}) {
82
    $output = $$template;
107 83
  } else {
108
    $output = $$source;
84
    $output = $self->presenter->render(
85
      $template, $options,
86
      %locals,
87
      SELF => $self,
88
    );
109 89
  }
110 90

  
111 91
  print $output unless $options->{inline} || $options->{no_output};
......
129 109
  $file->close;
130 110
}
131 111

  
112
sub presenter {
113
  return SL::Presenter->get;
114
}
115

  
132 116
sub controller_name {
133 117
  my $class = ref($_[0]) || $_[0];
134 118
  $class    =~ s/^SL::Controller:://;
......
233 217
  }
234 218
}
235 219

  
236
sub _template_obj {
237
  my ($self) = @_;
238

  
239
  $self->{__basepriv_template_obj} ||=
240
    Template->new({ INTERPOLATE  => 0,
241
                    EVAL_PERL    => 0,
242
                    ABSOLUTE     => 1,
243
                    CACHE_SIZE   => 0,
244
                    PLUGIN_BASE  => 'SL::Template::Plugin',
245
                    INCLUDE_PATH => '.:templates/webpages',
246
                    COMPILE_EXT  => '.tcc',
247
                    COMPILE_DIR  => $::lx_office_conf{paths}->{userspath} . '/templates-cache',
248
                    ERROR        => 'templates/webpages/generic/exception.html',
249
                  }) || croak;
250

  
251
  return $self->{__basepriv_template_obj};
252
}
253

  
254 220
1;
255 221

  
256 222
__END__
......
369 335
parameters are slurped into C<%locals>.
370 336

  
371 337
What is rendered and how C<$template> is interpreted is determined by
372
the options I<type>, I<inline>, I<partial> and I<no_layout>.
338
the options I<type>, I<inline>, I<partial> and I<no_layout>. The
339
actual rendering is handled by L<SL::Presenter/render>.
373 340

  
374 341
If C<< $options->{inline} >> is trueish then C<$template> is a string
375 342
containing the template code to interprete. Additionally the output
376 343
will not be sent to the browser. Instead it is only returned to the
377 344
caller.
378 345

  
379
If C<< $options->{raw} >> is trueish, the function will treat the input as
380
already parsed, and will not filter the input through Template. Unlike
381
C<inline>, the input is taked as a reference.
346
If C<< $options->{raw} >> is trueish, the function will treat the
347
input as already parsed, and will not filter the input through
348
Template. This also means that L<SL::Presenter/render> is not
349
called either. Unlike C<inline>, the input is taken as a reference.
382 350

  
383 351
If C<< $options->{inline} >> is falsish then C<$template> is
384 352
interpreted as the name of a template file. It is prefixed with
......
400 368
trueish). Setting C<< $options->{no_layout} >> to trueish will prevent
401 369
this.
402 370

  
403
The template itself has access to the following variables:
404

  
405
=over 2
406

  
407
=item * C<AUTH> -- C<$::auth>
408

  
409
=item * C<FORM> -- C<$::form>
410

  
411
=item * C<LOCALE> -- C<$::locale>
412

  
413
=item * C<LXCONFIG> -- all parameters from C<config/kivitendo.conf>
414
with the same name they appear in the file (first level is the
415
section, second the actual variable, e.g. C<system.dbcharset>,
416
C<features.webdav> etc)
417

  
418
=item * C<LXDEBUG> -- C<$::lxdebug>
419

  
420
=item * C<MYCONFIG> -- C<%::myconfig>
421

  
422
=item * C<SELF> -- the controller instance
423

  
424
=item * All items from C<%locals>
425

  
426
=back
371
The template itself has access to several variables. These are listed
372
in the documentation to L<SL::Presenter/render>.
427 373

  
428 374
Unless C<< $options->{inline} >> is trueish the function will send the
429 375
output to the browser.
......
563 509
mechanism was used then this is not C<dispatch> but the actual method
564 510
name the dispatching resolved to.
565 511

  
512
=item C<presenter>
513

  
514
Returns the global presenter object by calling
515
L<SL::Presenter/get>.
516

  
566 517
=back
567 518

  
568 519
=head2 PRIVATE FUNCTIONS
SL/Presenter.pm
1
package SL::Presenter;
2

  
3
use strict;
4

  
5
use parent qw(Rose::Object);
6

  
7
use Carp;
8
use Template;
9

  
10
use SL::Presenter::CustomerVendor;
11
use SL::Presenter::DeliveryOrder;
12
use SL::Presenter::EscapedText;
13
use SL::Presenter::Invoice;
14
use SL::Presenter::Order;
15
use SL::Presenter::Project;
16
use SL::Presenter::Record;
17

  
18
sub get {
19
  $::request->{presenter} ||= SL::Presenter->new;
20
  return $::request->{presenter};
21
}
22

  
23
sub render {
24
  my $self               = shift;
25
  my $template           = shift;
26
  my ($options, %locals) = (@_ && ref($_[0])) ? @_ : ({ }, @_);
27

  
28
  $options->{type}       = lc($options->{type} || 'html');
29

  
30
  my $source;
31
  if ($options->{inline}) {
32
    $source = \$template;
33

  
34
  } else {
35
    $source = "templates/webpages/${template}." . $options->{type};
36
    croak "Template file ${source} not found" unless -f $source;
37
  }
38

  
39
  my %params = ( %locals,
40
                 AUTH          => $::auth,
41
                 FLASH         => $::form->{FLASH},
42
                 FORM          => $::form,
43
                 INSTANCE_CONF => $::instance_conf,
44
                 LOCALE        => $::locale,
45
                 LXCONFIG      => \%::lx_office_conf,
46
                 LXDEBUG       => $::lxdebug,
47
                 MYCONFIG      => \%::myconfig,
48
                 PRESENTER     => $self,
49
               );
50

  
51
  my $output;
52
  my $parser = $self->get_template;
53
  $parser->process($source, \%params, \$output) || croak $parser->error;
54

  
55
  return SL::Presenter::EscapedText->new(text => $output, is_escaped => 1);
56
}
57

  
58
sub get_template {
59
  my ($self) = @_;
60

  
61
  $self->{template} ||=
62
    Template->new({ INTERPOLATE  => 0,
63
                    EVAL_PERL    => 0,
64
                    ABSOLUTE     => 1,
65
                    CACHE_SIZE   => 0,
66
                    PLUGIN_BASE  => 'SL::Template::Plugin',
67
                    INCLUDE_PATH => '.:templates/webpages',
68
                    COMPILE_EXT  => '.tcc',
69
                    COMPILE_DIR  => $::lx_office_conf{paths}->{userspath} . '/templates-cache',
70
                    ERROR        => 'templates/webpages/generic/exception.html',
71
                  }) || croak;
72

  
73
  return $self->{template};
74
}
75

  
76
sub escape {
77
  my ($self, $text) = @_;
78

  
79
  return SL::Presenter::EscapedText->new(text => $text);
80
}
81

  
82
sub escaped_text {
83
  my ($self, $text) = @_;
84

  
85
  return SL::Presenter::EscapedText->new(text => $text, is_escaped => 1);
86
}
87

  
88
1;
89

  
90
__END__
91

  
92
=head1 NAME
93

  
94
SL::Presenter - presentation layer class
95

  
96
=head1 SYNOPSIS
97

  
98
  use SL::Presenter;
99
  my $presenter = SL::Presenter->get;
100

  
101
  # Lower-level template parsing:
102
  my $html = $presenter->render(
103
    'presenter/dir/template.html',
104
    var1 => 'value',
105
  );
106

  
107
  # Higher-level rendering of certain objects:
108
  use SL::DB::Customer;
109

  
110
  my $linked_customer_name = $presenter->customer($customer, display => 'table-cell');
111

  
112
  # Render a list of links to sales/purchase records:
113
  use SL::DB::Order;
114

  
115
  my $quotation = SL::DB::Manager::Order->get_first(where => { quotation => 1 });
116
  my $records   = $quotation->linked_records(direction => 'to');
117
  my $html      = $presenter->grouped_record_list($records);
118

  
119
=head1 CLASS FUNCTIONS
120

  
121
=over 4
122

  
123
=item C<get>
124

  
125
Returns the global presenter object and creates it if it doesn't exist
126
already.
127

  
128
=back
129

  
130
=head1 INSTANCE FUNCTIONS
131

  
132
=over 4
133

  
134
=item C<render $template, [ $options, ] %locals>
135

  
136
Renders the template C<$template>. Provides other variables than
137
C<Form::parse_html_template> does.
138

  
139
C<$options>, if present, must be a hash reference. All remaining
140
parameters are slurped into C<%locals>.
141

  
142
This is the backend function that L<SL::Controller::Base/render>
143
calls. The big difference is that the presenter's L<render> function
144
always returns the input and never sends anything to the browser while
145
the controller's function usually sends the result to the
146
controller. Therefore the presenter's L<render> function does not use
147
all of the parameters for controlling the output that the controller's
148
function does.
149

  
150
What is rendered and how C<$template> is interpreted is determined by
151
the options I<type> and I<inline>.
152

  
153
If C<< $options->{inline} >> is trueish then C<$template> is a string
154
containing the template code to interprete.
155

  
156
If C<< $options->{inline} >> is falsish then C<$template> is
157
interpreted as the name of a template file. It is prefixed with
158
"templates/webpages/" and postfixed with a file extension based on
159
C<< $options->{type} >>. C<< $options->{type} >> can be either C<html>
160
or C<js> and defaults to C<html>. An exception will be thrown if that
161
file does not exist.
162

  
163
The template itself has access to the following variables:
164

  
165
=over 2
166

  
167
=item * C<AUTH> -- C<$::auth>
168

  
169
=item * C<FORM> -- C<$::form>
170

  
171
=item * C<LOCALE> -- C<$::locale>
172

  
173
=item * C<LXCONFIG> -- all parameters from C<config/kivitendo.conf>
174
with the same name they appear in the file (first level is the
175
section, second the actual variable, e.g. C<system.dbcharset>,
176
C<features.webdav> etc)
177

  
178
=item * C<LXDEBUG> -- C<$::lxdebug>
179

  
180
=item * C<MYCONFIG> -- C<%::myconfig>
181

  
182
=item * C<SELF> -- the controller instance
183

  
184
=item * All items from C<%locals>
185

  
186
=back
187

  
188
The function will always return the output and never send anything to
189
the browser.
190

  
191
Example: Render a HTML template with a certain title and a few locals
192

  
193
  $presenter->render('todo/list',
194
                     title      => 'List TODO items',
195
                     TODO_ITEMS => SL::DB::Manager::Todo->get_all_sorted);
196

  
197
Example: Render a string and return its content for further processing
198
by the calling function.
199

  
200
  my $content = $presenter->render(
201
    '[% USE JavaScript %][% JavaScript.replace_with("#someid", "js/something") %]',
202
    { type => 'js' }
203
  );
204

  
205
=item C<escape $text>
206

  
207
Returns an HTML-escaped version of C<$text>. Instead of a string an
208
instance of the thin proxy-object L<SL::Presenter::EscapedText> is
209
returned.
210

  
211
It is safe to call C<escape> on an instance of
212
L<SL::Presenter::EscapedText>. This is a no-op (the same instance will
213
be returned).
214

  
215
=item C<escaped_text $text>
216

  
217
Returns an instance of L<SL::Presenter::EscapedText>. C<$text> is
218
assumed to be a string that has already been HTML-escaped.
219

  
220
It is safe to call C<escaped_text> on an instance of
221
L<SL::Presenter::EscapedText>. This is a no-op (the same instance will
222
be returned).
223

  
224
=item C<get_template>
225

  
226
Returns the global instance of L<Template> and creates it if it
227
doesn't exist already.
228

  
229
=back
230

  
231
=head1 AUTHOR
232

  
233
Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
234

  
235
=cut
SL/Presenter/EscapedText.pm
1
package SL::Presenter::EscapedText;
2

  
3
use strict;
4

  
5
use overload '""' => \&escaped;
6

  
7
sub new {
8
  my ($class, %params) = @_;
9

  
10
  return $params{text} if ref($params{text}) eq $class;
11

  
12
  my $self      = bless {}, $class;
13
  $self->{text} = $params{is_escaped} ? $params{text} : $::locale->quote_special_chars('HTML', $params{text});
14

  
15
  return $self;
16
}
17

  
18
sub escaped {
19
  my ($self) = @_;
20
  return $self->{text};
21
}
22

  
23
1;
24
__END__
25

  
26
=pod
27

  
28
=encoding utf8
29

  
30
=head1 NAME
31

  
32
SL::Presenter::EscapedText - Thin proxy object around HTML-escaped strings
33

  
34
=head1 SYNOPSIS
35

  
36
  use SL::Presenter::EscapedText;
37

  
38
  sub blackbox {
39
    my ($text) = @_;
40
    return SL::Presenter::EscapedText->new(text => $text);
41
  }
42

  
43
  sub build_output {
44
    my $output_of_other_component = blackbox('Hello & Goodbye');
45

  
46
    # The following is safe, text will not be escaped twice:
47
    return SL::Presenter::EscapedText->new(text => $output_of_other_component);
48
  }
49

  
50
  my $output = build_output();
51
  print "Yeah: $output\n";
52

  
53
=head1 OVERVIEW
54

  
55
Sometimes it's nice to let a sub-component build its own
56
representation. However, you always have to be very careful about
57
whose responsibility escaping is. Only the building function knows
58
enough about the structure to be able to HTML escape properly.
59

  
60
But higher functions should not have to care if the output is already
61
escaped -- they should be able to simply escape it again. Without
62
producing stuff like '&amp;amp;'.
63

  
64
Stringification is overloaded. It will return the same as L<escaped>.
65

  
66
This works together with the template plugin
67
L<SL::Template::Plugin::P> and its C<escape> method.
68

  
69
=head1 FUNCTIONS
70

  
71
=over 4
72

  
73
=item C<new %params>
74

  
75
Creates an instance of C<EscapedText>.
76

  
77
The parameter C<text> is the text to escape. If it is already an
78
instance of C<EscapedText> then C<$params{text}> is returned
79
unmodified.
80

  
81
Otherwise C<text> is HTML-escaped and stored in the new instance. This
82
can be overridden by setting C<$params{is_escaped}> to a trueish
83
value.
84

  
85
=item C<escaped>
86

  
87
Returns the escaped string (not an instance of C<EscapedText> but an
88
actual string).
89

  
90
=back
91

  
92
=head1 BUGS
93

  
94
Nothing here yet.
95

  
96
=head1 AUTHOR
97

  
98
Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
99

  
100
=cut
SL/Template/Plugin/L.pm
6 6
use List::Util qw(max);
7 7
use Scalar::Util qw(blessed);
8 8

  
9
use SL::Presenter;
10

  
9 11
use strict;
10 12

  
11 13
{ # This will give you an id for identifying html tags and such.
......
675 677
    },
676 678
  );
677 679

  
678
  my $output;
679
  $controller->_template_obj->process('templates/webpages/common/paginate.html', \%template_params, \$output);
680
  return $output;
680
  return SL::Presenter->get->render('common/paginate', %template_params);
681 681
}
682 682

  
683 683
1;
SL/Template/Plugin/P.pm
1
package SL::Template::Plugin::P;
2

  
3
use base qw( Template::Plugin );
4

  
5
use SL::Presenter;
6
use SL::Presenter::EscapedText;
7

  
8
use strict;
9

  
10
sub new {
11
  my ($class, $context, @args) = @_;
12

  
13
  return bless {
14
    CONTEXT => $context,
15
  }, $class;
16
}
17

  
18
sub escape {
19
  my ($self, $string) = @_;
20
  return SL::Presenter::EscapedText->new(text => $string);
21
}
22

  
23
sub AUTOLOAD {
24
  our $AUTOLOAD;
25

  
26
  my ($self, @args) = @_;
27

  
28
  my $presenter     = SL::Presenter->get;
29
  my $method        =  $AUTOLOAD;
30
  $method           =~ s/.*:://;
31

  
32
  return '' unless $presenter->can($method);
33

  
34
  splice @args, -1, 1, %{ $args[-1] } if @args && (ref($args[-1]) eq 'HASH');
35

  
36
  $presenter->$method(@args);
37
}
38

  
39
1;
40

  
41
__END__
42

  
43
=pod
44

  
45
=encoding utf8
46

  
47
=head1 NAME
48

  
49
SL::Template::Plugin::P - Template plugin for the presentation layer
50

  
51
=head1 SYNOPSIS
52

  
53
  [% USE P %]
54

  
55
  Customer: [% P.customer(customer) %]
56

  
57
  Linked records:
58
  [% P.grouped_record_list(RECORDS) %]
59

  
60
=head1 FUNCTIONS
61

  
62
=over 4
63

  
64
=item C<AUTOLOAD>
65

  
66
All unknown functions called on C<P> are forwarded to functions with
67
the same name in the global presenter object.
68

  
69
The presenter's functions use hashes for named-argument
70
passing. Unfortunately L<Template> groups named arguments into hash
71
references. This makes mixing intentional hash references and named
72
arguments a bit hairy. For example, the following calls from a
73
template are undistinguishable for a plugin:
74

  
75
  [% P.some_func({ arg1 => 42, arg2 => 'Charlie' }) %]
76
  [% P.some_func(arg1 => 42, arg2 => 'Charlie') %]
77
  [% P.some_func(arg1=42, arg2='Charlie') %]
78
  [% P.some_func(arg1=42, arg2='Charlie') %]
79

  
80
C<AUTOLOAD> tries to be clever and unpacks a hash reference into a
81
normal hash if and only if it is the very last parameter to the
82
function call.
83

  
84
Returns the result of the corresponding function in the presenter.
85

  
86
=item C<escape $text>
87

  
88
Returns an HTML-escaped version of C<$text>. Instead of a string an
89
instance of the thin proxy-object L<SL::Presenter::EscapedText> is
90
returned.
91

  
92
It is safe to call C<escape> on an instance of
93
L<SL::Presenter::EscapedText>. This is a no-op (the same instance will
94
be returned).
95

  
96
=back
97

  
98
=head1 BUGS
99

  
100
Nothing here yet.
101

  
102
=head1 AUTHOR
103

  
104
Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
105

  
106
=cut

Auch abrufbar als: Unified diff