Revision 4a663bf8
Von Tamino Steinert vor 3 Monaten hinzugefügt
| SL/ClientJS.pm | ||
|---|---|---|
|
use Rose::Object::MakeMethods::Generic
|
||
|
(
|
||
|
scalar => [ qw() ],
|
||
|
'scalar --get_set_init' => [ qw(controller _actions _flash _flash_detail _no_flash_clear _error) ],
|
||
|
'scalar --get_set_init' => [ qw(controller _actions _error) ],
|
||
|
);
|
||
|
|
||
|
my %supported_methods = (
|
||
| ... | ... | |
|
redirect_to => 1, # window.location.href = <TARGET>
|
||
|
save_file => 4, # kivi.save_file(<TARGET>, <ARGS>)
|
||
|
|
||
|
flash => 2, # kivi.display_flash(<TARGET>, <ARGS>)
|
||
|
flash_detail => 2, # kivi.display_flash_detail(<TARGET>, <ARGS>)
|
||
|
clear_flash => 2, # kivi.clear_flash(<TARGET>, <ARGS>)
|
||
|
# flash
|
||
|
flash => -2, # kivi.Flash.display_flash.apply({}, action.slice(1, action.length))
|
||
|
clear_flash => 0, # kivi.Flash.clear_flash()
|
||
|
show_flash => 0, # kivi.Flash.show()
|
||
|
hide_flash => 0, # kivi.Flash.hide()
|
||
|
|
||
|
reinit_widgets => 0, # kivi.reinit_widgets()
|
||
|
run => -1, # kivi.run(<TARGET>, <ARGS>)
|
||
|
run_once_for => 3, # kivi.run_once_for(<TARGET>, <ARGS>)
|
||
| ... | ... | |
|
return [];
|
||
|
}
|
||
|
|
||
|
sub init__flash {
|
||
|
return {};
|
||
|
}
|
||
|
|
||
|
sub init__flash_detail {
|
||
|
return {};
|
||
|
}
|
||
|
|
||
|
sub init__error {
|
||
|
return '';
|
||
|
}
|
||
|
|
||
|
sub init__no_flash_clear {
|
||
|
return '';
|
||
|
}
|
||
|
|
||
|
sub to_json {
|
||
|
my ($self) = @_;
|
||
|
|
||
|
return SL::JSON::to_json({ error => $self->_error }) if $self->_error;
|
||
|
return SL::JSON::to_json({ no_flash_clear => $self->_no_flash_clear, eval_actions => $self->_actions });
|
||
|
return SL::JSON::to_json({ eval_actions => $self->_actions });
|
||
|
}
|
||
|
|
||
|
sub to_array {
|
||
| ... | ... | |
|
return $self;
|
||
|
}
|
||
|
|
||
|
sub flash {
|
||
|
my ($self, $type, @messages) = @_;
|
||
|
|
||
|
my $message = join ' ', grep { $_ } @messages;
|
||
|
|
||
|
if (!$self->_flash->{$type}) {
|
||
|
$self->_flash->{$type} = [ 'flash', $type, $message ];
|
||
|
push @{ $self->_actions }, $self->_flash->{$type};
|
||
|
} else {
|
||
|
$self->_flash->{$type}->[-1] .= ' ' . $message;
|
||
|
}
|
||
|
|
||
|
return $self;
|
||
|
}
|
||
|
|
||
|
sub flash_detail {
|
||
|
my ($self, $type, @messages) = @_;
|
||
|
|
||
|
my $message = join '<br>', grep { $_ } @messages;
|
||
|
|
||
|
if (!$self->_flash_detail->{$type}) {
|
||
|
$self->_flash_detail->{$type} = [ 'flash_detail', $type, $message ];
|
||
|
push @{ $self->_actions }, $self->_flash_detail->{$type};
|
||
|
} else {
|
||
|
$self->_flash_detail->{$type}->[-1] .= ' ' . $message;
|
||
|
}
|
||
|
|
||
|
return $self;
|
||
|
}
|
||
|
|
||
|
sub no_flash_clear{
|
||
|
my ($self) = @_;
|
||
|
$self->_no_flash_clear('1');
|
||
|
return $self;
|
||
|
$_[0]; # noop for compatibility
|
||
|
}
|
||
|
|
||
|
sub error {
|
||
| ... | ... | |
|
|
||
|
=over 4
|
||
|
|
||
|
=item C<flash $type, $message>
|
||
|
|
||
|
Display a C<$message> in the flash of type C<$type>. Multiple calls of
|
||
|
C<flash> on the same C<$self> will be merged by type.
|
||
|
|
||
|
On the client side the flashes of all types will be cleared after each
|
||
|
successful ClientJS call that did not end with C<$js-E<gt>error(...)>.
|
||
|
This clearing can be switched of by the function C<no_flash_clear>
|
||
|
|
||
|
=item C<flash_detail $type, $message>
|
||
|
|
||
|
Display a detailed message C<$message> in the flash of type C<$type>. Multiple calls of
|
||
|
C<flash_detail> on the same C<$self> will be merged by type.
|
||
|
So the flash message can be hold short and the visibility of details can toggled by the user.
|
||
|
|
||
|
=item C<no_flash_clear>
|
||
|
=item C<flash $type, $message [, $details [, timestamp ]]>
|
||
|
|
||
|
No automatic clearing of flash after successful ClientJS call
|
||
|
Display a C<$message> in the flash of type C<$type> with optional
|
||
|
C<$details>.
|
||
|
|
||
|
=item C<error $message>
|
||
|
|
||
| SL/Controller/Flash.pm | ||
|---|---|---|
|
package SL::Controller::Flash;
|
||
|
|
||
|
use strict;
|
||
|
use parent qw(SL::Controller::Base);
|
||
|
|
||
|
use SL::Helper::Flash;
|
||
|
|
||
|
sub action_reload {
|
||
|
my ($self) = @_;
|
||
|
|
||
|
$self->js->clear_flash;
|
||
|
$self->js->flash(@$_) for SL::Helper::Flash::flash_contents;
|
||
|
$self->js->show_flash;
|
||
|
$self->js->render;
|
||
|
}
|
||
|
|
||
|
1;
|
||
|
|
||
|
__END__
|
||
|
|
||
|
=encoding utf-8
|
||
|
|
||
|
=head1 NAME
|
||
|
|
||
|
SL::Controller::Flash - Flash actions
|
||
|
|
||
|
=head1 DESCRIPTION
|
||
|
|
||
|
This controller contains actions that can be used to reload and control client
|
||
|
side flash messages
|
||
|
|
||
|
=head1 BUGS
|
||
|
|
||
|
None yet :)
|
||
|
|
||
|
=head1 AUTHOR
|
||
|
|
||
|
Sven Schöling E<lt>sven.schoeling@opendynamic.deE<gt>
|
||
|
|
||
|
=cut
|
||
| SL/Helper/Flash.pm | ||
|---|---|---|
|
require Exporter;
|
||
|
our @ISA = qw(Exporter);
|
||
|
our @EXPORT = qw(flash flash_later);
|
||
|
our @EXPORT_OK = qw(render_flash delay_flash);
|
||
|
our @EXPORT_OK = qw(delay_flash);
|
||
|
|
||
|
my %valid_categories = (
|
||
|
map({$_ => 'info'} qw(information message)),
|
||
| ... | ... | |
|
$::auth->set_session_value({ key => "FLASH", value => _store_flash($::auth->get_session_value('FLASH'), @_), auto_restore => 1 });
|
||
|
}
|
||
|
|
||
|
sub delay_flash {
|
||
|
my $store = $::form->{FLASH} || { };
|
||
|
flash_later($_ => @{ $store->{$_} || [] }) for keys %$store;
|
||
|
sub flash_contents {
|
||
|
return unless $::form;
|
||
|
return unless $::form->{FLASH};
|
||
|
return unless 'ARRAY' eq ref $::form->{FLASH};
|
||
|
|
||
|
@{ $::form->{FLASH} }
|
||
|
}
|
||
|
|
||
|
sub render_flash {
|
||
|
return $::form->parse_html_template('common/flash');
|
||
|
sub delay_flash {
|
||
|
my $store = $::form->{FLASH} || [];
|
||
|
flash_later(@{ $_ || [] }) for @$store;
|
||
|
}
|
||
|
|
||
|
#
|
||
| ... | ... | |
|
#
|
||
|
|
||
|
sub _store_flash {
|
||
|
my $store = shift || { };
|
||
|
my $category = _check_category(+shift);
|
||
|
my ($store, $type, $message, $details, $timestamp) = @_;
|
||
|
$store //= [ ];
|
||
|
$timestamp //= time();
|
||
|
my $category = _check_category($type);
|
||
|
|
||
|
$store->{ $category } ||= [ ];
|
||
|
push @{ $store->{ $category } }, @_;
|
||
|
push @{ $store }, [ $type, $message, $details, $timestamp ];
|
||
|
|
||
|
return $store;
|
||
|
}
|
||
| ... | ... | |
|
|
||
|
=head1 SYNOPSIS
|
||
|
|
||
|
use SL::Helper::Flash qw(flash flash_later delay_flash);
|
||
|
|
||
|
# display in this request
|
||
|
flash('info', 'Customer saved!');
|
||
|
flash('error', 'Something went wrong', "details about what went wrong");
|
||
|
flash('warning', 'this might not be a good idea');
|
||
|
|
||
|
# display after a redirect
|
||
|
flash_later('info', 'Customer saved!');
|
||
|
flash_later('error', 'Something went wrong', "details about what went wrong");
|
||
|
flash_later('warning', 'this might not be a good idea');
|
||
|
|
||
|
# delay flash() calls to next request:
|
||
|
delay_flash();
|
||
|
|
||
|
=head1 DESCRIPTION
|
||
|
|
||
|
The flash is a store for messages that should be displayed to the
|
||
|
user. Each message has a category which is usually C<information>,
|
||
|
C<warning> or C<error>. The messages in each category are grouped and
|
||
|
displayed in colors appropriate for their severity (e.g. errors in
|
||
|
red).
|
||
|
|
||
|
Messages are rendered either by calling the function C<render_flash>
|
||
|
or by including the flash sub-template from a template with the
|
||
|
following code:
|
||
|
|
||
|
[%- INCLUDE 'common/flash.html' %]
|
||
|
Messages are rendered by including the L<SL::Layout::Flash> sub layout.
|
||
|
|
||
|
=head1 EXPORTS
|
||
|
|
||
|
The functions L</flash> and L</flash_later> are always exported.
|
||
|
|
||
|
The function L</render_flash> is only exported upon request.
|
||
|
|
||
|
=head1 FUNCTIONS
|
||
|
|
||
|
=over 4
|
||
|
|
||
|
=item C<flash $category, @messages>
|
||
|
=item C<flash $category, $message [, $details ]>
|
||
|
|
||
|
Stores messages for the given category. The category can be either
|
||
|
C<information>, C<warning> or C<error>. C<info> can also be used as an
|
||
|
alias for C<information>.
|
||
|
Store a message with optional details for the given category. The category can
|
||
|
be either C<information>, C<warning> or C<error>. C<info> can also be used as
|
||
|
an alias for C<information>.
|
||
|
|
||
|
=item C<flash_later $category, @messages>
|
||
|
=item C<flash_later $category, $message [, $details ]>
|
||
|
|
||
|
Stores messages for the given category for the next request. The
|
||
|
category can be either C<information>, C<warning> or C<error>. C<info>
|
||
|
can also be used as an alias for C<information>.
|
||
|
Store a message with optional details for the given category for the next
|
||
|
request. The category can be either C<information>, C<warning> or C<error>.
|
||
|
C<info> can also be used as an alias for C<information>.
|
||
|
|
||
|
The messages are stored in the user's session and restored upon the
|
||
|
The message is stored in the user's session and restored upon the
|
||
|
next request. Can be used for transmitting information over HTTP
|
||
|
redirects.
|
||
|
|
||
|
=item C<render_flash>
|
||
|
|
||
|
Outputs the flash message by parsing the C<common/flash.html> template
|
||
|
file.
|
||
|
|
||
|
This function is not exported by default.
|
||
|
|
||
|
=item C<delay_flash>
|
||
|
|
||
|
Delays flash, as if all flash messages in this request would have been
|
||
| ... | ... | |
|
|
||
|
Not exported by default.
|
||
|
|
||
|
=item C<flash_contents>
|
||
|
|
||
|
The contents of the current flash accumulator.
|
||
|
|
||
|
=back
|
||
|
|
||
|
=head1 AUTHOR
|
||
| SL/Layout/Admin.pm | ||
|---|---|---|
|
use SL::Layout::None;
|
||
|
use SL::Layout::Top;
|
||
|
use SL::Layout::CssMenu;
|
||
|
use SL::Layout::Flash;
|
||
|
|
||
|
sub init_sub_layouts {
|
||
|
$_[0]->sub_layouts_by_name->{flash} = SL::Layout::Flash->new;
|
||
|
|
||
|
[
|
||
|
SL::Layout::None->new,
|
||
|
SL::Layout::CssMenu->new(menu => SL::Menu->new('admin')),
|
||
|
$_[0]->sub_layouts_by_name->{flash},
|
||
|
]
|
||
|
}
|
||
|
|
||
| SL/Layout/Classic.pm | ||
|---|---|---|
|
use SL::Layout::None;
|
||
|
use SL::Layout::Split;
|
||
|
use SL::Layout::ActionBar;
|
||
|
use SL::Layout::Flash;
|
||
|
use SL::Layout::Content;
|
||
|
|
||
|
sub init_sub_layouts {
|
||
|
$_[0]->sub_layouts_by_name->{actionbar} = SL::Layout::ActionBar->new;
|
||
|
$_[0]->sub_layouts_by_name->{flash} = SL::Layout::Flash->new;
|
||
|
|
||
|
[
|
||
|
SL::Layout::None->new,
|
||
|
SL::Layout::Top->new,
|
||
|
SL::Layout::Split->new(
|
||
|
left => [ SL::Layout::MenuLeft->new ],
|
||
|
right => [ $_[0]->sub_layouts_by_name->{actionbar}, SL::Layout::Content->new ],
|
||
|
right => [
|
||
|
$_[0]->sub_layouts_by_name->{actionbar},
|
||
|
$_[0]->sub_layouts_by_name->{flash},
|
||
|
SL::Layout::Content->new,
|
||
|
],
|
||
|
)
|
||
|
]
|
||
|
}
|
||
| SL/Layout/Flash.pm | ||
|---|---|---|
|
package SL::Layout::Flash;
|
||
|
|
||
|
use strict;
|
||
|
use parent qw(SL::Layout::Base);
|
||
|
use SL::Presenter::EscapedText qw(escape_js);
|
||
|
use SL::Helper::Flash;
|
||
|
|
||
|
sub pre_content {
|
||
|
'<div style="position:relative"><div id="layout_flash_container"></div></div>'
|
||
|
}
|
||
|
|
||
|
sub javascripts_inline {
|
||
|
my ($self) = @_;
|
||
|
|
||
|
my $js = '';
|
||
|
|
||
|
for (SL::Helper::Flash::flash_contents()) {
|
||
|
next if $_->[3] + 60 < time(); # ignore entries from more than one minute ago
|
||
|
$js .= defined $_->[2]
|
||
|
? sprintf("kivi.Flash.display_flash('%s', '%s', '%s');", map { escape_js($_) } @$_[0,1,2] )
|
||
|
: sprintf("kivi.Flash.display_flash('%s', '%s');", map { escape_js($_) } @$_[0,1] );
|
||
|
}
|
||
|
|
||
|
$js;
|
||
|
}
|
||
|
|
||
|
sub static_javascripts {
|
||
|
'kivi.Flash.js'
|
||
|
}
|
||
|
|
||
|
|
||
|
1;
|
||
| SL/Layout/Javascript.pm | ||
|---|---|---|
|
use SL::Layout::DHTMLMenu;
|
||
|
use SL::Layout::Top;
|
||
|
use SL::Layout::ActionBar;
|
||
|
use SL::Layout::Flash;
|
||
|
use SL::Layout::Content;
|
||
|
|
||
|
use List::Util qw(max);
|
||
| ... | ... | |
|
|
||
|
sub init_sub_layouts {
|
||
|
$_[0]->sub_layouts_by_name->{actionbar} = SL::Layout::ActionBar->new;
|
||
|
$_[0]->sub_layouts_by_name->{flash} = SL::Layout::Flash->new;
|
||
|
[
|
||
|
SL::Layout::None->new,
|
||
|
SL::Layout::Top->new,
|
||
|
SL::Layout::DHTMLMenu->new,
|
||
|
$_[0]->sub_layouts_by_name->{actionbar},
|
||
|
$_[0]->sub_layouts_by_name->{flash},
|
||
|
SL::Layout::Content->new,
|
||
|
]
|
||
|
}
|
||
| SL/Layout/Material.pm | ||
|---|---|---|
|
use SL::Layout::None;
|
||
|
use SL::Layout::MaterialMenu;
|
||
|
use SL::Layout::MaterialStyle;
|
||
|
use SL::Layout::Flash;
|
||
|
use SL::Layout::Content;
|
||
|
|
||
|
sub get_stylesheet_for_user {
|
||
| ... | ... | |
|
SL::Layout::None->new,
|
||
|
SL::Layout::MaterialStyle->new,
|
||
|
SL::Layout::MaterialMenu->new,
|
||
|
SL::Layout::Flash->new,
|
||
|
SL::Layout::Content->new,
|
||
|
]
|
||
|
}
|
||
| SL/Layout/V3.pm | ||
|---|---|---|
|
use SL::Layout::Top;
|
||
|
use SL::Layout::CssMenu;
|
||
|
use SL::Layout::ActionBar;
|
||
|
use SL::Layout::Flash;
|
||
|
use SL::Layout::Content;
|
||
|
|
||
|
sub init_sub_layouts {
|
||
|
$_[0]->sub_layouts_by_name->{actionbar} = SL::Layout::ActionBar->new;
|
||
|
$_[0]->sub_layouts_by_name->{flash} = SL::Layout::Flash->new;
|
||
|
|
||
|
[
|
||
|
SL::Layout::None->new,
|
||
|
SL::Layout::Top->new,
|
||
|
SL::Layout::CssMenu->new,
|
||
|
$_[0]->sub_layouts_by_name->{actionbar},
|
||
|
$_[0]->sub_layouts_by_name->{flash},
|
||
|
SL::Layout::Content->new,
|
||
|
]
|
||
|
}
|
||
| css/kivitendo/main.css | ||
|---|---|---|
|
font-weight: bold;
|
||
|
}
|
||
|
|
||
|
/* Flash message */
|
||
|
#layout_flash_container {
|
||
|
position: absolute;
|
||
|
z-index: 200;
|
||
|
right: 20px;
|
||
|
min-width: 30%;
|
||
|
animation: fadein .5s;
|
||
|
}
|
||
|
@keyframes hop {
|
||
|
to { transform: scale(1.01); }
|
||
|
}
|
||
|
@keyframes flash {
|
||
|
from { background-color: black }
|
||
|
}
|
||
|
@keyframes fadein {
|
||
|
from { opacity: 0; }
|
||
|
to { opacity: 1; }
|
||
|
}
|
||
|
.layout-flash-message {
|
||
|
margin-top: 5px;
|
||
|
margin-bottom: 5px;
|
||
|
padding: 5px;
|
||
|
border-width: 1px;
|
||
|
border-style: solid;
|
||
|
/* animation: .5s ease-out .2s 1 normal none running flash;*/
|
||
|
}
|
||
|
.layout-flash-body{
|
||
|
padding-top:5px;
|
||
|
margin-top:5px;
|
||
|
border-top: 1px dotted black;
|
||
|
}
|
||
|
.layout-flash-timestamp{
|
||
|
opacity: 0.75;
|
||
|
}
|
||
|
.layout-flash-details::before {
|
||
|
content: ': ';
|
||
|
}
|
||
|
.layout-flash-type {
|
||
|
font-weight: bold;
|
||
|
float: right;
|
||
|
}
|
||
|
.layout-flash-error {
|
||
|
background-color: #FFD6D6;
|
||
|
border-color: #AE0014;
|
||
|
}
|
||
|
.layout-flash-ok {
|
||
|
background-color: #ADFFB6;
|
||
|
border-color: #007F0F;
|
||
|
}
|
||
|
.layout-flash-warning {
|
||
|
background-color: #FFE8C7;
|
||
|
border-color: #FF6600;
|
||
|
}
|
||
|
.layout-flash-info {
|
||
|
background-color: #DCF2FF;
|
||
|
border-color: #4690FF;
|
||
|
}
|
||
|
.layout-flash-title {
|
||
|
font-weight: bold;
|
||
|
}
|
||
|
.layout-flash-remove {
|
||
|
padding-right: 4px;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* Admin section: the menu itself doesn't occupy space. So make room
|
||
|
at the top of the div covering the whole admin area. */
|
||
|
body > div.admin {
|
||
| js/client_js.js | ||
|---|---|---|
|
// SL/ClientJS.pm for instructions.
|
||
|
|
||
|
namespace("kivi", function(ns) {
|
||
|
ns.display_flash = function(type, message, noscroll) {
|
||
|
$('#flash_' + type + '_content').text(message);
|
||
|
$('#flash_' + type).show();
|
||
|
if (!noscroll && $('#frame-header')[0]) {
|
||
|
$('#frame-header')[0].scrollIntoView();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
ns.display_flash_detail = function(type, message) {
|
||
|
$('#flash_' + type + '_detail').html(message);
|
||
|
$('#flash_' + type + '_disp').show();
|
||
|
};
|
||
|
|
||
|
ns.clear_flash = function(category , timeout) {
|
||
|
window.setTimeout(function(){
|
||
|
$('#flash_' + category).hide();
|
||
|
$('#flash_detail_' + category).hide();
|
||
|
$('#flash_' + category + '_disp').hide();
|
||
|
$('#flash_' + category + '_content').empty();
|
||
|
$('#flash_' + category + '_detail').empty();
|
||
|
}, timeout);
|
||
|
};
|
||
|
|
||
|
ns.eval_json_result = function(data) {
|
||
|
if (!data)
|
||
|
return;
|
||
|
|
||
|
if (data.error)
|
||
|
return ns.display_flash('error', data.error);
|
||
|
if (data.error && ns.Flash)
|
||
|
return ns.Flash.display_flash('error', data.error);
|
||
|
|
||
|
if (!data.no_flash_clear) {
|
||
|
$(['info', 'warning', 'error']).each(function(idx, category) {
|
||
|
$('#flash_' + category).hide();
|
||
|
$('#flash_detail_' + category).hide();
|
||
|
$('#flash_' + category + '_disp').hide();
|
||
|
$('#flash_' + category + '_content').empty();
|
||
|
$('#flash_' + category + '_detail').empty();
|
||
|
});
|
||
|
}
|
||
|
if ((data.js || '') !== '')
|
||
|
// jshint -W061
|
||
|
eval(data.js);
|
||
| ... | ... | |
|
// ## other stuff ##
|
||
|
else if (action[0] == 'redirect_to') window.location.href = action[1];
|
||
|
else if (action[0] == 'save_file') kivi.save_file(action[1], action[2], action[3], action[4]);
|
||
|
else if (action[0] == 'flash') kivi.display_flash(action[1], action[2]);
|
||
|
else if (action[0] == 'flash_detail') kivi.display_flash_detail(action[1], action[2]);
|
||
|
else if (action[0] == 'clear_flash') kivi.clear_flash(action[1], action[2]);
|
||
|
|
||
|
// flash
|
||
|
else if (action[0] == 'flash') kivi.Flash.display_flash.apply({}, action.slice(1, action.length));
|
||
|
else if (action[0] == 'clear_flash') kivi.Flash.clear_flash();
|
||
|
else if (action[0] == 'show_flash') kivi.Flash.show();
|
||
|
else if (action[0] == 'hide_flash') kivi.Flash.hide();
|
||
|
else if (action[0] == 'reinit_widgets') kivi.reinit_widgets();
|
||
|
else if (action[0] == 'run') kivi.run(action[1], action.slice(2, action.length));
|
||
|
else if (action[0] == 'run_once_for') kivi.run_once_for(action[1], action[2], action[3]);
|
||
| js/kivi.Flash.js | ||
|---|---|---|
|
namespace("kivi.Flash", function(ns) {
|
||
|
"use strict";
|
||
|
|
||
|
ns.type_to_title = {
|
||
|
error: kivi.t8('Error'),
|
||
|
warning: kivi.t8('Warning'),
|
||
|
info: kivi.t8('Information'),
|
||
|
ok: kivi.t8('Ok')
|
||
|
};
|
||
|
|
||
|
ns.display_flash = function(type, message, details, timestamp) {
|
||
|
let $dom = $('<div>');
|
||
|
$dom.addClass('layout-flash-' + type);
|
||
|
$dom.addClass('layout-flash-message');
|
||
|
|
||
|
let $header = $('<div>');
|
||
|
$header.addClass('layout-flash-header');
|
||
|
|
||
|
let $remove = $('<span>✘</span>');
|
||
|
$remove.addClass('layout-flash-remove').addClass('cursor-pointer');
|
||
|
$remove.attr('alt', kivi.t8('Close Flash'));
|
||
|
$header.append($remove);
|
||
|
|
||
|
if (timestamp === undefined) {
|
||
|
timestamp = new Date();
|
||
|
} else if (timestamp > 0) {
|
||
|
timestamp = new Date(timestamp * 1000);
|
||
|
}
|
||
|
|
||
|
let $time = $('<span>');
|
||
|
$time.addClass('layout-flash-timestamp');
|
||
|
$time.text(kivi.format_time(timestamp));
|
||
|
$header.append($time);
|
||
|
|
||
|
let $type = $('<span>');
|
||
|
$type.addClass('layout-flash-type');
|
||
|
$type.text(ns.type_to_title[type]);
|
||
|
$header.append($type);
|
||
|
|
||
|
let $body = $('<div>');
|
||
|
$body.addClass('layout-flash-body');
|
||
|
|
||
|
if (message !== undefined && message !== null) {
|
||
|
let $message = $('<span>');
|
||
|
$message.addClass('layout-flash-content');
|
||
|
$message.html(message);
|
||
|
$body.append($message);
|
||
|
}
|
||
|
|
||
|
if (details !== undefined && details !== null) {
|
||
|
let $details = $('<span>');
|
||
|
$details.addClass('layout-flash-details');
|
||
|
$details.html(details);
|
||
|
$body.append($details);
|
||
|
}
|
||
|
|
||
|
$dom.append($header);
|
||
|
$dom.append($body);
|
||
|
|
||
|
$("#layout_flash_container").append($dom);
|
||
|
|
||
|
// fadeout after 1min
|
||
|
$dom.delay(60000).fadeOut('slow');
|
||
|
|
||
|
ns.show();
|
||
|
};
|
||
|
|
||
|
ns.display_flash_detail = function(type, message) {
|
||
|
$('#flash_' + type + '_disp').show();
|
||
|
};
|
||
|
|
||
|
ns.clear_flash = function(category, timeout) {
|
||
|
if (timeout === undefined) {
|
||
|
ns.clear_flash_now(category);
|
||
|
} else {
|
||
|
window.setTimeout(function(){
|
||
|
ns.clear_flash_now(category);
|
||
|
}, timeout);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
ns.clear_flash_now = function(category) {
|
||
|
if (category) {
|
||
|
$('div.layout-flash-' + category).remove();
|
||
|
} else {
|
||
|
$('div.layout-flash-message').remove();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
ns.remove_entry = function(e) {
|
||
|
$(e.target).closest('div.layout-flash-message').remove();
|
||
|
};
|
||
|
|
||
|
ns.toggle = function() {
|
||
|
$('#layout_flash_container').toggle();
|
||
|
};
|
||
|
ns.show = function() {
|
||
|
$('#layout_flash_container').show();
|
||
|
};
|
||
|
ns.hide = function() {
|
||
|
$('#layout_flash_container').hide();
|
||
|
};
|
||
|
ns.reload_flash = function() {
|
||
|
$.get("controller.pl", { action: "Flash/reload" }, kivi.eval_json_result);
|
||
|
};
|
||
|
|
||
|
ns.reinit_widgets = function() {
|
||
|
|
||
|
};
|
||
|
});
|
||
|
|
||
|
$(function() {
|
||
|
"use strict";
|
||
|
// dispatch to kivi.Flash for compatibility
|
||
|
kivi.display_flash = kivi.Flash.display_flash;
|
||
|
kivi.display_flash_detail = kivi.Flash.display_flash_detail;
|
||
|
kivi.empty_flash = kivi.Flash.empty_flash;
|
||
|
kivi.clear_flash = kivi.Flash.clear_flash;
|
||
|
|
||
|
$('.layout-flash-toggle').click(kivi.Flash.toggle);
|
||
|
$('#layout_flash_container').on('click', '.layout-flash-remove', kivi.Flash.remove_entry);
|
||
|
});
|
||
| scripts/generate_client_js_actions.tpl | ||
|---|---|---|
|
// SL/ClientJS.pm for instructions.
|
||
|
|
||
|
namespace("kivi", function(ns) {
|
||
|
ns.display_flash = function(type, message, noscroll) {
|
||
|
$('#flash_' + type + '_content').text(message);
|
||
|
$('#flash_' + type).show();
|
||
|
if (!noscroll) {
|
||
|
$('#frame-header')[0].scrollIntoView();
|
||
|
}
|
||
|
};
|
||
|
|
||
|
ns.display_flash_detail = function(type, message) {
|
||
|
$('#flash_' + type + '_detail').html(message);
|
||
|
$('#flash_' + type + '_disp').show();
|
||
|
};
|
||
|
|
||
|
ns.clear_flash = function(category , timeout) {
|
||
|
window.setTimeout(function(){
|
||
|
$('#flash_' + category).hide();
|
||
|
$('#flash_detail_' + category).hide();
|
||
|
$('#flash_' + category + '_disp').hide();
|
||
|
$('#flash_' + category + '_content').empty();
|
||
|
$('#flash_' + category + '_detail').empty();
|
||
|
}, timeout);
|
||
|
};
|
||
|
|
||
|
ns.eval_json_result = function(data) {
|
||
|
if (!data)
|
||
|
return;
|
||
|
|
||
|
if (data.error)
|
||
|
return ns.display_flash('error', data.error);
|
||
|
return ns.Flash.display_flash('error', data.error);
|
||
|
|
||
|
if (!data.no_flash_clear) {
|
||
|
$(['info', 'warning', 'error']).each(function(idx, category) {
|
||
|
$('#flash_' + category).hide();
|
||
|
$('#flash_detail_' + category).hide();
|
||
|
$('#flash_' + category + '_disp').hide();
|
||
|
$('#flash_' + category + '_content').empty();
|
||
|
$('#flash_' + category + '_detail').empty();
|
||
|
});
|
||
|
}
|
||
|
if ((data.js || '') !== '')
|
||
|
// jshint -W061
|
||
|
eval(data.js);
|
||
| t/flash_migration/deprecated_calls.t | ||
|---|---|---|
|
use strict;
|
||
|
|
||
|
use lib 't';
|
||
|
|
||
|
use Support::Files;
|
||
|
use Support::TestSetup;
|
||
|
|
||
|
use File::Spec;
|
||
|
use File::Slurp;
|
||
|
use Template;
|
||
|
use Template::Provider;
|
||
|
use Test::More;
|
||
|
|
||
|
my @deprecated_calls_perl = (
|
||
|
qr/render_flash/,
|
||
|
qr/flash_detail/,
|
||
|
qr{common/flash},
|
||
|
qr/flash (?:_later)? \( \s* ' (?: error | warning | information ) ' \s* , \s* \@ /x,
|
||
|
);
|
||
|
|
||
|
my @deprecated_calls_js = (
|
||
|
qr/kivi\.display_flash/,
|
||
|
qr/kivi\.clear_flash/,
|
||
|
);
|
||
|
|
||
|
for my $file (@Support::Files::files) {
|
||
|
open my $fh, '<', $file or die "can't open $file";
|
||
|
|
||
|
while (my $line = <$fh>) {
|
||
|
for my $re (@deprecated_calls_perl) {
|
||
|
if ($line =~ $re) {
|
||
|
ok 0, "$file contains '$&', most likely due to incomplete merge of the layout flash feature. Consult the documentation in this test script";
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ok 1, $file;
|
||
|
}
|
||
|
|
||
|
for my $file (@Support::Files::javascript_files) {
|
||
|
next if $file eq 'js/kivi.Flash.js';
|
||
|
|
||
|
open my $fh, '<', $file or die "can't open $file";
|
||
|
|
||
|
while (my $line = <$fh>) {
|
||
|
for my $re (@deprecated_calls_js) {
|
||
|
if ($line =~ $re) {
|
||
|
TODO: { local $TODO = 'clean up compatibility kivi.display_flash and kivi.clear_flash';
|
||
|
ok 0, "$file contains '$&', most likely due to incomplete merge of the layout flash feature. Consult the documentation in this test script";
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ok 1, $file;
|
||
|
}
|
||
|
|
||
|
done_testing();
|
||
|
|
||
|
|
||
|
__END__
|
||
|
|
||
|
=encoding utf-8
|
||
|
|
||
|
=head1 NAME
|
||
|
|
||
|
t/flash_migration&deprecated_calls.t
|
||
|
|
||
|
=head1 DESCRIPTION
|
||
|
|
||
|
Okay, if this script triggers, this is what needs to be done:
|
||
|
|
||
|
In Javascript:
|
||
|
|
||
|
- all javascript calls to "kivi.display_flash" and "kivi.clear_flash" need to
|
||
|
be redirected to "kivi.Flash"
|
||
|
- kivi.display_flash_details doesn't exist any more, instead details can now be
|
||
|
passed as a third argument to kivi.Flash.display_flash
|
||
|
|
||
|
In html:
|
||
|
|
||
|
- There is no common/flash.html template any more, since the layout handles
|
||
|
that now. Any attempt to render it needs to be removed.
|
||
|
|
||
|
In Perl:
|
||
|
|
||
|
- flash_detail doesn't exist any more, instead flash() and flash_later() take a
|
||
|
third argument for details
|
||
|
- render_flash doesn't exist anymore and is now handled by the layout.
|
||
|
- flash('error', @errrs) and similar must be called in a loop: flash('error', $_) for @errors
|
||
|
|
||
|
=cut
|
||
| templates/design40_webpages/common/flash.html | ||
|---|---|---|
|
[% USE HTML %]
|
||
|
[% USE LxERP %]
|
||
|
[% USE T8 %]
|
||
|
|
||
|
[% BLOCK output %]
|
||
|
<div id="flash_[% type %]" class="flash_message flash_message_[% type %]" [% IF !messages || !messages.size %] style="display: none" [% END %]>
|
||
|
<div class="icon-container">
|
||
|
<a href="#" onclick='$("#flash_[% type %]_content").empty();$("#flash_[% type %]_detail").empty();$("#flash_[% type %]").hide()' class="icon-close"> ✕</a>
|
||
|
<span id="flash_[% type %]_disp" class="display" style="display: none">
|
||
|
<a href="#" onclick='$("#flash_detail_[% type %]").toggle();' class="button"> [% 'Details' | $T8 %] </a>
|
||
|
</span>
|
||
|
</div>
|
||
|
<div class="message-container">
|
||
|
<span class="flash_title">[% title %]:</span>
|
||
|
<div class="flash_notification">
|
||
|
<span id="flash_[% type %]_content" class="content">
|
||
|
[% FOREACH message = messages %] [% HTML.escape(message) %] [% UNLESS loop.last %]<br>[% END %] [% END %]
|
||
|
</span>
|
||
|
<div id="flash_detail_[% type %]" class="detail" style="display: none">
|
||
|
<span id="flash_[% type %]_detail"></span>
|
||
|
<a href="#" style="float:left" onclick='$("#flash_detail_[% type %]").hide()' class="icon-close">✕</a>
|
||
|
</div>
|
||
|
</div><!-- /.flash_notification -->
|
||
|
</div>
|
||
|
</div>
|
||
|
[% END #BLOCK output %]
|
||
|
|
||
|
[% PROCESS output title=LxERP.t8('Error') type='error' messages = FLASH.error %]
|
||
|
[% PROCESS output title=LxERP.t8('Warning') type='warning' messages = FLASH.warning %]
|
||
|
[% PROCESS output title=LxERP.t8('Information') type='info' messages = FLASH.info %]
|
||
|
[% PROCESS output title=LxERP.t8('Ok') type='ok' messages = FLASH.ok %]
|
||
| templates/webpages/common/flash.html | ||
|---|---|---|
|
[%- USE HTML -%][%- USE LxERP %][%- USE T8 %]
|
||
|
[%- BLOCK output %]
|
||
|
<div id="flash_[% type %]" class="flash_message_[% type %]"[% IF !messages || !messages.size %] style="display: none"[% END %]>
|
||
|
<a href='#' style='float:right'
|
||
|
onclick='$("#flash_[% type %]_content").empty();$("#flash_[% type %]_detail").empty();$("#flash_[% type %]").hide()'>
|
||
|
<img src='image/close.png' border='0' alt='[% 'Close Flash' | $T8 %]'></a>
|
||
|
<span class="flash_title">[%- title %]:</span>
|
||
|
<span id="flash_[% type %]_content">
|
||
|
[% FOREACH message = messages %]
|
||
|
[%- HTML.escape(message) %]
|
||
|
[%- UNLESS loop.last %]<br>[% END %]
|
||
|
[%- END %]
|
||
|
</span>
|
||
|
<span id="flash_[% type %]_disp" style="display: none">
|
||
|
<a href='#' style='float:left' onclick='$("#flash_detail_[% type %]").toggle();'>
|
||
|
[[% 'Details' | $T8 %]]</a> </span>
|
||
|
<div id="flash_detail_[% type %]" style="display: none">
|
||
|
<br>
|
||
|
<span id="flash_[% type %]_detail"></span><br>
|
||
|
<a href='#' style='float:left'
|
||
|
onclick='$("#flash_detail_[% type %]").hide()'>
|
||
|
<img src='image/close.png' border='0' alt='[% 'Close Details' | $T8 %]'></a><br>
|
||
|
</div>
|
||
|
</div>
|
||
|
[%- END %]
|
||
|
[%- PROCESS output title=LxERP.t8('Error') type='error' messages = FLASH.error %]
|
||
|
[%- PROCESS output title=LxERP.t8('Warning') type='warning' messages = FLASH.warning %]
|
||
|
[%- PROCESS output title=LxERP.t8('Information') type='info' messages = FLASH.info %]
|
||
|
[%- 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)