1 package SL::Layout::Base;
4 use parent qw(Rose::Object);
6 use File::Slurp qw(read_file);
7 use List::MoreUtils qw(uniq);
10 use Rose::Object::MakeMethods::Generic (
11 'scalar --get_set_init' => [ qw(menu auto_reload_resources_param sub_layouts_by_name) ],
12 'scalar' => qw(focus),
14 'add_stylesheets_inline' => { interface => 'add', hash_key => 'stylesheets_inline' },
15 'add_javascripts_inline' => { interface => 'add', hash_key => 'javascripts_inline' },
16 'sub_layouts', => { interface => 'get_set_init' },
17 'add_sub_layouts' => { interface => 'add', hash_key => 'sub_layouts' },
23 use SL::System::Process;
28 my ($class, @slurp) = @_;
30 my $self = $class->SUPER::new(@slurp);
34 SL::Menu->new('user');
37 sub init_sublayouts_by_name {
43 return grep { $_ } ($_[0]->sub_layouts_by_name->{$_[1]});
46 sub init_auto_reload_resources_param {
47 if ($::lx_office_conf{debug}->{auto_reload_resources}) {
48 return sprintf('?rand=%d-%d-%d', Time::HiRes::gettimeofday(), int(rand 1000000000000));
51 if ($::lx_office_conf{debug}{git_commit_reload_resources}) {
52 my $git_dir = SL::System::Process::exe_dir() . '/.git';
54 return '' unless -d $git_dir;
56 my $content = eval { scalar(read_file($git_dir . '/HEAD')) };
58 return '' unless ($content // '') =~ m{\Aref: ([^\r\n]+)};
60 $content = eval { scalar(read_file($git_dir . '/' . $1)) };
62 return '' unless ($content // '') =~ m{\A([0-9a-fA-F]+)};
70 ##########################################
71 # inheritable/overridable
72 ##########################################
75 join '', map { $_->pre_content } $_[0]->sub_layouts;
79 join '', map { $_->start_content } $_[0]->sub_layouts;
83 join '', map { $_->end_content } $_[0]->sub_layouts;
87 join '', map { $_->post_content } $_[0]->sub_layouts;
90 sub stylesheets_inline {
91 uniq ( map { $_->stylesheets_inline } $_[0]->sub_layouts ),
92 @{ $_[0]->{stylesheets_inline} || [] };
95 sub javascripts_inline {
96 uniq ( map { $_->javascripts_inline } $_[0]->sub_layouts ),
97 @{ $_[0]->{javascripts_inline} || [] };
100 sub init_sub_layouts { [] }
102 sub init_sub_layouts_by_name { +{} }
105 #########################################
107 ########################################
109 # override in sub layouts
110 sub static_stylesheets {}
112 sub add_stylesheets {
118 push @{ $self->{stylesheets} ||= [] }, @_ if @_;
119 (map { $_->use_stylesheet } $self->sub_layouts), $self->static_stylesheets, @{ $self->{stylesheets} ||= [] };
124 my $css_path = $self->get_stylesheet_for_user;
126 return uniq grep { $_ } map { $self->_find_stylesheet($_, $css_path) }
127 $self->use_stylesheet;
130 sub _find_stylesheet {
131 my ($self, $stylesheet, $css_path) = @_;
133 return "$css_path/$stylesheet" if -f "$css_path/$stylesheet";
134 return "css/$stylesheet" if -f "css/$stylesheet";
135 return $stylesheet if -f $stylesheet;
136 return $stylesheet if $stylesheet =~ /^http/; # external
139 sub get_stylesheet_for_user {
140 my $css_path = 'css';
141 if (my $user_style = $::myconfig{stylesheet}) {
142 $user_style =~ s/\.css$//; # nuke trailing .css, this is a remnant of pre 2.7.0 stylesheet handling
143 if (-d "$css_path/$user_style" &&
144 -f "$css_path/$user_style/main.css") {
145 $css_path = "$css_path/$user_style";
147 $css_path = "$css_path/kivitendo";
150 $css_path = "$css_path/kivitendo";
156 #########################################
158 ########################################
160 # override in sub layouts
161 sub static_javascripts {}
163 sub add_javascripts {
169 push @{ $self->{javascripts} ||= [] }, @_ if @_;
170 map({ $_->use_javascript } $self->sub_layouts), $self->static_javascripts, @{ $self->{javascripts} ||= [] };
176 return uniq grep { $_ } map { $self->_find_javascript($_) }
177 $self->use_javascript;
180 sub _find_javascript {
181 my ($self, $javascript) = @_;
183 return "js/$javascript" if -f "js/$javascript";
184 return $javascript if -f $javascript;
185 return $javascript if $javascript =~ /^http/;
189 ############################################
190 # track state of form header
191 ############################################
194 $_[0]{_header_done} = 1;
213 SL::Layout::Base - Base class for layouts
217 package SL::Layout::MyLayout;
219 use parent qw(SL::Layout::Base);
223 For a description of the external interface of layouts in general see
224 L<SL::Layout::Dispatcher>.
226 This is a base class for layouts in general. It provides the basic interface
227 and some capabilities to extend and cascade layouts.
230 =head1 IMPLEMENTING LAYOUT CALLBACKS
232 There are eight callbacks (C<pre_content>, C<post_content>, C<start_content>,
233 C<end_content>, C<stylesheets>, C<stylesheets_inline>, C<javascripts>,
234 C<javascripts_inline>) which are documented in L<SL::Layout::Dispatcher>. If
235 you are writing a new simple layout, you can just override some of them like
238 package SL::Layout::MyEvilLayout;
241 '<h1>This is MY page now</h1>'
245 '<p align="right"><small><em>Brought to you by my own layout class</em></small></p>'
249 To preserve the sanitizing effects of C<stylesheets> and C<javascripts> you should instead do the following:
252 $_[0]->add_stylesheets(qw(mystyle1.css mystyle2.css);
253 $_[0]->SUPER::stylesheets;
256 If you want to add something to a different layout, you should write a sub
257 layout and add it to the other layouts.
262 Layouts can be aggregated, so that common elements can be used in different
263 layouts. Currently this is used for the L<None|SL::Layout::None> sub layout,
264 which contains a lot of the stylesheets and javascripts necessary. Another
265 example is the L<Top|SL::Layout::Top> layout, which is used to generate a
266 common top bar for all menu types.
268 To add a sub layout to your layout just overwrite the sub_layout method:
270 package SL::Layout::MyFinalLayout;
272 sub init_sub_layout {
274 SL::Layout::None->new,
275 SL::Layout::MyEvilLayout->new,
279 You can also add a sublayout at runtime:
281 $layout->add_sub_layout(SL::Layout::SideBar->new);
283 The standard implementation for the callbacks will see to it that the contents
284 of all sub layouts will get rendered.
287 =head1 COMBINING SUB LAYOUTS AND OWN BEHAVIOUR
289 This is still somewhat rough, and improvements are welcome.
291 For the C<*_content> callbacks this works if you just remember to dispatch to the base method:
294 return $_[0]->render_status_bar .
295 $_[0]->SUPER::post_content
299 Stylesheets and Javascripts can be added to every layout and sub-layout at
300 runtime with L<SL::Layout::Dispatcher/add_stylesheets> and
301 L<SL::Layout::Dispatcher/add_javascripts> (C<use_stylesheets> and
302 C<use_javascripts> are aliases for backwards compatibility):
304 $layout->add_stylesheets("custom.css");
305 $layout->add_javascripts("app.js", "widget.js");
307 Or they can be overwritten in sub layouts with the calls
308 L<SL::Layout::Displatcher/static_stylesheets> and
309 L<SL::Layout::Dispatcher/static_javascripts>:
311 sub static_stylesheets {
315 sub static_javascripts {
316 qw(app.css widget.js)
319 Note how these are relative to the base dirs of the currently selected
320 stylesheets. Javascripts are resolved relative to the C<js/> basedir.
322 Setting directly with C<stylesheets> and C<javascripts> is eprecated.
325 =head1 GORY DETAILS ABOUT JAVASCRIPT AND STYLESHEET OVERLOADING
327 The original code used to store one stylesheet in C<< $form->{stylesheet} >> and
328 allowed/expected authors of potential C<bin/mozilla/> controllers to change
329 that into their own modified stylesheet.
331 This was at some point cleaned up into a method C<use stylesheet> which took a
332 string of space separated stylesheets and processed them into the response.
334 A lot of controllers are still using this method so the layout interface
335 supports it to change as little controller code as possible, while providing the
336 more intuitive C<add_stylesheets> method.
338 At the same time the following things need to be possible:
346 $layout->add_stylesheets(...)
348 Since add_stylesheets adds to C<< $self->{stylesheets} >> there must be a way to read
349 from it. Currently this is the deprecated C<use_stylesheet>.
355 A leaf layout should be able to override a callback to return a list.
361 C<stylesheets> needs to retain its sanitizing behaviour.
367 The standard implementation should be able to collect from sub layouts.
371 Preserving Inclusion Order
373 Since there is currently no standard way of mixing own content and including
374 sub layouts, this has to be done manually. Certain things like jquery get added
375 in L<SL::Layout::None> so that they get rendered first.
379 The current implementation provides no good candidate for overriding in sub
380 classes, which should be changed. The other points work pretty well.
384 * stylesheet/javascript interface is a horrible mess.
386 * It's currently not possible to do compositor layouts without assupmtions
387 about the position of the content. That's because the content will return
388 control to the actual controller, so the layouts need to know where to split
389 pre- and post-content.
393 Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>