Revision de968328
Von Tamino Steinert vor 10 Monaten hinzugefügt
SL/ClientJS.pm | ||
---|---|---|
10 | 10 |
use Rose::Object::MakeMethods::Generic |
11 | 11 |
( |
12 | 12 |
scalar => [ qw() ], |
13 |
'scalar --get_set_init' => [ qw(controller _actions _flash _flash_detail _no_flash_clear _error) ],
|
|
13 |
'scalar --get_set_init' => [ qw(controller _actions _error) ], |
|
14 | 14 |
); |
15 | 15 |
|
16 | 16 |
my %supported_methods = ( |
... | ... | |
115 | 115 |
redirect_to => 1, # window.location.href = <TARGET> |
116 | 116 |
save_file => 4, # kivi.save_file(<TARGET>, <ARGS>) |
117 | 117 |
|
118 |
flash => 2, # kivi.display_flash(<TARGET>, <ARGS>) |
|
119 |
flash_detail => 2, # kivi.display_flash_detail(<TARGET>, <ARGS>) |
|
120 |
clear_flash => 2, # kivi.clear_flash(<TARGET>, <ARGS>) |
|
118 |
# flash |
|
119 |
flash => -2, # kivi.Flash.display_flash.apply({}, action.slice(1, action.length)) |
|
120 |
clear_flash => 0, # kivi.Flash.clear_flash() |
|
121 |
show_flash => 0, # kivi.Flash.show() |
|
122 |
hide_flash => 0, # kivi.Flash.hide() |
|
123 |
|
|
121 | 124 |
reinit_widgets => 0, # kivi.reinit_widgets() |
122 | 125 |
run => -1, # kivi.run(<TARGET>, <ARGS>) |
123 | 126 |
run_once_for => 3, # kivi.run_once_for(<TARGET>, <ARGS>) |
... | ... | |
181 | 184 |
return []; |
182 | 185 |
} |
183 | 186 |
|
184 |
sub init__flash { |
|
185 |
return {}; |
|
186 |
} |
|
187 |
|
|
188 |
sub init__flash_detail { |
|
189 |
return {}; |
|
190 |
} |
|
191 |
|
|
192 |
sub init__error { |
|
193 |
return ''; |
|
194 |
} |
|
195 |
|
|
196 |
sub init__no_flash_clear { |
|
197 |
return ''; |
|
198 |
} |
|
199 |
|
|
200 | 187 |
sub to_json { |
201 | 188 |
my ($self) = @_; |
202 | 189 |
|
203 | 190 |
return SL::JSON::to_json({ error => $self->_error }) if $self->_error; |
204 |
return SL::JSON::to_json({ no_flash_clear => $self->_no_flash_clear, eval_actions => $self->_actions });
|
|
191 |
return SL::JSON::to_json({ eval_actions => $self->_actions }); |
|
205 | 192 |
} |
206 | 193 |
|
207 | 194 |
sub to_array { |
... | ... | |
234 | 221 |
return $self; |
235 | 222 |
} |
236 | 223 |
|
237 |
sub flash { |
|
238 |
my ($self, $type, @messages) = @_; |
|
239 |
|
|
240 |
my $message = join ' ', grep { $_ } @messages; |
|
241 |
|
|
242 |
if (!$self->_flash->{$type}) { |
|
243 |
$self->_flash->{$type} = [ 'flash', $type, $message ]; |
|
244 |
push @{ $self->_actions }, $self->_flash->{$type}; |
|
245 |
} else { |
|
246 |
$self->_flash->{$type}->[-1] .= ' ' . $message; |
|
247 |
} |
|
248 |
|
|
249 |
return $self; |
|
250 |
} |
|
251 |
|
|
252 |
sub flash_detail { |
|
253 |
my ($self, $type, @messages) = @_; |
|
254 |
|
|
255 |
my $message = join '<br>', grep { $_ } @messages; |
|
256 |
|
|
257 |
if (!$self->_flash_detail->{$type}) { |
|
258 |
$self->_flash_detail->{$type} = [ 'flash_detail', $type, $message ]; |
|
259 |
push @{ $self->_actions }, $self->_flash_detail->{$type}; |
|
260 |
} else { |
|
261 |
$self->_flash_detail->{$type}->[-1] .= ' ' . $message; |
|
262 |
} |
|
263 |
|
|
264 |
return $self; |
|
265 |
} |
|
266 |
|
|
267 | 224 |
sub no_flash_clear{ |
268 |
my ($self) = @_; |
|
269 |
$self->_no_flash_clear('1'); |
|
270 |
return $self; |
|
225 |
$_[0]; # noop for compatibility |
|
271 | 226 |
} |
272 | 227 |
|
273 | 228 |
sub error { |
... | ... | |
485 | 440 |
|
486 | 441 |
=over 4 |
487 | 442 |
|
488 |
=item C<flash $type, $message> |
|
489 |
|
|
490 |
Display a C<$message> in the flash of type C<$type>. Multiple calls of |
|
491 |
C<flash> on the same C<$self> will be merged by type. |
|
492 |
|
|
493 |
On the client side the flashes of all types will be cleared after each |
|
494 |
successful ClientJS call that did not end with C<$js-E<gt>error(...)>. |
|
495 |
This clearing can be switched of by the function C<no_flash_clear> |
|
496 |
|
|
497 |
=item C<flash_detail $type, $message> |
|
498 |
|
|
499 |
Display a detailed message C<$message> in the flash of type C<$type>. Multiple calls of |
|
500 |
C<flash_detail> on the same C<$self> will be merged by type. |
|
501 |
So the flash message can be hold short and the visibility of details can toggled by the user. |
|
502 |
|
|
503 |
=item C<no_flash_clear> |
|
443 |
=item C<flash $type, $message [, $details [, timestamp ]]> |
|
504 | 444 |
|
505 |
No automatic clearing of flash after successful ClientJS call |
|
445 |
Display a C<$message> in the flash of type C<$type> with optional |
|
446 |
C<$details>. |
|
506 | 447 |
|
507 | 448 |
=item C<error $message> |
508 | 449 |
|
SL/Controller/Flash.pm | ||
---|---|---|
1 |
package SL::Controller::Flash; |
|
2 |
|
|
3 |
use strict; |
|
4 |
use parent qw(SL::Controller::Base); |
|
5 |
|
|
6 |
use SL::Helper::Flash; |
|
7 |
|
|
8 |
sub action_reload { |
|
9 |
my ($self) = @_; |
|
10 |
|
|
11 |
$self->js->clear_flash; |
|
12 |
$self->js->flash(@$_) for SL::Helper::Flash::flash_contents; |
|
13 |
$self->js->show_flash; |
|
14 |
$self->js->render; |
|
15 |
} |
|
16 |
|
|
17 |
1; |
|
18 |
|
|
19 |
__END__ |
|
20 |
|
|
21 |
=encoding utf-8 |
|
22 |
|
|
23 |
=head1 NAME |
|
24 |
|
|
25 |
SL::Controller::Flash - Flash actions |
|
26 |
|
|
27 |
=head1 DESCRIPTION |
|
28 |
|
|
29 |
This controller contains actions that can be used to reload and control client |
|
30 |
side flash messages |
|
31 |
|
|
32 |
=head1 BUGS |
|
33 |
|
|
34 |
None yet :) |
|
35 |
|
|
36 |
=head1 AUTHOR |
|
37 |
|
|
38 |
Sven Schöling E<lt>sven.schoeling@opendynamic.deE<gt> |
|
39 |
|
|
40 |
=cut |
SL/Helper/Flash.pm | ||
---|---|---|
5 | 5 |
require Exporter; |
6 | 6 |
our @ISA = qw(Exporter); |
7 | 7 |
our @EXPORT = qw(flash flash_later); |
8 |
our @EXPORT_OK = qw(render_flash delay_flash);
|
|
8 |
our @EXPORT_OK = qw(delay_flash); |
|
9 | 9 |
|
10 | 10 |
my %valid_categories = ( |
11 | 11 |
map({$_ => 'info'} qw(information message)), |
... | ... | |
24 | 24 |
$::auth->set_session_value({ key => "FLASH", value => _store_flash($::auth->get_session_value('FLASH'), @_), auto_restore => 1 }); |
25 | 25 |
} |
26 | 26 |
|
27 |
sub delay_flash { |
|
28 |
my $store = $::form->{FLASH} || { }; |
|
29 |
flash_later($_ => @{ $store->{$_} || [] }) for keys %$store; |
|
27 |
sub flash_contents { |
|
28 |
return unless $::form; |
|
29 |
return unless $::form->{FLASH}; |
|
30 |
return unless 'ARRAY' eq ref $::form->{FLASH}; |
|
31 |
|
|
32 |
@{ $::form->{FLASH} } |
|
30 | 33 |
} |
31 | 34 |
|
32 |
sub render_flash { |
|
33 |
return $::form->parse_html_template('common/flash'); |
|
35 |
sub delay_flash { |
|
36 |
my $store = $::form->{FLASH} || []; |
|
37 |
flash_later(@{ $_ || [] }) for @$store; |
|
34 | 38 |
} |
35 | 39 |
|
36 | 40 |
# |
... | ... | |
38 | 42 |
# |
39 | 43 |
|
40 | 44 |
sub _store_flash { |
41 |
my $store = shift || { }; |
|
42 |
my $category = _check_category(+shift); |
|
45 |
my ($store, $type, $message, $details, $timestamp) = @_; |
|
46 |
$store //= [ ]; |
|
47 |
$timestamp //= time(); |
|
48 |
my $category = _check_category($type); |
|
43 | 49 |
|
44 |
$store->{ $category } ||= [ ]; |
|
45 |
push @{ $store->{ $category } }, @_; |
|
50 |
push @{ $store }, [ $type, $message, $details, $timestamp ]; |
|
46 | 51 |
|
47 | 52 |
return $store; |
48 | 53 |
} |
... | ... | |
68 | 73 |
|
69 | 74 |
=head1 SYNOPSIS |
70 | 75 |
|
76 |
use SL::Helper::Flash qw(flash flash_later delay_flash); |
|
77 |
|
|
78 |
# display in this request |
|
79 |
flash('info', 'Customer saved!'); |
|
80 |
flash('error', 'Something went wrong', "details about what went wrong"); |
|
81 |
flash('warning', 'this might not be a good idea'); |
|
82 |
|
|
83 |
# display after a redirect |
|
84 |
flash_later('info', 'Customer saved!'); |
|
85 |
flash_later('error', 'Something went wrong', "details about what went wrong"); |
|
86 |
flash_later('warning', 'this might not be a good idea'); |
|
87 |
|
|
88 |
# delay flash() calls to next request: |
|
89 |
delay_flash(); |
|
90 |
|
|
91 |
=head1 DESCRIPTION |
|
92 |
|
|
71 | 93 |
The flash is a store for messages that should be displayed to the |
72 | 94 |
user. Each message has a category which is usually C<information>, |
73 | 95 |
C<warning> or C<error>. The messages in each category are grouped and |
74 | 96 |
displayed in colors appropriate for their severity (e.g. errors in |
75 | 97 |
red). |
76 | 98 |
|
77 |
Messages are rendered either by calling the function C<render_flash> |
|
78 |
or by including the flash sub-template from a template with the |
|
79 |
following code: |
|
80 |
|
|
81 |
[%- INCLUDE 'common/flash.html' %] |
|
99 |
Messages are rendered by including the L<SL::Layout::Flash> sub layout. |
|
82 | 100 |
|
83 | 101 |
=head1 EXPORTS |
84 | 102 |
|
85 | 103 |
The functions L</flash> and L</flash_later> are always exported. |
86 | 104 |
|
87 |
The function L</render_flash> is only exported upon request. |
|
88 |
|
|
89 | 105 |
=head1 FUNCTIONS |
90 | 106 |
|
91 | 107 |
=over 4 |
92 | 108 |
|
93 |
=item C<flash $category, @messages>
|
|
109 |
=item C<flash $category, $message [, $details ]>
|
|
94 | 110 |
|
95 |
Stores messages for the given category. The category can be either
|
|
96 |
C<information>, C<warning> or C<error>. C<info> can also be used as an
|
|
97 |
alias for C<information>. |
|
111 |
Store a message with optional details for the given category. The category can
|
|
112 |
be either C<information>, C<warning> or C<error>. C<info> can also be used as
|
|
113 |
an alias for C<information>.
|
|
98 | 114 |
|
99 |
=item C<flash_later $category, @messages>
|
|
115 |
=item C<flash_later $category, $message [, $details ]>
|
|
100 | 116 |
|
101 |
Stores messages for the given category for the next request. The
|
|
102 |
category can be either C<information>, C<warning> or C<error>. C<info>
|
|
103 |
can also be used as an alias for C<information>. |
|
117 |
Store a message with optional details for the given category for the next
|
|
118 |
request. The category can be either C<information>, C<warning> or C<error>.
|
|
119 |
C<info> can also be used as an alias for C<information>.
|
|
104 | 120 |
|
105 |
The messages are stored in the user's session and restored upon the
|
|
121 |
The message is stored in the user's session and restored upon the
|
|
106 | 122 |
next request. Can be used for transmitting information over HTTP |
107 | 123 |
redirects. |
108 | 124 |
|
109 |
=item C<render_flash> |
|
110 |
|
|
111 |
Outputs the flash message by parsing the C<common/flash.html> template |
|
112 |
file. |
|
113 |
|
|
114 |
This function is not exported by default. |
|
115 |
|
|
116 | 125 |
=item C<delay_flash> |
117 | 126 |
|
118 | 127 |
Delays flash, as if all flash messages in this request would have been |
... | ... | |
120 | 129 |
|
121 | 130 |
Not exported by default. |
122 | 131 |
|
132 |
=item C<flash_contents> |
|
133 |
|
|
134 |
The contents of the current flash accumulator. |
|
135 |
|
|
123 | 136 |
=back |
124 | 137 |
|
125 | 138 |
=head1 AUTHOR |
SL/Layout/Admin.pm | ||
---|---|---|
7 | 7 |
use SL::Layout::None; |
8 | 8 |
use SL::Layout::Top; |
9 | 9 |
use SL::Layout::CssMenu; |
10 |
use SL::Layout::Flash; |
|
10 | 11 |
|
11 | 12 |
sub init_sub_layouts { |
13 |
$_[0]->sub_layouts_by_name->{flash} = SL::Layout::Flash->new; |
|
14 |
|
|
12 | 15 |
[ |
13 | 16 |
SL::Layout::None->new, |
14 | 17 |
SL::Layout::CssMenu->new(menu => SL::Menu->new('admin')), |
18 |
$_[0]->sub_layouts_by_name->{flash}, |
|
15 | 19 |
] |
16 | 20 |
} |
17 | 21 |
|
SL/Layout/Classic.pm | ||
---|---|---|
8 | 8 |
use SL::Layout::None; |
9 | 9 |
use SL::Layout::Split; |
10 | 10 |
use SL::Layout::ActionBar; |
11 |
use SL::Layout::Flash; |
|
11 | 12 |
use SL::Layout::Content; |
12 | 13 |
|
13 | 14 |
sub init_sub_layouts { |
14 | 15 |
$_[0]->sub_layouts_by_name->{actionbar} = SL::Layout::ActionBar->new; |
16 |
$_[0]->sub_layouts_by_name->{flash} = SL::Layout::Flash->new; |
|
15 | 17 |
|
16 | 18 |
[ |
17 | 19 |
SL::Layout::None->new, |
18 | 20 |
SL::Layout::Top->new, |
19 | 21 |
SL::Layout::Split->new( |
20 | 22 |
left => [ SL::Layout::MenuLeft->new ], |
21 |
right => [ $_[0]->sub_layouts_by_name->{actionbar}, SL::Layout::Content->new ], |
|
23 |
right => [ |
|
24 |
$_[0]->sub_layouts_by_name->{actionbar}, |
|
25 |
$_[0]->sub_layouts_by_name->{flash}, |
|
26 |
SL::Layout::Content->new, |
|
27 |
], |
|
22 | 28 |
) |
23 | 29 |
] |
24 | 30 |
} |
SL/Layout/Flash.pm | ||
---|---|---|
1 |
package SL::Layout::Flash; |
|
2 |
|
|
3 |
use strict; |
|
4 |
use parent qw(SL::Layout::Base); |
|
5 |
use SL::Presenter::EscapedText qw(escape_js); |
|
6 |
use SL::Helper::Flash; |
|
7 |
|
|
8 |
sub pre_content { |
|
9 |
'<div style="position:relative"><div id="layout_flash_container"></div></div>' |
|
10 |
} |
|
11 |
|
|
12 |
sub javascripts_inline { |
|
13 |
my ($self) = @_; |
|
14 |
|
|
15 |
my $js = ''; |
|
16 |
|
|
17 |
for (SL::Helper::Flash::flash_contents()) { |
|
18 |
next if $_->[3] + 60 < time(); # ignore entries from more than one minute ago |
|
19 |
$js .= defined $_->[2] |
|
20 |
? sprintf("kivi.Flash.display_flash('%s', '%s', '%s');", map { escape_js($_) } @$_[0,1,2] ) |
|
21 |
: sprintf("kivi.Flash.display_flash('%s', '%s');", map { escape_js($_) } @$_[0,1] ); |
|
22 |
} |
|
23 |
|
|
24 |
$js; |
|
25 |
} |
|
26 |
|
|
27 |
sub static_javascripts { |
|
28 |
'kivi.Flash.js' |
|
29 |
} |
|
30 |
|
|
31 |
|
|
32 |
1; |
SL/Layout/Javascript.pm | ||
---|---|---|
7 | 7 |
use SL::Layout::DHTMLMenu; |
8 | 8 |
use SL::Layout::Top; |
9 | 9 |
use SL::Layout::ActionBar; |
10 |
use SL::Layout::Flash; |
|
10 | 11 |
use SL::Layout::Content; |
11 | 12 |
|
12 | 13 |
use List::Util qw(max); |
... | ... | |
15 | 16 |
|
16 | 17 |
sub init_sub_layouts { |
17 | 18 |
$_[0]->sub_layouts_by_name->{actionbar} = SL::Layout::ActionBar->new; |
19 |
$_[0]->sub_layouts_by_name->{flash} = SL::Layout::Flash->new; |
|
18 | 20 |
[ |
19 | 21 |
SL::Layout::None->new, |
20 | 22 |
SL::Layout::Top->new, |
21 | 23 |
SL::Layout::DHTMLMenu->new, |
22 | 24 |
$_[0]->sub_layouts_by_name->{actionbar}, |
25 |
$_[0]->sub_layouts_by_name->{flash}, |
|
23 | 26 |
SL::Layout::Content->new, |
24 | 27 |
] |
25 | 28 |
} |
SL/Layout/Material.pm | ||
---|---|---|
6 | 6 |
use SL::Layout::None; |
7 | 7 |
use SL::Layout::MaterialMenu; |
8 | 8 |
use SL::Layout::MaterialStyle; |
9 |
use SL::Layout::Flash; |
|
9 | 10 |
use SL::Layout::Content; |
10 | 11 |
|
11 | 12 |
sub get_stylesheet_for_user { |
... | ... | |
22 | 23 |
SL::Layout::None->new, |
23 | 24 |
SL::Layout::MaterialStyle->new, |
24 | 25 |
SL::Layout::MaterialMenu->new, |
26 |
SL::Layout::Flash->new, |
|
25 | 27 |
SL::Layout::Content->new, |
26 | 28 |
] |
27 | 29 |
} |
SL/Layout/V3.pm | ||
---|---|---|
7 | 7 |
use SL::Layout::Top; |
8 | 8 |
use SL::Layout::CssMenu; |
9 | 9 |
use SL::Layout::ActionBar; |
10 |
use SL::Layout::Flash; |
|
10 | 11 |
use SL::Layout::Content; |
11 | 12 |
|
12 | 13 |
sub init_sub_layouts { |
13 | 14 |
$_[0]->sub_layouts_by_name->{actionbar} = SL::Layout::ActionBar->new; |
15 |
$_[0]->sub_layouts_by_name->{flash} = SL::Layout::Flash->new; |
|
14 | 16 |
|
15 | 17 |
[ |
16 | 18 |
SL::Layout::None->new, |
17 | 19 |
SL::Layout::Top->new, |
18 | 20 |
SL::Layout::CssMenu->new, |
19 | 21 |
$_[0]->sub_layouts_by_name->{actionbar}, |
22 |
$_[0]->sub_layouts_by_name->{flash}, |
|
20 | 23 |
SL::Layout::Content->new, |
21 | 24 |
] |
22 | 25 |
} |
css/kivitendo/main.css | ||
---|---|---|
607 | 607 |
font-weight: bold; |
608 | 608 |
} |
609 | 609 |
|
610 |
/* Flash message */ |
|
611 |
#layout_flash_container { |
|
612 |
position: absolute; |
|
613 |
z-index: 200; |
|
614 |
right: 20px; |
|
615 |
min-width: 30%; |
|
616 |
animation: fadein .5s; |
|
617 |
} |
|
618 |
@keyframes hop { |
|
619 |
to { transform: scale(1.01); } |
|
620 |
} |
|
621 |
@keyframes flash { |
|
622 |
from { background-color: black } |
|
623 |
} |
|
624 |
@keyframes fadein { |
|
625 |
from { opacity: 0; } |
|
626 |
to { opacity: 1; } |
|
627 |
} |
|
628 |
.layout-flash-message { |
|
629 |
margin-top: 5px; |
|
630 |
margin-bottom: 5px; |
|
631 |
padding: 5px; |
|
632 |
border-width: 1px; |
|
633 |
border-style: solid; |
|
634 |
/* animation: .5s ease-out .2s 1 normal none running flash;*/ |
|
635 |
} |
|
636 |
.layout-flash-body{ |
|
637 |
padding-top:5px; |
|
638 |
margin-top:5px; |
|
639 |
border-top: 1px dotted black; |
|
640 |
} |
|
641 |
.layout-flash-timestamp{ |
|
642 |
opacity: 0.75; |
|
643 |
} |
|
644 |
.layout-flash-details::before { |
|
645 |
content: ': '; |
|
646 |
} |
|
647 |
.layout-flash-type { |
|
648 |
font-weight: bold; |
|
649 |
float: right; |
|
650 |
} |
|
651 |
.layout-flash-error { |
|
652 |
background-color: #FFD6D6; |
|
653 |
border-color: #AE0014; |
|
654 |
} |
|
655 |
.layout-flash-ok { |
|
656 |
background-color: #ADFFB6; |
|
657 |
border-color: #007F0F; |
|
658 |
} |
|
659 |
.layout-flash-warning { |
|
660 |
background-color: #FFE8C7; |
|
661 |
border-color: #FF6600; |
|
662 |
} |
|
663 |
.layout-flash-info { |
|
664 |
background-color: #DCF2FF; |
|
665 |
border-color: #4690FF; |
|
666 |
} |
|
667 |
.layout-flash-title { |
|
668 |
font-weight: bold; |
|
669 |
} |
|
670 |
.layout-flash-remove { |
|
671 |
padding-right: 4px; |
|
672 |
} |
|
673 |
|
|
674 |
|
|
610 | 675 |
/* Admin section: the menu itself doesn't occupy space. So make room |
611 | 676 |
at the top of the div covering the whole admin area. */ |
612 | 677 |
body > div.admin { |
js/client_js.js | ||
---|---|---|
5 | 5 |
// SL/ClientJS.pm for instructions. |
6 | 6 |
|
7 | 7 |
namespace("kivi", function(ns) { |
8 |
ns.display_flash = function(type, message, noscroll) { |
|
9 |
$('#flash_' + type + '_content').text(message); |
|
10 |
$('#flash_' + type).show(); |
|
11 |
if (!noscroll && $('#frame-header')[0]) { |
|
12 |
$('#frame-header')[0].scrollIntoView(); |
|
13 |
} |
|
14 |
}; |
|
15 |
|
|
16 |
ns.display_flash_detail = function(type, message) { |
|
17 |
$('#flash_' + type + '_detail').html(message); |
|
18 |
$('#flash_' + type + '_disp').show(); |
|
19 |
}; |
|
20 |
|
|
21 |
ns.clear_flash = function(category , timeout) { |
|
22 |
window.setTimeout(function(){ |
|
23 |
$('#flash_' + category).hide(); |
|
24 |
$('#flash_detail_' + category).hide(); |
|
25 |
$('#flash_' + category + '_disp').hide(); |
|
26 |
$('#flash_' + category + '_content').empty(); |
|
27 |
$('#flash_' + category + '_detail').empty(); |
|
28 |
}, timeout); |
|
29 |
}; |
|
30 | 8 |
|
31 | 9 |
ns.eval_json_result = function(data) { |
32 | 10 |
if (!data) |
33 | 11 |
return; |
34 | 12 |
|
35 |
if (data.error) |
|
36 |
return ns.display_flash('error', data.error); |
|
13 |
if (data.error && ns.Flash)
|
|
14 |
return ns.Flash.display_flash('error', data.error);
|
|
37 | 15 |
|
38 |
if (!data.no_flash_clear) { |
|
39 |
$(['info', 'warning', 'error']).each(function(idx, category) { |
|
40 |
$('#flash_' + category).hide(); |
|
41 |
$('#flash_detail_' + category).hide(); |
|
42 |
$('#flash_' + category + '_disp').hide(); |
|
43 |
$('#flash_' + category + '_content').empty(); |
|
44 |
$('#flash_' + category + '_detail').empty(); |
|
45 |
}); |
|
46 |
} |
|
47 | 16 |
if ((data.js || '') !== '') |
48 | 17 |
// jshint -W061 |
49 | 18 |
eval(data.js); |
... | ... | |
152 | 121 |
// ## other stuff ## |
153 | 122 |
else if (action[0] == 'redirect_to') window.location.href = action[1]; |
154 | 123 |
else if (action[0] == 'save_file') kivi.save_file(action[1], action[2], action[3], action[4]); |
155 |
else if (action[0] == 'flash') kivi.display_flash(action[1], action[2]); |
|
156 |
else if (action[0] == 'flash_detail') kivi.display_flash_detail(action[1], action[2]); |
|
157 |
else if (action[0] == 'clear_flash') kivi.clear_flash(action[1], action[2]); |
|
124 |
|
|
125 |
// flash |
|
126 |
else if (action[0] == 'flash') kivi.Flash.display_flash.apply({}, action.slice(1, action.length)); |
|
127 |
else if (action[0] == 'clear_flash') kivi.Flash.clear_flash(); |
|
128 |
else if (action[0] == 'show_flash') kivi.Flash.show(); |
|
129 |
else if (action[0] == 'hide_flash') kivi.Flash.hide(); |
|
158 | 130 |
else if (action[0] == 'reinit_widgets') kivi.reinit_widgets(); |
159 | 131 |
else if (action[0] == 'run') kivi.run(action[1], action.slice(2, action.length)); |
160 | 132 |
else if (action[0] == 'run_once_for') kivi.run_once_for(action[1], action[2], action[3]); |
js/kivi.Flash.js | ||
---|---|---|
1 |
namespace("kivi.Flash", function(ns) { |
|
2 |
"use strict"; |
|
3 |
|
|
4 |
ns.type_to_title = { |
|
5 |
error: kivi.t8('Error'), |
|
6 |
warning: kivi.t8('Warning'), |
|
7 |
info: kivi.t8('Information'), |
|
8 |
ok: kivi.t8('Ok') |
|
9 |
}; |
|
10 |
|
|
11 |
ns.display_flash = function(type, message, details, timestamp) { |
|
12 |
let $dom = $('<div>'); |
|
13 |
$dom.addClass('layout-flash-' + type); |
|
14 |
$dom.addClass('layout-flash-message'); |
|
15 |
|
|
16 |
let $header = $('<div>'); |
|
17 |
$header.addClass('layout-flash-header'); |
|
18 |
|
|
19 |
let $remove = $('<span>✘</span>'); |
|
20 |
$remove.addClass('layout-flash-remove').addClass('cursor-pointer'); |
|
21 |
$remove.attr('alt', kivi.t8('Close Flash')); |
|
22 |
$header.append($remove); |
|
23 |
|
|
24 |
if (timestamp === undefined) { |
|
25 |
timestamp = new Date(); |
|
26 |
} else if (timestamp > 0) { |
|
27 |
timestamp = new Date(timestamp * 1000); |
|
28 |
} |
|
29 |
|
|
30 |
let $time = $('<span>'); |
|
31 |
$time.addClass('layout-flash-timestamp'); |
|
32 |
$time.text(kivi.format_time(timestamp)); |
|
33 |
$header.append($time); |
|
34 |
|
|
35 |
let $type = $('<span>'); |
|
36 |
$type.addClass('layout-flash-type'); |
|
37 |
$type.text(ns.type_to_title[type]); |
|
38 |
$header.append($type); |
|
39 |
|
|
40 |
let $body = $('<div>'); |
|
41 |
$body.addClass('layout-flash-body'); |
|
42 |
|
|
43 |
if (message !== undefined && message !== null) { |
|
44 |
let $message = $('<span>'); |
|
45 |
$message.addClass('layout-flash-content'); |
|
46 |
$message.html(message); |
|
47 |
$body.append($message); |
|
48 |
} |
|
49 |
|
|
50 |
if (details !== undefined && details !== null) { |
|
51 |
let $details = $('<span>'); |
|
52 |
$details.addClass('layout-flash-details'); |
|
53 |
$details.html(details); |
|
54 |
$body.append($details); |
|
55 |
} |
|
56 |
|
|
57 |
$dom.append($header); |
|
58 |
$dom.append($body); |
|
59 |
|
|
60 |
$("#layout_flash_container").append($dom); |
|
61 |
|
|
62 |
// fadeout after 1min |
|
63 |
$dom.delay(60000).fadeOut('slow'); |
|
64 |
|
|
65 |
ns.show(); |
|
66 |
}; |
|
67 |
|
|
68 |
ns.display_flash_detail = function(type, message) { |
|
69 |
$('#flash_' + type + '_disp').show(); |
|
70 |
}; |
|
71 |
|
|
72 |
ns.clear_flash = function(category, timeout) { |
|
73 |
if (timeout === undefined) { |
|
74 |
ns.clear_flash_now(category); |
|
75 |
} else { |
|
76 |
window.setTimeout(function(){ |
|
77 |
ns.clear_flash_now(category); |
|
78 |
}, timeout); |
|
79 |
} |
|
80 |
}; |
|
81 |
|
|
82 |
ns.clear_flash_now = function(category) { |
|
83 |
if (category) { |
|
84 |
$('div.layout-flash-' + category).remove(); |
|
85 |
} else { |
|
86 |
$('div.layout-flash-message').remove(); |
|
87 |
} |
|
88 |
}; |
|
89 |
|
|
90 |
ns.remove_entry = function(e) { |
|
91 |
$(e.target).closest('div.layout-flash-message').remove(); |
|
92 |
}; |
|
93 |
|
|
94 |
ns.toggle = function() { |
|
95 |
$('#layout_flash_container').toggle(); |
|
96 |
}; |
|
97 |
ns.show = function() { |
|
98 |
$('#layout_flash_container').show(); |
|
99 |
}; |
|
100 |
ns.hide = function() { |
|
101 |
$('#layout_flash_container').hide(); |
|
102 |
}; |
|
103 |
ns.reload_flash = function() { |
|
104 |
$.get("controller.pl", { action: "Flash/reload" }, kivi.eval_json_result); |
|
105 |
}; |
|
106 |
|
|
107 |
ns.reinit_widgets = function() { |
|
108 |
|
|
109 |
}; |
|
110 |
}); |
|
111 |
|
|
112 |
$(function() { |
|
113 |
"use strict"; |
|
114 |
// dispatch to kivi.Flash for compatibility |
|
115 |
kivi.display_flash = kivi.Flash.display_flash; |
|
116 |
kivi.display_flash_detail = kivi.Flash.display_flash_detail; |
|
117 |
kivi.empty_flash = kivi.Flash.empty_flash; |
|
118 |
kivi.clear_flash = kivi.Flash.clear_flash; |
|
119 |
|
|
120 |
$('.layout-flash-toggle').click(kivi.Flash.toggle); |
|
121 |
$('#layout_flash_container').on('click', '.layout-flash-remove', kivi.Flash.remove_entry); |
|
122 |
}); |
scripts/generate_client_js_actions.tpl | ||
---|---|---|
5 | 5 |
// SL/ClientJS.pm for instructions. |
6 | 6 |
|
7 | 7 |
namespace("kivi", function(ns) { |
8 |
ns.display_flash = function(type, message, noscroll) { |
|
9 |
$('#flash_' + type + '_content').text(message); |
|
10 |
$('#flash_' + type).show(); |
|
11 |
if (!noscroll) { |
|
12 |
$('#frame-header')[0].scrollIntoView(); |
|
13 |
} |
|
14 |
}; |
|
15 |
|
|
16 |
ns.display_flash_detail = function(type, message) { |
|
17 |
$('#flash_' + type + '_detail').html(message); |
|
18 |
$('#flash_' + type + '_disp').show(); |
|
19 |
}; |
|
20 |
|
|
21 |
ns.clear_flash = function(category , timeout) { |
|
22 |
window.setTimeout(function(){ |
|
23 |
$('#flash_' + category).hide(); |
|
24 |
$('#flash_detail_' + category).hide(); |
|
25 |
$('#flash_' + category + '_disp').hide(); |
|
26 |
$('#flash_' + category + '_content').empty(); |
|
27 |
$('#flash_' + category + '_detail').empty(); |
|
28 |
}, timeout); |
|
29 |
}; |
|
30 | 8 |
|
31 | 9 |
ns.eval_json_result = function(data) { |
32 | 10 |
if (!data) |
33 | 11 |
return; |
34 | 12 |
|
35 | 13 |
if (data.error) |
36 |
return ns.display_flash('error', data.error); |
|
14 |
return ns.Flash.display_flash('error', data.error);
|
|
37 | 15 |
|
38 |
if (!data.no_flash_clear) { |
|
39 |
$(['info', 'warning', 'error']).each(function(idx, category) { |
|
40 |
$('#flash_' + category).hide(); |
|
41 |
$('#flash_detail_' + category).hide(); |
|
42 |
$('#flash_' + category + '_disp').hide(); |
|
43 |
$('#flash_' + category + '_content').empty(); |
|
44 |
$('#flash_' + category + '_detail').empty(); |
|
45 |
}); |
|
46 |
} |
|
47 | 16 |
if ((data.js || '') !== '') |
48 | 17 |
// jshint -W061 |
49 | 18 |
eval(data.js); |
t/flash_migration/deprecated_calls.t | ||
---|---|---|
1 |
use strict; |
|
2 |
|
|
3 |
use lib 't'; |
|
4 |
|
|
5 |
use Support::Files; |
|
6 |
use Support::TestSetup; |
|
7 |
|
|
8 |
use File::Spec; |
|
9 |
use File::Slurp; |
|
10 |
use Template; |
|
11 |
use Template::Provider; |
|
12 |
use Test::More; |
|
13 |
|
|
14 |
my @deprecated_calls_perl = ( |
|
15 |
qr/render_flash/, |
|
16 |
qr/flash_detail/, |
|
17 |
qr{common/flash}, |
|
18 |
qr/flash (?:_later)? \( \s* ' (?: error | warning | information ) ' \s* , \s* \@ /x, |
|
19 |
); |
|
20 |
|
|
21 |
my @deprecated_calls_js = ( |
|
22 |
qr/kivi\.display_flash/, |
|
23 |
qr/kivi\.clear_flash/, |
|
24 |
); |
|
25 |
|
|
26 |
for my $file (@Support::Files::files) { |
|
27 |
open my $fh, '<', $file or die "can't open $file"; |
|
28 |
|
|
29 |
while (my $line = <$fh>) { |
|
30 |
for my $re (@deprecated_calls_perl) { |
|
31 |
if ($line =~ $re) { |
|
32 |
ok 0, "$file contains '$&', most likely due to incomplete merge of the layout flash feature. Consult the documentation in this test script"; |
|
33 |
} |
|
34 |
} |
|
35 |
} |
|
36 |
|
|
37 |
ok 1, $file; |
|
38 |
} |
|
39 |
|
|
40 |
for my $file (@Support::Files::javascript_files) { |
|
41 |
next if $file eq 'js/kivi.Flash.js'; |
|
42 |
|
|
43 |
open my $fh, '<', $file or die "can't open $file"; |
|
44 |
|
|
45 |
while (my $line = <$fh>) { |
|
46 |
for my $re (@deprecated_calls_js) { |
|
47 |
if ($line =~ $re) { |
|
48 |
TODO: { local $TODO = 'clean up compatibility kivi.display_flash and kivi.clear_flash'; |
|
49 |
ok 0, "$file contains '$&', most likely due to incomplete merge of the layout flash feature. Consult the documentation in this test script"; |
|
50 |
} |
|
51 |
} |
|
52 |
} |
|
53 |
} |
|
54 |
|
|
55 |
ok 1, $file; |
|
56 |
} |
|
57 |
|
|
58 |
done_testing(); |
|
59 |
|
|
60 |
|
|
61 |
__END__ |
|
62 |
|
|
63 |
=encoding utf-8 |
|
64 |
|
|
65 |
=head1 NAME |
|
66 |
|
|
67 |
t/flash_migration&deprecated_calls.t |
|
68 |
|
|
69 |
=head1 DESCRIPTION |
|
70 |
|
|
71 |
Okay, if this script triggers, this is what needs to be done: |
|
72 |
|
|
73 |
In Javascript: |
|
74 |
|
|
75 |
- all javascript calls to "kivi.display_flash" and "kivi.clear_flash" need to |
|
76 |
be redirected to "kivi.Flash" |
|
77 |
- kivi.display_flash_details doesn't exist any more, instead details can now be |
|
78 |
passed as a third argument to kivi.Flash.display_flash |
|
79 |
|
|
80 |
In html: |
|
81 |
|
|
82 |
- There is no common/flash.html template any more, since the layout handles |
|
83 |
that now. Any attempt to render it needs to be removed. |
|
84 |
|
|
85 |
In Perl: |
|
86 |
|
|
87 |
- flash_detail doesn't exist any more, instead flash() and flash_later() take a |
|
88 |
third argument for details |
|
89 |
- render_flash doesn't exist anymore and is now handled by the layout. |
|
90 |
- flash('error', @errrs) and similar must be called in a loop: flash('error', $_) for @errors |
|
91 |
|
|
92 |
=cut |
templates/design40_webpages/common/flash.html | ||
---|---|---|
1 |
[% USE HTML %] |
|
2 |
[% USE LxERP %] |
|
3 |
[% USE T8 %] |
|
4 |
|
|
5 |
[% BLOCK output %] |
|
6 |
<div id="flash_[% type %]" class="flash_message flash_message_[% type %]" [% IF !messages || !messages.size %] style="display: none" [% END %]> |
|
7 |
<div class="icon-container"> |
|
8 |
<a href="#" onclick='$("#flash_[% type %]_content").empty();$("#flash_[% type %]_detail").empty();$("#flash_[% type %]").hide()' class="icon-close"> ✕</a> |
|
9 |
<span id="flash_[% type %]_disp" class="display" style="display: none"> |
|
10 |
<a href="#" onclick='$("#flash_detail_[% type %]").toggle();' class="button"> [% 'Details' | $T8 %] </a> |
|
11 |
</span> |
|
12 |
</div> |
|
13 |
<div class="message-container"> |
|
14 |
<span class="flash_title">[% title %]:</span> |
|
15 |
<div class="flash_notification"> |
|
16 |
<span id="flash_[% type %]_content" class="content"> |
|
17 |
[% FOREACH message = messages %] [% HTML.escape(message) %] [% UNLESS loop.last %]<br>[% END %] [% END %] |
|
18 |
</span> |
|
19 |
<div id="flash_detail_[% type %]" class="detail" style="display: none"> |
|
20 |
<span id="flash_[% type %]_detail"></span> |
|
21 |
<a href="#" style="float:left" onclick='$("#flash_detail_[% type %]").hide()' class="icon-close">✕</a> |
|
22 |
</div> |
|
23 |
</div><!-- /.flash_notification --> |
|
24 |
</div> |
|
25 |
</div> |
|
26 |
[% END #BLOCK output %] |
|
27 |
|
|
28 |
[% PROCESS output title=LxERP.t8('Error') type='error' messages = FLASH.error %] |
|
29 |
[% PROCESS output title=LxERP.t8('Warning') type='warning' messages = FLASH.warning %] |
|
30 |
[% PROCESS output title=LxERP.t8('Information') type='info' messages = FLASH.info %] |
|
31 |
[% PROCESS output title=LxERP.t8('Ok') type='ok' messages = FLASH.ok %] |
templates/webpages/common/flash.html | ||
---|---|---|
1 |
[%- USE HTML -%][%- USE LxERP %][%- USE T8 %] |
|
2 |
[%- BLOCK output %] |
|
3 |
<div id="flash_[% type %]" class="flash_message_[% type %]"[% IF !messages || !messages.size %] style="display: none"[% END %]> |
|
4 |
<a href='#' style='float:right' |
|
5 |
onclick='$("#flash_[% type %]_content").empty();$("#flash_[% type %]_detail").empty();$("#flash_[% type %]").hide()'> |
|
6 |
<img src='image/close.png' border='0' alt='[% 'Close Flash' | $T8 %]'></a> |
|
7 |
<span class="flash_title">[%- title %]:</span> |
|
8 |
<span id="flash_[% type %]_content"> |
|
9 |
[% FOREACH message = messages %] |
|
10 |
[%- HTML.escape(message) %] |
|
11 |
[%- UNLESS loop.last %]<br>[% END %] |
|
12 |
[%- END %] |
|
13 |
</span> |
|
14 |
<span id="flash_[% type %]_disp" style="display: none"> |
|
15 |
<a href='#' style='float:left' onclick='$("#flash_detail_[% type %]").toggle();'> |
|
16 |
[[% 'Details' | $T8 %]]</a> </span> |
|
17 |
<div id="flash_detail_[% type %]" style="display: none"> |
|
18 |
<br> |
|
19 |
<span id="flash_[% type %]_detail"></span><br> |
|
20 |
<a href='#' style='float:left' |
|
21 |
onclick='$("#flash_detail_[% type %]").hide()'> |
|
22 |
<img src='image/close.png' border='0' alt='[% 'Close Details' | $T8 %]'></a><br> |
|
23 |
</div> |
|
24 |
</div> |
|
25 |
[%- END %] |
|
26 |
[%- PROCESS output title=LxERP.t8('Error') type='error' messages = FLASH.error %] |
|
27 |
[%- PROCESS output title=LxERP.t8('Warning') type='warning' messages = FLASH.warning %] |
|
28 |
[%- PROCESS output title=LxERP.t8('Information') type='info' messages = FLASH.info %] |
|
29 |
[%- PROCESS output title=LxERP.t8('Ok') type='ok' messages = FLASH.ok %] |
Auch abrufbar als: Unified diff
SL::Layout::Flash: fliegende Flash-Meldungen (portiert von Odyn)