1 package SL::Layout::Base;
4 use parent qw(Rose::Object);
6 use List::MoreUtils qw(uniq);
9 use Rose::Object::MakeMethods::Generic (
10 'scalar --get_set_init' => [ qw(menu auto_reload_resources_param) ],
11 'scalar' => qw(focus),
13 'add_stylesheets_inline' => { interface => 'add', hash_key => 'stylesheets_inline' },
14 'add_javascripts_inline' => { interface => 'add', hash_key => 'javascripts_inline' },
15 'sub_layouts', => { interface => 'get_set_init' },
16 'add_sub_layouts' => { interface => 'add', hash_key => 'sub_layouts' },
26 my ($class, @slurp) = @_;
28 my $self = $class->SUPER::new(@slurp);
32 Menu->new('menu.ini');
35 sub init_auto_reload_resources_param {
36 return '' unless $::lx_office_conf{debug}->{auto_reload_resources};
37 return sprintf('?rand=%d-%d-%d', Time::HiRes::gettimeofday(), int(rand 1000000000000));
40 ##########################################
41 # inheritable/overridable
42 ##########################################
45 join '', map { $_->pre_content } $_[0]->sub_layouts;
49 join '', map { $_->start_content } $_[0]->sub_layouts;
53 join '', map { $_->end_content } $_[0]->sub_layouts;
57 join '', map { $_->post_content } $_[0]->sub_layouts;
60 sub stylesheets_inline {
61 uniq ( map { $_->stylesheets_inline } $_[0]->sub_layouts ),
62 @{ $_[0]->{stylesheets_inline} || [] };
65 sub javascripts_inline {
66 uniq ( map { $_->javascripts_inline } $_[0]->sub_layouts ),
67 @{ $_[0]->{javascripts_inline} || [] };
70 sub init_sub_layouts { [] }
73 #########################################
75 ########################################
83 push @{ $self->{stylesheets} ||= [] }, @_ if @_;
84 @{ $self->{stylesheets} ||= [] };
89 my $css_path = $self->get_stylesheet_for_user;
91 return uniq grep { $_ } map { $self->_find_stylesheet($_, $css_path) }
92 $self->use_stylesheet, map { $_->stylesheets } $self->sub_layouts;
95 sub _find_stylesheet {
96 my ($self, $stylesheet, $css_path) = @_;
98 return "$css_path/$stylesheet" if -f "$css_path/$stylesheet";
99 return "css/$stylesheet" if -f "css/$stylesheet";
100 return $stylesheet if -f $stylesheet;
103 sub get_stylesheet_for_user {
104 my $css_path = 'css';
105 if (my $user_style = $::myconfig{stylesheet}) {
106 $user_style =~ s/\.css$//; # nuke trailing .css, this is a remnand of pre 2.7.0 stylesheet handling
107 if (-d "$css_path/$user_style" &&
108 -f "$css_path/$user_style/main.css") {
109 $css_path = "$css_path/$user_style";
111 $css_path = "$css_path/kivitendo";
114 $css_path = "$css_path/kivitendo";
116 $::myconfig{css_path} = $css_path; # needed for menunew, FIXME: don't do this here
121 sub add_javascripts {
127 push @{ $self->{javascripts} ||= [] }, @_ if @_;
128 @{ $self->{javascripts} ||= [] };
134 return uniq grep { $_ } map { $self->_find_javascript($_) }
135 map({ $_->javascripts } $self->sub_layouts), $self->use_javascript;
138 sub _find_javascript {
139 my ($self, $javascript) = @_;
141 return "js/$javascript" if -f "js/$javascript";
142 return $javascript if -f $javascript;
146 ############################################
147 # track state of form header
148 ############################################
151 $_[0]{_header_done} = 1;
170 SL::Layout::Base - Base class for layouts
174 package SL::Layout::MyLayout;
176 use parent qw(SL::Layout::Base);
180 For a description about the external interface of layouts in general see
181 L<SL::Layout::Dispatcher>.
183 This is a base class for layouts in general. It provides the basic interface
184 and some capabilities to extend and cascade layouts.
187 =head1 IMPLEMENTING LAYOUT CALLBACKS
189 There are eight callbacks (C<pre_content>, C<post_content>, C<start_content>,
190 C<end_content>, C<stylesheets>, C<stylesheets_inline>, C<javscripts>,
191 C<javascripts_inline>) which are documented in L<SL::Layout::Dispatcher>. If
192 you are writing a new simple layout, you can just override some of them like
195 package SL::Layout::MyEvilLayout;
198 '<h1>This is MY page now</h1>'
202 '<p align="right"><small><em>Brought to you by my own layout class</em></small></p>'
206 To preserve the sanitizing effects of C<stylesheets> and C<javascripts> you should instead do the following:
209 $_[0]->add_stylesheets(qw(mystyle1.css mystyle2.css);
210 $_[0]->SUPER::stylesheets;
213 If you want to add something to a different layout, you should write a sub
214 layout and add it to the other layouts.
219 Layouts can be aggregated, so that common elements can be used in different
220 layouts. Currently this is used for the L<None|SL::Layout::None> sub layout,
221 which contains a lot of the stylesheets and javascripts necessary. Another
222 example is the L<Top|SL::Layout::Top> layout, which is used to generate a
223 common top bar for all menu types.
225 To add a sub layout to your layout just overwrite the sub_layout method:
227 package SL::Layout::MyFinalLayout;
229 sub init_sub_layout {
231 SL::Layout::None->new,
232 SL::Layout::MyEvilLayout->new,
236 You can also add a sublayout at runtime:
238 $layout->add_sub_layout(SL::Layout::SideBar->new);
240 The standard implementation for the callbacks will see to it that the contents
241 of all sub layouts will get rendered.
244 =head1 COMBINING SUB LAYOUTS AND OWN BEHAVIOUR
246 This is still somewhat rough, and improvements are welcome.
248 For the C<*_content> callbacks this works if you just remember to dispatch to the base method:
251 return $_[0]->render_status_bar .
252 $_[0]->SUPER::post_content
255 For the stylesheet and javascript callbacks things are hard, because of the
256 backwards compatibility, and the built-in sanity checks. The best way currently
257 is to just add your content and dispatch to the base method.
260 $_[0]->add_stylesheets(qw(mystyle1.css mystyle2.css);
261 $_[0]->SUPER::stylesheets;
264 =head1 GORY DETAILS ABOUT JAVASCRIPT AND STYLESHEET OVERLOADING
266 The original code used to store one stylehsheet in C<< $form->{stylesheet} >> and
267 allowed/expected authors of potential C<bin/mozilla/> controllers to change
268 that into their own modified stylesheet.
270 This was at some point cleaned up into a method C<use stylesheet> which took a
271 string of space separated stylesheets and processed them into the response.
273 A lot of controllers are still using this methods so the layout interface
274 supports it to change as few controller code as possible, while providing the
275 more intuitive C<add_stylesheets> method.
277 At the same time the following things need to be possible:
285 $layout->add_stylesheets(...)
287 Since add_stylesheets adds to C<< $self->{stylesheets} >> there must be a way to read
288 from it. Currently this is the deprecated C<use_stylesheet>.
294 A leaf layout should be able to override a callback to return a list.
300 C<stylesheets> needs to retain it's sanitizing behaviour.
306 The standard implementation should be able to collect from sub layouts.
310 Preserving of Inclusion Order
312 Since there is currently no standard way of mixing own content and including
313 sub layouts, this has to be done manually. Certain things like jquery get added
314 in L<SL::Layout::None> so that they get rendered first.
318 The current implementation provides no good candidate for overriding in sub
319 classes, which should be changed. The other points work pretty well.
323 None yet, if you don't count the horrible stylesheet/javascript interface.
327 Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>