Projekt

Allgemein

Profil

Herunterladen (9,99 KB) Statistiken
| Zweig: | Markierung: | Revision:
b6fd15a8 Sven Schöling
package SL::Layout::Base;
4a12c839 Sven Schöling
use strict;
9ad27e65 Sven Schöling
use parent qw(Rose::Object);
4a12c839 Sven Schöling
f64972cc Moritz Bunkus
use SL::Version;

cf96c588 Moritz Bunkus
use File::Slurp qw(read_file);
2bed2abd Sven Schöling
use List::MoreUtils qw(uniq);
bae050e9 Moritz Bunkus
use Time::HiRes qw();
2bed2abd Sven Schöling
4a12c839 Sven Schöling
use Rose::Object::MakeMethods::Generic (
e0a3b19e Sven Schöling
'scalar --get_set_init' => [ qw(menu auto_reload_resources_param sub_layouts_by_name) ],
4061ebb7 Sven Schöling
'scalar' => qw(focus),
a8814e0e Sven Schöling
'array' => [
'add_stylesheets_inline' => { interface => 'add', hash_key => 'stylesheets_inline' },
'add_javascripts_inline' => { interface => 'add', hash_key => 'javascripts_inline' },
8bed51b5 Sven Schöling
'sub_layouts', => { interface => 'get_set_init' },
'add_sub_layouts' => { interface => 'add', hash_key => 'sub_layouts' },
a8814e0e Sven Schöling
],
4a12c839 Sven Schöling
);

use SL::Menu;
9ad27e65 Sven Schöling
use SL::Presenter;
cf96c588 Moritz Bunkus
use SL::System::Process;
4a12c839 Sven Schöling
my %menu_cache;

sub new {
my ($class, @slurp) = @_;

my $self = $class->SUPER::new(@slurp);
}

sub init_menu {
b251cc22 Sven Schöling
SL::Menu->new('user');
4a12c839 Sven Schöling
}

e0a3b19e Sven Schöling
sub init_sublayouts_by_name {
{}
}

4abd6557 Sven Schöling
sub webpages_path {
"templates/webpages";
}

54fc4b1b Sven Schöling
sub allow_stylesheet_fallback {
1
}

e0a3b19e Sven Schöling
sub get {
$_[0]->sub_layouts;
67fbdebe Moritz Bunkus
return grep { $_ } ($_[0]->sub_layouts_by_name->{$_[1]});
e0a3b19e Sven Schöling
}

