...
 
Commits (6)
......@@ -38,7 +38,7 @@ useRedirectOnError = 0
[manager]
enabledModules = conf, sessions, notifications, 2ndFA, viewer
enabledModules = conf, sessions, 2ndFA, notifications, messages, viewer
protection = manager
viewerHiddenKeys = samlIDPMetaDataNodes samlSPMetaDataNodes portalDisplayLogout captcha_login_enabled
......
......@@ -375,8 +375,7 @@ languages = fr, en, it, vi, ar
; Manager modules enabled
; Set here the list of modules you want to see in manager interface
; The first will be used as default module displayed
;enabledModules = conf, sessions, notifications, 2ndFA, viewer
enabledModules = conf, sessions, notifications, 2ndFA
enabledModules = conf, sessions, 2ndFA, notifications, messages
; To avoid restricted users to edit configuration, defaulModule MUST be different than 'conf'
; 'conf' is set by default
......
package Lemonldap::NG::Common::Messages;
use strict;
use Mouse;
our $VERSION = '2.0.7';
extends 'Lemonldap::NG::Common::Module';
sub import {
# if ( $_[1] eq 'XML' ) {
# extends 'Lemonldap::NG::Common::Notifications::XML',
# 'Lemonldap::NG::Common::Module';
# }
# else {
extends 'Lemonldap::NG::Common::Messages::JSON',
'Lemonldap::NG::Common::Module';
# }
}
has msgField => (
is => 'rw',
builder => sub {
my $uid =
$_[0]->conf->{notificationField}
|| $_[0]->conf->{whatToTrace}
|| 'uid';
$uid =~ s/^\$//;
return $uid;
}
);
sub getMessages {
my ( $self, $uid ) = @_;
my $forUser = $self->get($uid);
my $forAll = $self->get( $self->conf->{notificationWildcard} );
if ( $forUser and $forAll ) {
return { %$forUser, %$forAll };
}
else {
return ( ( $forUser ? $forUser : $forAll ), $forUser );
}
}
1;
## @file
# DBI storage methods for messages
## @class
# DBI storage methods for messages
package Lemonldap::NG::Common::Messages::DBI;
use strict;
use Mouse;
use Time::Local;
use DBI;
use Encode;
our $VERSION = '2.0.7';
extends 'Lemonldap::NG::Common::Messages';
sub import {
shift;
return Lemonldap::NG::Common::Messages->import(@_);
}
has dbiTable => (
is => 'ro',
lazy => 1,
default => sub { $_[0]->{table} || 'notifications' }
);
has dbiChain => (
is => 'ro',
required => 1
);
has dbiUser => (
is => 'ro',
lazy => 1,
default => sub {
$_[0]->{p}->logger->warn('Warning: "dbiUser" parameter is not set');
return '';
}
);
has dbiPassword => ( is => 'ro', default => '' );
# Database handle object
has _dbh => (
is => 'rw',
lazy => 1,
builder => sub {
my $self = shift;
my $r = DBI->connect_cached(
$self->{dbiChain}, $self->{dbiUser},
$self->{dbiPassword}, { RaiseError => 1 }
);
$self->logger->error($DBI::errstr) unless ($r);
return $r;
}
);
# Current query
has sth => ( is => 'rw' );
# Returns messages corresponding to the user $uid.
# If $ref is set, returns only message corresponding to this reference.
sub get {
my ( $self, $uid, $ref ) = @_;
return () unless ($uid);
$self->_execute(
"SELECT * FROM "
. $self->dbiTable
. " WHERE done IS NULL AND uid=?"
. ( $ref ? " AND ref=?" : '' )
. " ORDER BY date",
$uid,
( $ref ? $ref : () )
) or return ();
my $result;
while ( my $h = $self->sth->fetchrow_hashref() ) {
# Get XML message
my $xml = $h->{xml};
# Decode it to get the correct uncoded string
Encode::from_to( $xml, "utf8", "iso-8859-1", Encode::FB_CROAK );
# Store message in result
my $identifier =
&getIdentifier( $self, $h->{uid}, $h->{ref}, $h->{date} );
$result->{$identifier} = $xml;
}
$self->logger->warn( $self->sth->err() ) if ( $self->sth->err() );
return $result;
}
## @method hashref getAll()
# Return all messages not notified.
# @return hashref where keys are internal reference and values are hashref with
# keys date, uid and ref.
sub getAll {
my $self = shift;
$self->_execute( 'SELECT * FROM '
. $self->dbiTable
. ' WHERE done IS NULL ORDER BY date' );
my $result;
while ( my $h = $self->sth->fetchrow_hashref() ) {
$result->{"$h->{date}#$h->{uid}#$h->{ref}"} = {
date => $h->{date},
uid => $h->{uid},
ref => $h->{ref},
condition => $h->{condition}
};
}
$self->logger->warn( $self->sth->err() ) if ( $self->sth->err() );
return $result;
}
## @method boolean delete(string myref)
# Mark a message as done.
# @param $myref identifier returned by get() or getAll()
sub delete {
my ( $self, $myref ) = @_;
my ( $d, $u, $r );
unless ( ( $d, $u, $r ) = ( $myref =~ /^([^#]+)#(.+?)#(.+)$/ ) ) {
$self->logger->warn("Bad reference $myref");
return 0;
}
my @ts = localtime();
$ts[5] += 1900;
$ts[4]++;
return $self->_execute( 'UPDATE '
. $self->dbiTable
. " SET done='$ts[5]-$ts[4]-$ts[3] $ts[2]:$ts[1]' "
. 'WHERE done IS NULL AND uid=? AND ref=? AND date=?',
$u, $r, $d );
}
## @method boolean purge(string myref, boolean force)
# Purge message (really delete record)
# @param $myref identifier returned by get or getAll
# @param $force force purge for not deleted session
# @return true if something was deleted
sub purge {
my ( $self, $myref, $force ) = @_;
my ( $d, $u, $r );
unless ( ( $d, $u, $r ) = ( $myref =~ /^([^#]+)#(.+?)#(.+)$/ ) ) {
$self->logger->warn("Bad reference $myref");
return 0;
}
unless ( $d =~ s/^(\d{4})(\d{2})(\d{2}).*$/$1-$2-$3/
or $d =~ s/^(\d{4}-\d{2}-\d{2}).*$/$1/ )
{
$self->logger->warn("Bad date $d");
return 0;
}
my $clause;
$clause = "done IS NOT NULL AND" unless ($force);
return $self->_execute( 'DELETE FROM '
. $self->dbiTable
. " WHERE $clause uid=? AND ref=? AND date=?",
$u, $r, $d );
}
## @method boolean Msg(string date, string uid, string ref, string condition, string xml)
# Insert a new message
# @param date Date
# @param uid UID
# @param ref Reference of the message
# @param condition Condition for the message
# @param xml XML message
# @return true if succeed
sub newMsg {
my ( $self, $date, $uid, $ref, $condition, $xml ) = @_;
my $res =
$condition =~ /.+/
? $self->_execute( 'INSERT INTO '
. $self->dbiTable
. ' (date,uid,ref,cond,xml) VALUES(?,?,?,?,?)',
$date, $uid, $ref, $condition, $xml )
: $self->_execute( 'INSERT INTO '
. $self->dbiTable
. ' (date,uid,ref,xml) VALUES(?,?,?,?)',
$date, $uid, $ref, $xml );
return $res;
}
## @method hashref getDone()
# Returns a list of message that have been done
# @return hashref where keys are internal reference and values are hashref with
# keys notified, uid and ref.
sub getDone {
my ($self) = @_;
$self->_execute( 'SELECT * FROM '
. $self->dbiTable
. ' WHERE done IS NOT NULL ORDER BY done' );
my $result;
while ( my $h = $self->sth->fetchrow_hashref() ) {
my @t = split( /\D+/, $h->{date} );
my $done =
timelocal( $t[5] || 0, $t[4] || 0, $t[3] || 0, $t[2], $t[1], $t[0] );
$result->{"$h->{date}#$h->{uid}#$h->{ref}"} =
{ notified => $done, uid => $h->{uid}, ref => $h->{ref}, };
}
$self->logger->warn( $self->sth->err() ) if ( $self->sth->err() );
return $result;
}
## @method private object _execute(string query, array args)
# Execute a query and catch errors
# @return number of lines touched or 1 if select succeed
sub _execute {
my ( $self, $query, @args ) = @_;
my $dbh = $self->_dbh or die "DB connection unavailable";
unless ( $self->sth( $dbh->prepare($query) ) ) {
$self->logger->warn( $dbh->errstr() );
return 0;
}
my $tmp;
unless ( $tmp = $self->sth->execute(@args) ) {
$self->logger->warn( $self->sth->errstr() );
return 0;
}
return $tmp;
}
## @method string getIdentifier(string uid, string ref, string date)
# Get message identifier
# @param $uid uid
# @param $ref ref
# @param $date date
# @return the message identifier
sub getIdentifier {
my ( $self, $uid, $ref, $date ) = @_;
return $date . "#" . $uid . "#" . $ref;
}
1;
## @file
# File storage methods for messages
## @class
# File storage methods for messages
package Lemonldap::NG::Common::Messages::File;
use strict;
use Mouse;
use MIME::Base64;
our $VERSION = '2.0.7';
extends 'Lemonldap::NG::Common::Messages';
our $ext = 'json';
sub import {
shift;
$ext = 'xml' if ( $_[0] eq 'XML' );
return Lemonldap::NG::Common::Messages->import(@_);
}
has dirName => ( is => 'ro', required => 1 );
has table => (
is => 'rw',
trigger => sub {
$_[0]->{dirName} =~ s/\/conf\/?$//;
$_[0]->{dirName} .= "/$_[0]->{table}";
}
);
has fileNameSeparator => ( is => 'rw', default => '_' );
# Returns messages corresponding to the user $uid.
# If $ref is set, returns only message corresponding to this reference.
sub get {
my ( $self, $ref ) = @_;
return () unless ($ref);
my $fns = $self->{fileNameSeparator};
my $identifier = &getIdentifier( $self, $ref );
opendir D, $self->{dirName};
my @msg = grep /^${identifier}\S*\.$ext$/, readdir(D);
closedir D;
my $files;
foreach my $file (@msg) {
unless ( open F, $self->{dirName} . "/$file" ) {
$self->logger->error(
"Unable to read message $self->{dirName}/$file");
next;
}
$files->{$file} = join( '', <F> );
}
return $files;
}
## @method hashref getAll()
# Return all messages not notified.
# @return hashref where keys are internal reference and values are hashref with
# keys date, uid and ref.
sub getAll {
my $self = shift;
opendir D, $self->{dirName};
my @msg;
my $fns = $self->{fileNameSeparator};
@msg = grep /^\S*\.$ext$/, readdir(D);
my %h = map {
/^(\d{8})${fns}([^\s${fns}]+)${fns}([^\s${fns}]+)(?:${fns}([^\s${fns}]+))?\.$ext$/
? (
$_ => {
date => $1,
uid => $2,
ref => decode_base64($3),
condition => decode_base64( $4 // '' )
}
)
: ()
} @msg;
return \%h;
}
## @method boolean delete(string myref)
# Mark a message as done.
# @param $myref identifier returned by get() or getAll()
sub delete {
my ( $self, $myref ) = @_;
my $new = ( $myref =~ /(.*?)(?:\.$ext)$/ )[0] . '.done';
return rename( $self->{dirName} . "/$myref", $self->{dirName} . "/$new" );
}
## @method boolean purge(string myref)
# Purge message (really delete record)
# @param $myref identifier returned by get() or getAll()
# @return true if something was deleted
sub purge {
my ( $self, $myref ) = @_;
return unlink( $self->{dirName} . "/$myref" );
}
# Insert a new message
sub newMsg {
my ( $self, $date, $uid, $ref, $condition, $content ) = @_;
my $fns = $self->{fileNameSeparator};
$date =~ s/-//g;
return ( 0, "Bad date" ) unless ( $date =~ /^\d{8}/ );
my $filename =
$self->{dirName}
. "/${date}${fns}${uid}${fns}"
. encode_base64( $ref, '' );
$filename .= "${fns}" . encode_base64( $condition, '' ) if $condition;
$filename .= ".$ext";
return ( 0, 'This message still exists' ) if ( -e $filename );
my $old = ( $filename =~ /(.*?)(?:\.$ext)$/ )[0] . '.done';
return ( 0, 'This message has been done' ) if ( -e $old );
open my $F, ">$filename" or return ( 0, "Unable to create $filename ($!)" );
binmode($F);
print $F $content;
return ( 0, "Unable to close $filename ($!)" ) unless ( close $F );
return 1;
}
## @method hashref getDone()
# Returns a list of message that have been done
# @return hashref where keys are internal reference and values are hashref with
# keys notified, uid and ref.
sub getDone {
my ($self) = @_;
opendir D, $self->{dirName};
my @msg;
my $fns = $self->{fileNameSeparator};
@msg = grep /^\d{8}${fns}\S*\.done$/, readdir(D);
my $res;
foreach my $file (@msg) {
my ( $u, $r ) =
( $file =~
/^\d+${fns}([^${fns}]+)${fns}([^${fns}]+)${fns}?([^${fns}]+)\.done$/
);
die unless ( -f "$self->{dirName}/$file" );
my $time = ( stat("$self->{dirName}/$file") )[10];
$res->{$file} = {
'uid' => $u,
'ref' => decode_base64($r),
'notified' => $time,
};
}
return $res;
}
## @method string getIdentifier(string uid, string ref, string date)
# Get message identifier
# @param $uid uid
# @param $ref ref
# @param $date date
# @return the message identifier
sub getIdentifier {
my ( $self, $ref, $date ) = @_;
#my $result;
# Special fix to manage purge from message explorer
#return $date if $date;
my $fns = $self->{fileNameSeparator};
#if ($date) {
#$result = "MSG$fns$ref$fns";
#}
#$result .= $uid;
#if ($ref) {
my $tmp = encode_base64( $ref, '' );
#$result .= $fns . $tmp;
#}
return "MSG$fns$tmp$fns";
}
1;
package Lemonldap::NG::Common::Messages::JSON;
use strict;
use Mouse;
use JSON qw(from_json to_json);
our $VERSION = '2.0.7';
sub newMessage {
my ( $self, $jsonString ) = @_;
my $json;
eval { $json = from_json( $jsonString, { allow_nonref => 1 } ) };
if ( my $err = $@ ) {
eval { $self->logger->error("Unable to decode JSON file: $err") };
return 0;
}
my @msgs;
$json = [$json] unless ( ref($json) eq 'ARRAY' );
foreach my $msg (@$json) {
my @data;
# Mandatory information
foreach (qw(startDate startTime reference level condition)) {
my $tmp;
unless ( $tmp = $msg->{$_} ) {
my $err = "Attribute $_ is missing";
$self->logger->error("$err");
return ( 0, "$err" );
}
if ( $self->get( $msg->{reference} ) ) {
my $err = "A message already exists with reference "
. $msg->{reference};
$self->logger->error("$err");
return ( 0, "$err" );
}
push @data, $tmp;
}
push @data, ( $msg->{condition} // '' );
push @msgs, [ @data, $jsonString ];
}
my $count;
foreach (@msgs) {
$count++;
my ( $r, $err ) = $self->newMsg(@$_);
die "$err" unless ($r);
}
return $count;
}
sub deleteMessage {
my ( $self, $uid, $myref ) = @_;
my @data;
# Check input parameters
unless ( $uid and $myref ) {
$self->userLogger->error(
'REST service "delete message" called without all parameters');
return 0;
}
$self->logger->debug(
"REST service deleteMessage called for uid $uid and reference $myref"
);
# Get messages
my $user = $self->get($uid);
# Return 0 if no files were found
return 0 unless ($user);
# Counting
my $count = 0;
foreach my $ref ( keys %$user ) {
my $json = from_json( $user->{$ref}, { allow_nonref => 1 } );
$json = [$json] unless ( ref($json) eq 'ARRAY' );
# Browse message in file
foreach my $msg (@$json) {
# Get message's data
if ( $msg->{reference} eq $myref ) {
push @data, $ref;
}
# Delete the message (really)
foreach (@data) {
if ( $self->purge( $_, 1 ) ) {
$self->logger->debug("Message $_ was removed.");
$count++;
}
}
}
}
return $count;
}
1;
This diff is collapsed.
......@@ -17,7 +17,7 @@ use JSON;
use Lemonldap::NG::Common::Conf::Constants;
use Lemonldap::NG::Common::PSGI::Constants;
our $VERSION = '2.0.6';
our $VERSION = '2.0.7';
extends 'Lemonldap::NG::Common::Conf::AccessLib',
'Lemonldap::NG::Handler::PSGI::Router';
......@@ -52,7 +52,7 @@ sub init {
return 0;
}
$self->{enabledModules} ||= "conf, sessions, notifications, 2ndFA";
$self->{enabledModules} ||= "conf, sessions, notifications, 2ndFA, messages";
my @links;
my @enabledModules =
map { push @links, $_; "Lemonldap::NG::Manager::" . ucfirst($_) }
......@@ -101,6 +101,7 @@ sub init {
'conf' => 'cog',
'sessions' => 'duplicate',
'notifications' => 'bell',
'messages' => 'alert',
'2ndFA' => 'wrench',
'viewer' => 'eye-open',
};
......
This diff is collapsed.
###
# LemonLDAP::NG Messages Explorer client
###
scheme = [
(v) ->
"groupBy=substr(uid,1)"
(v) ->
"uid=#{v}*&groupBy=uid"
(v) ->
"uid=#{v}"
]
# Session menu
menu =
actives: [
title: 'markAsDone'
icon: 'check'
]
done: [
title: 'deleteMessage'
icon: 'trash'
]
new: [
title: 'save'
icon: 'save'
]
home: []
# AngularJS application
llapp = angular.module 'llngMessagesExplorer', [ 'ui.tree', 'ui.bootstrap', 'llApp' ]
# Main controller
llapp.controller 'MessagesExplorerCtrl', [ '$scope', '$translator', '$location', '$q', '$http', '$uibModal', ($scope, $translator, $location, $q, $http, $uibModal) ->
$scope.links = links
$scope.menulinks = menulinks
$scope.staticPrefix = staticPrefix
$scope.scriptname = scriptname
$scope.formPrefix = formPrefix
$scope.availableLanguages = availableLanguages
$scope.waiting = true
$scope.showM = false
$scope.showT = true
$scope.showForm = false
$scope.data = []
$scope.form = {}
$scope.formPost = {}
$scope.currentScope = null
$scope.currentMessage = null
$scope.menu = menu
# Import translation functions
$scope.translateP = $translator.translateP
$scope.translate = $translator.translate
$scope.translateTitle = (node) ->
$translator.translateField node, 'title'
# Handler menu items
$scope.menuClick = (button) ->
if button.popup
window.open button.popup
else
button.action or= button.title
switch typeof button.action
when 'function'
button.action $scope.currentNode, $scope
when 'string'
$scope[button.action]()
else
console.log typeof button.action
$scope.showM = false
# Message management
$scope.markAsDone = ->
$scope.waiting = true
$http.put("#{scriptname}messages/#{$scope.type}/#{$scope.currentMessage.uid}_#{$scope.currentMessage.reference}", {done:1}).then (response) ->
$scope.currentMessage = null
$scope.currentScope.remove()
$scope.message =
title: 'messageDeleted'
$scope.showModal "alert.html"
$scope.waiting = false
$scope.init()
, (response) ->
$scope.message =
title: 'messageNotDeleted'
message: response.statusText
$scope.showModal "alert.html"
$scope.waiting = false
$scope.init()
$scope.deleteMessage = ->
$scope.waiting = true
$http['delete']("#{scriptname}messages/#{$scope.type}/#{$scope.currentMessage.uid}_#{$scope.currentMessage.reference}_#{$scope.currentMessage.done}").then (response) ->
$scope.currentMessage = null
$scope.currentScope.remove()
$scope.message =
title: 'messagePurged'
$scope.showModal "alert.html"
$scope.waiting = false
$scope.init()
, (response) ->
$scope.message =
title: 'messageNotPurged'
message: response.statusText
$scope.showModal "alert.html"
$scope.waiting = false
$scope.init()
$scope.stoggle = (scope) ->
node = scope.$modelValue
if node.nodes.length == 0
$scope.updateTree node.value, node.nodes, node.level, node.query
scope.toggle()
$scope.msgDate = (s) ->
if s?
if s.match /(\d{4})-(\d{2})-(\d{2})/
s = s.substr(0, 4) + s.substr(5, 2) + s.substr(8, 2)
d = new Date(s.substr(0, 4), s.substr(4, 2) - 1, s.substr(6, 2))
return d.toLocaleDateString()
return ''
$scope.getLanguage = (lang) ->
$scope.lang = lang
$scope.form = 'white'
$scope.init()
$scope.showM = false
$scope.$on '$locationChangeSuccess', (event, next, current) ->
n = next.match /#!?\/(\w+)/
$scope.type = if n? then n[1] else 'actives'
if $scope.type == 'new'
$scope.displayCreateForm()
else
$scope.showForm = false
$scope.init()
autoId = 0
$scope.updateTree = (value, node, level, currentQuery) ->
$scope.waiting = true
query = scheme[level] value, currentQuery
$http.get("#{scriptname}messages/#{$scope.type}?#{query}").then (response) ->
data = response.data
if data.result
for n in data.values
autoId++
n.id = "node#{autoId}"
if level <scheme.length - 1
n.nodes = []
n.level = level + 1
n.query = query
node.push n
$scope.waiting = false
, (resp) ->
$scope.waiting = false
$scope.displayMessage = (scope) ->
$scope.waiting = true
$scope.currentScope = scope
node = scope.$modelValue
messageId = node.message
if $scope.type == 'actives'
messageId = "#{node.uid}_#{node.reference}"
$http.get("#{scriptname}messages/#{$scope.type}/#{messageId}").then (response) ->
$scope.currentMessage =
uid: node.uid
reference: node.reference
condition: node.condition
if $scope.type == 'actives'
$scope.currentMessage.messages = response.data.messages
else
$scope.currentMessage.done = response.data.done
$scope.waiting = false
, (resp) ->
$scope.waiting = false
$scope.showT = false
# Modal launcher
$scope.showModal = (tpl, init) ->
modalInstance = $uibModal.open
templateUrl: tpl
controller: 'ModalInstanceCtrl'
size: 'lg'
resolve:
elem: ->
(s) ->
$scope[s]
set: ->
(f,s) ->
$scope[f] = s
init: ->
init
d = $q.defer()
modalInstance.result.then (msgok) ->
$scope.message =
title: ''
message: ''
d.resolve msgok
, (msgnok) ->
$scope.message =
title: ''
message: ''
d.reject msgnok
$scope.save = ->
if $scope.form.reference and $scope.form.body
$scope.waiting = true
if $scope.form.startDate
$scope.formPost.date = dateToString($scope.form.startDate)
if $scope.form.startTime
$scope.formPost.startTime = new Date $scope.form.startTime
if $scope.form.endDate
$scope.formPost.endDate = dateToString($scope.form.endDate)
if $scope.form.endTime
$scope.formPost.endTime = new Date $scope.form.endTime
$scope.formPost.reference = $scope.form.reference
$scope.formPost.level = $scope.form.level
$scope.formPost.condition = $scope.form.condition
$scope.formPost.body = $scope.form.body
$http.post('messages/actives', $scope.formPost).then (response) ->
data = response.data
$scope.form = {}
if data.result == 1
$scope.message =
title: 'messageCreated'
else
$scope.message =
title: 'messageNotCreated'
message: data.error
$scope.showModal "alert.html"
$scope.waiting = false
, (response) ->
$scope.message =
title: 'messageNotCreated'
message: response.statusText
$scope.showModal "alert.html"
$scope.waiting = false
else
$scope.message =
title: 'incompleteForm'
$scope.showModal "alert.html"
$scope.init = ->
$scope.waiting = true
$scope.showM = false
$scope.showT = false
$scope.data = []
$scope.currentScope = null
$scope.currentMessage = null
$q.all [
$translator.init $scope.lang
$scope.updateTree '', $scope.data, 0
]
.then ->
$scope.waiting = false
, (resp) ->
$scope.waiting = false
# Colorized link
$scope.activeModule = "messages"
$scope.myStyle = {color: '#ffb84d'}
$scope.displayCreateForm = ->
$scope.waiting = true
$translator.init($scope.lang).then ->
$scope.currentMessage = null
$scope.showForm = true
$scope.data = []
$scope.waiting = false
$scope.form.startDate = new Date()
c = $location.path().match /^\/(\w+)/
$scope.type = if c then c[1] else 'actives'
# Datepicker
$scope.popupopen = (start) ->
if start
$scope.popupSD.opened = true
else
$scope.popupED.opened = true
$scope.dateOptions =
startingDay: 1
minDate : new Date()
$scope.popupSD =
opened: false
$scope.popupED =
opened: false
# Timepicker
$scope.clearTime = (start) ->
if start
$scope.form.startTime = ''
else
$scope.form.endTime = ''
# Messages available levels
$scope.levels = ['danger', 'warning', 'notice']
$scope.form.level = $scope.levels[1]
# Date conversion
dateToString = (dt) ->
year = dt.getFullYear()
month = dt.getMonth() + 1
if month < 10
month = "0#{month}"
day = dt.getDate()
if day < 10
day = "0#{day}"
return "#{year}-#{month}-#{day}"
]
This diff is collapsed.
(function(){var i,m;m=[function(e){return"groupBy=substr(uid,1)"},function(e){return"uid="+e+"*&groupBy=uid"},function(e){return"uid="+e}],i={actives:[{title:"markAsDone",icon:"check"}],done:[{title:"deleteMessage",icon:"trash"}],new:[{title:"save",icon:"save"}],home:[]},angular.module("llngMessagesExplorer",["ui.tree","ui.bootstrap","llApp"]).controller("MessagesExplorerCtrl",["$scope","$translator","$location","$q","$http","$uibModal",function(l,t,e,a,r,s){var c,n,o;return l.links=links,l.menulinks=menulinks,l.staticPrefix=staticPrefix,l.scriptname=scriptname,l.formPrefix=formPrefix,l.availableLanguages=availableLanguages,l.waiting=!0,l.showM=!1,l.showT=!0,l.showForm=!1,l.data=[],l.form={},l.formPost={},l.currentScope=null,l.currentMessage=null,l.menu=i,l.translateP=t.translateP,l.translate=t.translate,l.translateTitle=function(e){return t.translateField(e,"title")},l.menuClick=function(e){if(e.popup)window.open(e.popup);else switch(e.action||(e.action=e.title),typeof e.action){case"function":e.action(l.currentNode,l);break;case"string":l[e.action]();break;default:console.log(typeof e.action)}return l.showM=!1},l.markAsDone=function(){return l.waiting=!0,r.put(scriptname+"messages/"+l.type+"/"+l.currentMessage.uid+"_"+l.currentMessage.reference,{done:1}).then(function(e){return l.currentMessage=null,l.currentScope.remove(),l.message={title:"messageDeleted"},l.showModal("alert.html"),l.waiting=!1,l.init()},function(e){return l.message={title:"messageNotDeleted",message:e.statusText},l.showModal("alert.html"),l.waiting=!1,l.init()})},l.deleteMessage=function(){return l.waiting=!0,r.delete(scriptname+"messages/"+l.type+"/"+l.currentMessage.uid+"_"+l.currentMessage.reference+"_"+l.currentMessage.done).then(function(e){return l.currentMessage=null,l.currentScope.remove(),l.message={title:"messagePurged"},l.showModal("alert.html"),l.waiting=!1,l.init()},function(e){return l.message={title:"messageNotPurged",message:e.statusText},l.showModal("alert.html"),l.waiting=!1,l.init()})},l.stoggle=function(e){var t;if(0===(t=e.$modelValue).nodes.length)return l.updateTree(t.value,t.nodes,t.level,t.query),e.toggle()},l.msgDate=function(e){return null!=e?(e.match(/(\d{4})-(\d{2})-(\d{2})/)&&(e=e.substr(0,4)+e.substr(5,2)+e.substr(8,2)),new Date(e.substr(0,4),e.substr(4,2)-1,e.substr(6,2)).toLocaleDateString()):""},l.getLanguage=function(e){return l.lang=e,l.form="white",l.init(),l.showM=!1},l.$on("$locationChangeSuccess",function(e,t,n){var r;return r=t.match(/#!?\/(\w+)/),l.type=null!=r?r[1]:"actives","new"===l.type?l.displayCreateForm():(l.showForm=!1,l.init())}),c=0,l.updateTree=function(e,o,i,t){var u;return l.waiting=!0,u=m[i](e,t),r.get(scriptname+"messages/"+l.type+"?"+u).then(function(e){var t,n,r,a,s;if((t=e.data).result)for(n=0,r=(s=t.values).length;n<r;n++)a=s[n],c++,a.id="node"+c,i<m.length-1&&(a.nodes=[],a.level=i+1,a.query=u),o.push(a);return l.waiting=!1},function(e){return l.waiting=!1})},l.displayMessage=function(e){var t,n;return l.waiting=!0,l.currentScope=e,t=(n=e.$modelValue).message,"actives"===l.type&&(t=n.uid+"_"+n.reference),r.get(scriptname+"messages/"+l.type+"/"+t).then(function(e){return l.currentMessage={uid:n.uid,reference:n.reference,condition:n.condition},"actives"===l.type?l.currentMessage.messages=e.data.messages:l.currentMessage.done=e.data.done,l.waiting=!1},function(e){return l.waiting=!1}),l.showT=!1},l.showModal=function(e,t){var n,r;return r=s.open({templateUrl:e,controller:"ModalInstanceCtrl",size:"lg",resolve:{elem:function(){return function(e){return l[e]}},set:function(){return function(e,t){return l[e]=t}},init:function(){return t}}}),n=a.defer(),r.result.then(function(e){return l.message={title:"",message:""},n.resolve(e)},function(e){return l.message={title:"",message:""},n.reject(e)})},l.save=function(){return l.form.reference&&l.form.body?(l.waiting=!0,l.form.startDate&&(l.formPost.date=o(l.form.startDate)),l.form.startTime&&(l.formPost.startTime=new Date(l.form.startTime)),l.form.endDate&&(l.formPost.endDate=o(l.form.endDate)),l.form.endTime&&(l.formPost.endTime=new Date(l.form.endTime)),l.formPost.reference=l.form.reference,l.formPost.level=l.form.level,l.formPost.condition=l.form.condition,l.formPost.body=l.form.body,r.post("messages/actives",l.formPost).then(function(e){var t;return t=e.data,l.form={},1===t.result?l.message={title:"messageCreated"}:l.message={title:"messageNotCreated",message:t.error},l.showModal("alert.html"),l.waiting=!1},function(e){return l.message={title:"messageNotCreated",message:e.statusText},l.showModal("alert.html"),l.waiting=!1})):(l.message={title:"incompleteForm"},l.showModal("alert.html"))},l.init=function(){return l.waiting=!0,l.showM=!1,l.showT=!1,l.data=[],l.currentScope=null,l.currentMessage=null,a.all([t.init(l.lang),l.updateTree("",l.data,0)]).then(function(){return l.waiting=!1},function(e){return l.waiting=!1}),l.activeModule="messages",l.myStyle={color:"#ffb84d"}},l.displayCreateForm=function(){return l.waiting=!0,t.init(l.lang).then(function(){return l.currentMessage=null,l.showForm=!0,l.data=[],l.waiting=!1,l.form.startDate=new Date})},n=e.path().match(/^\/(\w+)/),l.type=n?n[1]:"actives",l.popupopen=function(e){return e?l.popupSD.opened=!0:l.popupED.opened=!0},l.dateOptions={startingDay:1,minDate:new Date},l.popupSD={opened:!1},l.popupED={opened:!1},l.clearTime=function(e){return e?l.form.startTime="":l.form.endTime=""},l.levels=["danger","warning","notice"],l.form.level=l.levels[1],o=function(e){var t,n,r;return r=e.getFullYear(),(n=e.getMonth()+1)<10&&(n="0"+n),(t=e.getDate())<10&&(t="0"+t),r+"-"+n+"-"+t}}])}).call(this);
\ No newline at end of file
{"version":3,"sources":["lemonldap-ng-manager/site/htdocs/static/js/messages.js"],"names":["menu","scheme","v","actives","title","icon","done","new","home","angular","module","controller","$scope","$translator","$location","$q","$http","$uibModal","autoId","c","dateToString","links","menulinks","staticPrefix","scriptname","formPrefix","availableLanguages","waiting","showM","showT","showForm","data","form","formPost","currentScope","currentMessage","translateP","translate","translateTitle","node","translateField","menuClick","button","popup","window","open","action","currentNode","console","log","markAsDone","put","type","uid","reference","then","response","remove","message","showModal","init","statusText","deleteMessage","stoggle","scope","$modelValue","nodes","length","updateTree","value","level","query","toggle","msgDate","s","match","substr","Date","toLocaleDateString","getLanguage","lang","$on","event","next","current","n","displayCreateForm","currentQuery","get","i","len","ref","result","values","id","push","resp","displayMessage","messageId","condition","messages","tpl","d","modalInstance","templateUrl","size","resolve","elem","set","f","defer","msgok","msgnok","reject","save","body","startDate","date","startTime","endDate","endTime","post","error","all","activeModule","myStyle","color","path","popupopen","start","popupSD","opened","popupED","dateOptions","startingDay","minDate","clearTime","levels","dt","day","month","year","getFullYear","getMonth","getDate","call","this"],"mappings":"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"}
\ No newline at end of file
......@@ -316,7 +316,7 @@
"impersonationHiddenAttributes":"Attributs masqués",
"impersonationMergeSSOgroups":"Fusionner les groupes SSO réels et usurpés",
"impersonationSkipEmptyValues":"Ignorer les valeurs nulles",
"incompleteForm":"Des champs requis manquent",
"incompleteForm":"Des champs requis sont manquants",
"index":"Index",
"infoFormMethod":"Méthode du formulaire d'information",
"invalidSessionData":"Donnée de session invalide",
......
<TMPL_INCLUDE NAME="header.tpl">
<title>LemonLDAP::NG messages explorer</title>
</head>
<body ng-app="llngMessagesExplorer" ng-controller="MessagesExplorerCtrl" ng-csp>
<TMPL_INCLUDE NAME="menubar.tpl">
<div id="content" class="row container-fluid">
<div id="pleaseWait" ng-show="waiting"><span trspan="waitingForDatas"></span></div>
<!-- Tree -->
<aside id="left" class="col-lg-4 col-md-4 col-sm-5 col-xs-12 scrollable " ng-class="{'hidden-xs':!showT}" role="complementary">
<div class="navbar navbar-default">
<div class="navbar-collapse">
<ul class="nav navbar-nav" role="grid">
<li><a id="a-actives" href="#" role="row"><i class="glyphicon glyphicon-eye-open"></i> {{translate('actives')}}</a></li>
<li><a id="a-done" href="#!/done" role="row"><i class="glyphicon glyphicon-check"></i> {{translate('dones')}}</a></li>
<li><a id="a-new" href="#!/new" role="row"><i class="glyphicon glyphicon-plus-sign"></i> {{translate('create')}}</a></li>
</ul>
</div>
</div>
<div class="region region-sidebar-first">
<section id="block-superfish-1" class="block block-superfish clearfix">
<div ui-tree data-drag-enabled="false" id="tree-root">
<div ng-show="data.length==0" class="center">
<span class="label label-warning" trspan="noDatas"></span>
</div>
<ol ui-tree-nodes="" ng-model="data">
<li ng-repeat="node in data track by node.id" ui-tree-node ng-include="'nodes_renderer.html'" collapsed="true"></li>
</ol>
</div>
</section>
</div>
<div class="hresizer hidden-xs" resizer="vertical" resizer-left="#left" resizer-right="#right"></div>
</aside>
<!-- Right(main) div -->
<div id="right" class="col-lg-8 col-md-8 col-sm-7 col-xs-12 scrollable" ng-class="{'hidden-xs':showT&&!showM}">
<!-- Menu buttons -->
<div ng-if="type=='new'||currentMessage" class="lmmenu navbar navbar-default" ng-class="{'hidden-xs':!showM}">
<div class="navbar-collapse" ng-class="{'collapse':!showM}" id="formmenu">
<ul class="nav navbar-nav">
<li ng-repeat="button in menu[type]" ng-include="'menubutton.html'"></li>
<li uib-dropdown class="visible-xs">
<a id="langmenu" name="menu" uib-dropdown-toggle data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Menu <span class="caret"></span></a>
<ul uib-dropdown-menu aria-labelled-by="langmenu" role="grid">
<li ng-repeat="link in links"><a href="{{link.target}}" role="row"><i ng-if="link.icon" class="glyphicon glyphicon-{{link.icon}}"></i> {{translate(link.title)}}</a></li>
<li ng-repeat="menulink in menulinks"><a href="{{menulink.target}}" role="row"><i ng-if="menulink.icon" class="glyphicon glyphicon-{{menulink.icon}}"></i> {{translate(menulink.title)}}</a></li>
<li ng-include="'languages.html'"></li>
</ul>
</li>
</ul>
</div>
</div>
<!-- Message content -->
<div class="panel panel-default" ng-hide="currentMessage===null">
<div class="panel-heading">
<h1 class="panel-title text-center">{{translate('view')}}</h1>
</div>
<table class="table">
<tr>
<th><span trspan="uid" /></th>
<td>{{currentMessage.uid}}</td>
</tr>
<tr>
<th><span trspan="reference" /></th>
<td>{{currentMessage.reference}}</td>
</tr>
<tr ng-if="currentMessage.done">
<th><span trspan="internalReference" /></th>
<td>{{currentMessage.done}}</td>
</tr>
<tr ng-if="currentMessage.messages">
<th>Messages</th>
<td><pre ng-repeat="n in currentMessage.messages">{{n}}</pre></td>
</tr>
</table>
</div>
<!-- Create form -->
<div class="panel panel-default" ng-if="showForm">
<div class="panel-heading">
<h1 class="panel-title text-center">{{translate('create')}}</h1>
</div>
<form>
<table class="table">
<tr>
<th><span trspan="reference" /></th>
<td><input type="text" class="form-control" ng-model="form.reference" /></td>
</tr>
<tr>
<th><span trspan="condition" /></th>
<td><input type="text" class="form-control" ng-model="form.condition"/></td>
</tr>
<tr>
<th><span trspan="level" /></th>
<td><select class="form-control" ng-model="form.level">
<option ng-repeat="level in levels" value="{{level}}" ng-selected="form.level==level">{{level}}</option>
</select></td>
</tr>
<tr>
<th><span trspan="startDate" /></th>
<td>
<p class="input-group">
<input type="text" class="form-control" uib-datepicker-popup="yyyy-MM-dd" ng-model="form.startDate" min-date="minDate" is-open="popupSD.opened" datepicker-options="dateOptions" popup-placement="auto top-right"/>
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="popupopen(1)"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</p>
<p class="input-group">
<span uib-timepicker ng-model="form.startTime" ng-change="changed()" show-meridian="ismeridian"></span>
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="clearTime(1)"><i class="glyphicon glyphicon-time"></i></button>
</span>
</p>
</td>
</tr>
<tr>
<th><span trspan="endDate" /></th>
<td>
<p class="input-group">
<input type="text" class="form-control" uib-datepicker-popup="yyyy-MM-dd" ng-model="form.endDate" min-date="minDate" is-open="popupED.opened" datepicker-options="dateOptions" popup-placement="auto top-right"/>
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="popupopen()"><i class="glyphicon glyphicon-calendar"></i></button>
</span>
</p>
<p class="input-group">
<span uib-timepicker ng-model="form.endTime" ng-change="changed()" show-meridian="ismeridian"></span>
<span class="input-group-btn">
<button type="button" class="btn btn-default" ng-click="clearTime()"><i class="glyphicon glyphicon-time"></i></button>
</span>
</p>
</td>
</tr>
<th><span trspan="content" /></th>
<td>
<textarea rows=5 class="form-control" ng-model="form.body"></textarea>
<div class="alert alert-info">
<p><span trspan="allowedMarkups" /></p>
<table border="0">
<thead>
<tr><th>JSON</th></tr>
</thead>
<tbody><tr>
<td>
<pre>
{
"title": "...",
"subtitle": "...",
"text": "..."
}
</pre>
</td>
</tr></tbody></table>
</div>
</td>
</tr>
</table>
</form>
</div>
</div>
</div>
<script type="text/ng-template" id="nodes_renderer.html">
<div ui-tree-handle class="tree-node tree-node-content panel-info" ng-class="{'bg-info':this.$modelValue===currentMessage.$modelValue,'tree-node-default':this.$modelValue!==currentMessage.$modelValue}">
<span ng-if="node.value">
<a id="a-{{node.value}}" class="btn btn-node btn-sm" ng-click="stoggle(this)">
<span class="glyphicon" ng-class="{'glyphicon-chevron-right': collapsed,'glyphicon-chevron-down': !collapsed}"></span>
</a>
<span id="s-{{node.value}}" ng-click="stoggle(this)">{{node.value}} <span class="badge">{{node.count}}</span></span>
</span>
<span ng-if="node.message">
<a class="btn btn-node btn-sm" ng-click="displayMessage(this)">
<span class="glyphicon glyphicon-pencil"></span>
</a>
<span id="s-{{node.message}}" ng-click="displayMessage(this)">{{node.reference}} <i ng-if="node.startDate">({{msgDate(node.startDate)}})</i></span>
</span>
</div>
<ol ui-tree-nodes="" ng-model="node.nodes" ng-class="{hidden: collapsed}">
<li ng-repeat="node in node.nodes track by node.id" ui-tree-node ng-include="'nodes_renderer.html'" collapsed="true"></li>
</ol>
</script>
<script type="text/ng-template" id="alert.html">
<div class="modal-header">
<h3 class="modal-title" trspan="{{elem('message').title}}" />
</div>
<div class="modal-body" ng-if="elem('message').message">
<div class="modal-p">{{translateP(elem('message').message)}}</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" id="promptok" ng-click="ok()" trspan="ok"></button>
<button class="btn btn-warning" ng-click="cancel()" trspan="cancel"></button>
</div>
</script>
<TMPL_INCLUDE NAME="scripts.tpl">
<!-- //if:jsminified
<script type="text/javascript" src="<TMPL_VAR NAME="STATIC_PREFIX">js/messages.min.js"></script>
//else -->
<script type="text/javascript" src="<TMPL_VAR NAME="STATIC_PREFIX">js/messages.js"></script>
<!-- //endif -->
<TMPL_INCLUDE NAME="footer.tpl">
<TMPL_INCLUDE NAME="header.tpl">
<title>LemonLDAP::NG Manager</title>
<title>LemonLDAP::NG Viewer</title>
<link rel="prefetch" href="<TMPL_VAR NAME="STATIC_PREFIX">forms/home.html" />
<link rel="prefetch" href="<TMPL_VAR NAME="STATIC_PREFIX">struct.json" />
</head>
......