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' },
27 my ($class, @slurp) = @_;
29 my $self = $class->SUPER::new(@slurp);
34 my $dbh = $::form->get_standard_dbh;
35 if(scalar(grep(/^Switzerland/, (selectrow_query($::form, $dbh, 'SELECT coa FROM defaults'))[0]))) {
36 @menu_files = qw(menus/erp_ch.ini);
38 @menu_files = qw(menus/erp.ini);
41 unshift @menu_files, 'menus/crm.ini' if $::instance_conf->crm_installed;
42 Menu->new(@menu_files);
45 sub init_auto_reload_resources_param {
46 return '' unless $::lx_office_conf{debug}->{auto_reload_resources};
47 return sprintf('?rand=%d-%d-%d', Time::HiRes::gettimeofday(), int(rand 1000000000000));
50 ##########################################
51 # inheritable/overridable
52 ##########################################
55 join '', map { $_->pre_content } $_[0]->sub_layouts;
59 join '', map { $_->start_content } $_[0]->sub_layouts;
63 join '', map { $_->end_content } $_[0]->sub_layouts;
67 join '', map { $_->post_content } $_[0]->sub_layouts;
70 sub stylesheets_inline {
71 uniq ( map { $_->stylesheets_inline } $_[0]->sub_layouts ),
72 @{ $_[0]->{stylesheets_inline} || [] };
75 sub javascripts_inline {
76 uniq ( map { $_->javascripts_inline } $_[0]->sub_layouts ),
77 @{ $_[0]->{javascripts_inline} || [] };
80 sub init_sub_layouts { [] }
83 #########################################
85 ########################################
93 push @{ $self->{stylesheets} ||= [] }, @_ if @_;
94 @{ $self->{stylesheets} ||= [] };
99 my $css_path = $self->get_stylesheet_for_user;
101 return uniq grep { $_ } map { $self->_find_stylesheet($_, $css_path) }
102 $self->use_stylesheet, map { $_->stylesheets } $self->sub_layouts;
105 sub _find_stylesheet {
106 my ($self, $stylesheet, $css_path) = @_;
108 return "$css_path/$stylesheet" if -f "$css_path/$stylesheet";
109 return "css/$stylesheet" if -f "css/$stylesheet";
110 return $stylesheet if -f $stylesheet;
113 sub get_stylesheet_for_user {
114 my $css_path = 'css';
115 if (my $user_style = $::myconfig{stylesheet}) {
116 $user_style =~ s/\.css$//; # nuke trailing .css, this is a remnant of pre 2.7.0 stylesheet handling
117 if (-d "$css_path/$user_style" &&
118 -f "$css_path/$user_style/main.css") {
119 $css_path = "$css_path/$user_style";
121 $css_path = "$css_path/kivitendo";
124 $css_path = "$css_path/kivitendo";
130 sub add_javascripts {
136 push @{ $self->{javascripts} ||= [] }, @_ if @_;
137 @{ $self->{javascripts} ||= [] };
143 return uniq grep { $_ } map { $self->_find_javascript($_) }
144 map({ $_->javascripts } $self->sub_layouts), $self->use_javascript;
147 sub _find_javascript {
148 my ($self, $javascript) = @_;
150 return "js/$javascript" if -f "js/$javascript";
151 return $javascript if -f $javascript;
155 ############################################
156 # track state of form header
157 ############################################
160 $_[0]{_header_done} = 1;
179 SL::Layout::Base - Base class for layouts
183 package SL::Layout::MyLayout;
185 use parent qw(SL::Layout::Base);
189 For a description of the external interface of layouts in general see
190 L<SL::Layout::Dispatcher>.
192 This is a base class for layouts in general. It provides the basic interface
193 and some capabilities to extend and cascade layouts.
196 =head1 IMPLEMENTING LAYOUT CALLBACKS
198 There are eight callbacks (C<pre_content>, C<post_content>, C<start_content>,
199 C<end_content>, C<stylesheets>, C<stylesheets_inline>, C<javascripts>,
200 C<javascripts_inline>) which are documented in L<SL::Layout::Dispatcher>. If
201 you are writing a new simple layout, you can just override some of them like
204 package SL::Layout::MyEvilLayout;
207 '<h1>This is MY page now</h1>'
211 '<p align="right"><small><em>Brought to you by my own layout class</em></small></p>'
215 To preserve the sanitizing effects of C<stylesheets> and C<javascripts> you should instead do the following:
218 $_[0]->add_stylesheets(qw(mystyle1.css mystyle2.css);
219 $_[0]->SUPER::stylesheets;
222 If you want to add something to a different layout, you should write a sub
223 layout and add it to the other layouts.
228 Layouts can be aggregated, so that common elements can be used in different
229 layouts. Currently this is used for the L<None|SL::Layout::None> sub layout,
230 which contains a lot of the stylesheets and javascripts necessary. Another
231 example is the L<Top|SL::Layout::Top> layout, which is used to generate a
232 common top bar for all menu types.
234 To add a sub layout to your layout just overwrite the sub_layout method:
236 package SL::Layout::MyFinalLayout;
238 sub init_sub_layout {
240 SL::Layout::None->new,
241 SL::Layout::MyEvilLayout->new,
245 You can also add a sublayout at runtime:
247 $layout->add_sub_layout(SL::Layout::SideBar->new);
249 The standard implementation for the callbacks will see to it that the contents
250 of all sub layouts will get rendered.
253 =head1 COMBINING SUB LAYOUTS AND OWN BEHAVIOUR
255 This is still somewhat rough, and improvements are welcome.
257 For the C<*_content> callbacks this works if you just remember to dispatch to the base method:
260 return $_[0]->render_status_bar .
261 $_[0]->SUPER::post_content
264 For the stylesheet and javascript callbacks things are hard, because of the
265 backwards compatibility, and the built-in sanity checks. The best way currently
266 is to just add your content and dispatch to the base method.
269 $_[0]->add_stylesheets(qw(mystyle1.css mystyle2.css);
270 $_[0]->SUPER::stylesheets;
273 =head1 GORY DETAILS ABOUT JAVASCRIPT AND STYLESHEET OVERLOADING
275 The original code used to store one stylesheet in C<< $form->{stylesheet} >> and
276 allowed/expected authors of potential C<bin/mozilla/> controllers to change
277 that into their own modified stylesheet.
279 This was at some point cleaned up into a method C<use stylesheet> which took a
280 string of space separated stylesheets and processed them into the response.
282 A lot of controllers are still using this method so the layout interface
283 supports it to change as little controller code as possible, while providing the
284 more intuitive C<add_stylesheets> method.
286 At the same time the following things need to be possible:
294 $layout->add_stylesheets(...)
296 Since add_stylesheets adds to C<< $self->{stylesheets} >> there must be a way to read
297 from it. Currently this is the deprecated C<use_stylesheet>.
303 A leaf layout should be able to override a callback to return a list.
309 C<stylesheets> needs to retain its sanitizing behaviour.
315 The standard implementation should be able to collect from sub layouts.
319 Preserving Inclusion Order
321 Since there is currently no standard way of mixing own content and including
322 sub layouts, this has to be done manually. Certain things like jquery get added
323 in L<SL::Layout::None> so that they get rendered first.
327 The current implementation provides no good candidate for overriding in sub
328 classes, which should be changed. The other points work pretty well.
332 None yet, if you don't count the horrible stylesheet/javascript interface.
336 Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>