f64972cc Moritz Bunkus
sub _current_git_ref {
my $git_dir = SL::System::Process::exe_dir() . '/.git';
cf96c588 Moritz Bunkus
f64972cc Moritz Bunkus
return unless -d $git_dir;
cf96c588 Moritz Bunkus
f64972cc Moritz Bunkus
my $content = eval { scalar(read_file($git_dir . '/HEAD')) };
cf96c588 Moritz Bunkus
f64972cc Moritz Bunkus
return unless ($content // '') =~ m{\Aref: ([^\r\n]+)};
cf96c588 Moritz Bunkus
f64972cc Moritz Bunkus
$content = eval { scalar(read_file($git_dir . '/' . $1)) };
cf96c588 Moritz Bunkus
f64972cc Moritz Bunkus
return unless ($content // '') =~ m{\A([0-9a-fA-F]+)};
cf96c588 Moritz Bunkus
f64972cc Moritz Bunkus
return $1;
}

sub init_auto_reload_resources_param {
my $value;
cf96c588 Moritz Bunkus
f64972cc Moritz Bunkus
$value = sprintf('%d-%d-%d', Time::HiRes::gettimeofday(), int(rand 1000000000000)) if $::lx_office_conf{debug}->{auto_reload_resources};
$value ||= _current_git_ref();
$value ||= SL::Version->get_version;
5b8c6acb Sven Schöling
f64972cc Moritz Bunkus
return $value ? "?rand=${value}" : '';
bae050e9 Moritz Bunkus
}

0f179c9a Sven Schöling
##########################################
# inheritable/overridable
##########################################

4a12c839 Sven Schöling
sub pre_content {
a8814e0e Sven Schöling
join '', map { $_->pre_content } $_[0]->sub_layouts;
4a12c839 Sven Schöling
}

sub start_content {
a8814e0e Sven Schöling
join '', map { $_->start_content } $_[0]->sub_layouts;
4a12c839 Sven Schöling
}

sub end_content {
a8814e0e Sven Schöling
join '', map { $_->end_content } $_[0]->sub_layouts;
4a12c839 Sven Schöling
}

sub post_content {
a8814e0e Sven Schöling
join '', map { $_->post_content } $_[0]->sub_layouts;
4a12c839 Sven Schöling
}

0f179c9a Sven Schöling
sub stylesheets_inline {
2bed2abd Sven Schöling
uniq ( map { $_->stylesheets_inline } $_[0]->sub_layouts ),
a8814e0e Sven Schöling
@{ $_[0]->{stylesheets_inline} || [] };
0f179c9a Sven Schöling
}

a8814e0e Sven Schöling
sub javascripts_inline {
2bed2abd Sven Schöling
uniq ( map { $_->javascripts_inline } $_[0]->sub_layouts ),
a8814e0e Sven Schöling
@{ $_[0]->{javascripts_inline} || [] };
0f179c9a Sven Schöling
}

8bed51b5 Sven Schöling
sub init_sub_layouts { [] }

e0a3b19e Sven Schöling
sub init_sub_layouts_by_name { +{} }

a8814e0e Sven Schöling
0f179c9a Sven Schöling
#########################################
9836938f Sven Schöling
# Stylesheets
0f179c9a Sven Schöling
########################################

9836938f Sven Schöling
# override in sub layouts
sub static_stylesheets {}

4e7776aa Sven Schöling
sub add_stylesheets {
&use_stylesheet;
}

0f179c9a Sven Schöling
sub use_stylesheet {
my $self = shift;
push @{ $self->{stylesheets} ||= [] }, @_ if @_;
9836938f Sven Schöling
(map { $_->use_stylesheet } $self->sub_layouts), $self->static_stylesheets, @{ $self->{stylesheets} ||= [] };
0f179c9a Sven Schöling
}

4a12c839 Sven Schöling
sub stylesheets {
0f179c9a Sven Schöling
my ($self) = @_;
my $css_path = $self->get_stylesheet_for_user;

2bed2abd Sven Schöling
return uniq grep { $_ } map { $self->_find_stylesheet($_, $css_path) }
9836938f Sven Schöling
$self->use_stylesheet;
4a12c839 Sven Schöling
}

0f179c9a Sven Schöling
sub _find_stylesheet {
my ($self, $stylesheet, $css_path) = @_;

return "$css_path/$stylesheet" if -f "$css_path/$stylesheet";
54fc4b1b Sven Schöling
return "css/$stylesheet" if -f "css/$stylesheet" && $self->allow_stylesheet_fallback;
0f179c9a Sven Schöling
return $stylesheet if -f $stylesheet;
3b7f1516 Sven Schöling
return $stylesheet if $stylesheet =~ /^http/; # external
4a12c839 Sven Schöling
}

0f179c9a Sven Schöling
sub get_stylesheet_for_user {
my $css_path = 'css';
if (my $user_style = $::myconfig{stylesheet}) {
048f2af4 Geoffrey Richardson
$user_style =~ s/\.css$//; # nuke trailing .css, this is a remnant of pre 2.7.0 stylesheet handling
0f179c9a Sven Schöling
if (-d "$css_path/$user_style" &&
-f "$css_path/$user_style/main.css") {
$css_path = "$css_path/$user_style";
} else {
7e96b2d0 Moritz Bunkus
$css_path = "$css_path/kivitendo";
0f179c9a Sven Schöling
}
} else {
7e96b2d0 Moritz Bunkus
$css_path = "$css_path/kivitendo";
0f179c9a Sven Schöling
}

return $css_path;
}

9836938f Sven Schöling
#########################################
# Javascripts
########################################

# override in sub layouts
sub static_javascripts {}

4e7776aa Sven Schöling
sub add_javascripts {
&use_javascript
}
0f179c9a Sven Schöling
sub use_javascript {
my $self = shift;
push @{ $self->{javascripts} ||= [] }, @_ if @_;
9836938f Sven Schöling
map({ $_->use_javascript } $self->sub_layouts), $self->static_javascripts, @{ $self->{javascripts} ||= [] };
0f179c9a Sven Schöling
}

sub javascripts {
my ($self) = @_;

baf1e895 Moritz Bunkus
return uniq grep { $_ } map { $self->_find_javascript($_) }
9836938f Sven Schöling
$self->use_javascript;
0f179c9a Sven Schöling
}

sub _find_javascript {
my ($self, $javascript) = @_;

return "js/$javascript" if -f "js/$javascript";
return $javascript if -f $javascript;
3b7f1516 Sven Schöling
return $javascript if $javascript =~ /^http/;
4a12c839 Sven Schöling
}

2219d158 Sven Schöling
############################################
# track state of form header
############################################

sub header_done {
$_[0]{_header_done} = 1;
}

sub need_footer {
$_[0]{_header_done};
}

9ad27e65 Sven Schöling
sub presenter {
SL::Presenter->get;
}

4a12c839 Sven Schöling
1;
42f69828 Sven Schöling
__END__

=encoding utf-8

=head1 NAME

SL::Layout::Base - Base class for layouts

=head1 SYNOPSIS

package SL::Layout::MyLayout;

use parent qw(SL::Layout::Base);

=head1 DESCRIPTION

048f2af4 Geoffrey Richardson
For a description of the external interface of layouts in general see
42f69828 Sven Schöling
L<SL::Layout::Dispatcher>.

This is a base class for layouts in general. It provides the basic interface
and some capabilities to extend and cascade layouts.


=head1 IMPLEMENTING LAYOUT CALLBACKS

There are eight callbacks (C<pre_content>, C<post_content>, C<start_content>,
048f2af4 Geoffrey Richardson
C<end_content>, C<stylesheets>, C<stylesheets_inline>, C<javascripts>,
42f69828 Sven Schöling
C<javascripts_inline>) which are documented in L<SL::Layout::Dispatcher>. If
you are writing a new simple layout, you can just override some of them like
this:

package SL::Layout::MyEvilLayout;

sub pre_content {
'<h1>This is MY page now</h1>'
}

sub post_content {
'<p align="right"><small><em>Brought to you by my own layout class</em></small></p>'
}


To preserve the sanitizing effects of C<stylesheets> and C<javascripts> you should instead do the following:

sub stylesheets {
$_[0]->add_stylesheets(qw(mystyle1.css mystyle2.css);
$_[0]->SUPER::stylesheets;
}

If you want to add something to a different layout, you should write a sub
layout and add it to the other layouts.


=head1 SUB LAYOUTS

Layouts can be aggregated, so that common elements can be used in different
layouts. Currently this is used for the L<None|SL::Layout::None> sub layout,
which contains a lot of the stylesheets and javascripts necessary. Another
example is the L<Top|SL::Layout::Top> layout, which is used to generate a
common top bar for all menu types.

To add a sub layout to your layout just overwrite the sub_layout method:

package SL::Layout::MyFinalLayout;

sub init_sub_layout {
[
SL::Layout::None->new,
SL::Layout::MyEvilLayout->new,
]
}

You can also add a sublayout at runtime:

$layout->add_sub_layout(SL::Layout::SideBar->new);

The standard implementation for the callbacks will see to it that the contents
of all sub layouts will get rendered.


=head1 COMBINING SUB LAYOUTS AND OWN BEHAVIOUR

This is still somewhat rough, and improvements are welcome.

For the C<*_content> callbacks this works if you just remember to dispatch to the base method:

sub post_content {
return $_[0]->render_status_bar .
$_[0]->SUPER::post_content
}


9836938f Sven Schöling
Stylesheets and Javascripts can be added to every layout and sub-layout at
runtime with L<SL::Layout::Dispatcher/add_stylesheets> and
L<SL::Layout::Dispatcher/add_javascripts> (C<use_stylesheets> and
C<use_javascripts> are aliases for backwards compatibility):

$layout->add_stylesheets("custom.css");
$layout->add_javascripts("app.js", "widget.js");

Or they can be overwritten in sub layouts with the calls
L<SL::Layout::Displatcher/static_stylesheets> and
L<SL::Layout::Dispatcher/static_javascripts>:

sub static_stylesheets {
"custom.css"
}

sub static_javascripts {
qw(app.css widget.js)
42f69828 Sven Schöling
}

9836938f Sven Schöling
Note how these are relative to the base dirs of the currently selected
stylesheets. Javascripts are resolved relative to the C<js/> basedir.

Setting directly with C<stylesheets> and C<javascripts> is eprecated.


42f69828 Sven Schöling
=head1 GORY DETAILS ABOUT JAVASCRIPT AND STYLESHEET OVERLOADING

048f2af4 Geoffrey Richardson
The original code used to store one stylesheet in C<< $form->{stylesheet} >> and
42f69828 Sven Schöling
allowed/expected authors of potential C<bin/mozilla/> controllers to change
that into their own modified stylesheet.

This was at some point cleaned up into a method C<use stylesheet> which took a
string of space separated stylesheets and processed them into the response.

048f2af4 Geoffrey Richardson
A lot of controllers are still using this method so the layout interface
supports it to change as little controller code as possible, while providing the
42f69828 Sven Schöling
more intuitive C<add_stylesheets> method.

At the same time the following things need to be possible:

=over 4

=item 1.

Runtime additions.

$layout->add_stylesheets(...)

Since add_stylesheets adds to C<< $self->{stylesheets} >> there must be a way to read
from it. Currently this is the deprecated C<use_stylesheet>.

=item 2.

Overriding Callbacks

A leaf layout should be able to override a callback to return a list.

=item 3.

Sanitizing

048f2af4 Geoffrey Richardson
C<stylesheets> needs to retain its sanitizing behaviour.
42f69828 Sven Schöling
=item 4.

Aggregation

The standard implementation should be able to collect from sub layouts.

=item 5.

048f2af4 Geoffrey Richardson
Preserving Inclusion Order
42f69828 Sven Schöling
Since there is currently no standard way of mixing own content and including
sub layouts, this has to be done manually. Certain things like jquery get added
in L<SL::Layout::None> so that they get rendered first.

=back

The current implementation provides no good candidate for overriding in sub
classes, which should be changed. The other points work pretty well.

=head1 BUGS

3dc29e42 Sven Schöling
* stylesheet/javascript interface is a horrible mess.

* It's currently not possible to do compositor layouts without assupmtions
about the position of the content. That's because the content will return
control to the actual controller, so the layouts need to know where to split
pre- and post-content.
42f69828 Sven Schöling
=head1 AUTHOR

Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>

=cut