Working on notifications (#595)

This commit is contained in:
Xavier Guimard 2016-05-29 07:48:06 +00:00
parent 0a54423e6f
commit 205bf2e7ea

View File

@ -1,15 +1,30 @@
# Notifications plugin.
#
# Three entry points for notifications:
# * a new route "/notifback" for checking accepted notifications
# (sub getNotifBack). It launch then autoRedirect() with "mustRedirect"
# set to 1 because underlying handler has not seen user as authenticated
# so datas are not set;
# * two callbacks inserted in process steps:
# - one inserted after authentication process,
# - one for authenticated users,
# These callbacks launch checkForNotifications to get notification and
# cipher LemonLDAP::NG cookies if any found.
package Lemonldap::NG::Portal::Plugins::Notifications; package Lemonldap::NG::Portal::Plugins::Notifications;
use strict; use strict;
use Mouse; use Mouse;
use XML::LibXML; use XML::LibXML;
use Lemonldap::NG::Portal::Main::Constants qw(PE_OK PE_NOTIFICATION);
our $VERSION = '2.0.0'; our $VERSION = '2.0.0';
# Lemonldap::NG::Portal::Main::Plugin provides addAuthRoute() and
# addUnauthRoute() methods in addition of Lemonldap::NG::Portal::Main::Module.
extends 'Lemonldap::NG::Portal::Main::Plugin'; extends 'Lemonldap::NG::Portal::Main::Plugin';
has notifObject => ( is => 'rw' ); # XML parser. TODO: replace this by JSON
has parser => ( has parser => (
is => 'rw', is => 'rw',
builder => sub { builder => sub {
@ -17,26 +32,47 @@ has parser => (
} }
); );
# Two entry points for notifications: # XSLT document
# * a new route "/reference" for checking accepted notifications has stylesheet => (
# (sub getNotifBack). It launch then autoRedirect() with "mustRedirect" is => 'rw',
# set to 1 because underlying handler has not seen user as authenticated lazy => 1,
# so datas are not set; builder => sub {
# * a process step inserted after authentication process: my $self = $_[0];
# checkForNotifications that cipher cookies my $xslt = XML::LibXSLT->new();
my $styleFile =
( -e $self->conf->{notificationXSLTfile}
? $self->conf->{notificationXSLTfile}
: $self->conf->{templatesDir} . '/skins/common/notification.xsl' );
return $xslt->parse_stylesheet( $self->parser($styleFile) );
}
);
# Underlying notifications storage object (File, DBI, LDAP,...)
has notifObject => ( is => 'rw' );
# Declare additional process steps
sub afterDatas { 'checkNotifDuringAuth' }
sub forAuthUser { 'checkNotifForAuthUser' }
# Plugin initialization
sub init { sub init {
my ($self) = @_; my ($self) = @_;
$self->addUnauthRoute( 'reference' => 'getNotifBack', ['POST'] );
$self->addAuthRoute( 'reference' => 'getNotifBack', ['POST'] );
# Use configuration options # Declare new route
$self->addUnauthRoute( 'notifback' => 'getNotifBack', ['POST'] );
$self->addAuthRoute( 'notifback' => 'getNotifBack', ['POST'] );
# Search for configuration options
my $type = $self->conf->{notificationStorage}; my $type = $self->conf->{notificationStorage};
unless ($type) { unless ($type) {
$self->error('notificationStorage is not defined, aborting'); $self->error('notificationStorage is not defined, aborting');
return 0; return 0;
} }
eval "require Lemonldap::NG::Common::Notification::$type";
# Initialize notifications storage object
$type = "Lemonldap::NG::Common::Notification::$type";
eval "require $type";
if ($@) { if ($@) {
$self->error($@); $self->error($@);
return 0; return 0;
@ -47,10 +83,7 @@ sub init {
unless ( unless (
eval { eval {
$self->notifObject( $self->notifObject(
"Lemonldap::NG::Common::Notification::$type"->new( $type->new( $self->conf->{notificationStorageOptions} ) );
$self->conf->{notificationStorageOptions}
)
);
} }
) )
{ {
@ -60,25 +93,133 @@ sub init {
1; 1;
} }
sub afterDatas { 'checkForNotifications' } sub checkNotifDuringAuth {
my ( $self, $req ) = @_;
if ( my $notif = $self->checkForNotifications($req) ) {
# Cipher cookies
}
else {
return PE_OK;
}
}
sub checkNotifForAuthUser {
my ( $self, $req ) = @_;
if ( my $notif = $self->checkForNotifications($req) ) {
# Restore and cipher cookies
}
else {
return PE_OK;
}
}
has notifField => (
is => 'rw',
builder => sub {
return
$_[0]->conf->{notificationField}
|| $_[0]->conf->{whatToTrace}
|| 'uid';
}
);
# Search for notifications and if any, returns HTML fragment.
# TODO: replace this by JSON+Ajax
sub checkForNotifications { sub checkForNotifications {
my ( $self, $req ) = @_; my ( $self, $req ) = @_;
# Look for pending notifications in database # Look for pending notifications in database
my $uid = $req->sessionInfo->{ $self->notifField };
my $notifs;
my $form;
{
my $forUser = $self->notifObject->get($uid);
my $forAll =
$self->notifObject->get( $self->conf->{notificationWildcard} );
if ( $forUser and $forAll ) { $notifs = { %$forUser, %$forAll }; }
else { $notifs = $forUser ? $forUser : $forAll; }
}
return 0 unless ($notifs);
# If found, store them and cipher cookies # Transform notifications
PE_OK; my $i=0;#Files count
foreach my $file (values %$notifs) {
eval {
my $xml = $self->parser->parse_string($file);
my $j=0;#Notifications count
foreach my $notif($xml->documentElement->getElementsByTagName('notification') ){
# Get the reference
my $reference = $notif->getAttribute('reference');
$self->lmLog( "Get reference $reference", 'debug' );
# Check it in session
if ( exists $req->{sessionInfo}->{ "notification_$reference" } ) {
# The notification was already accepted
$self->lmLog(
"Notification $reference was already accepted",
'debug' );
# Remove it from XML
$notif->unbindNode();
next;
}
# Check condition if any
my $condition = $notif->getAttribute('condition');
if ($condition) {
$self->lmLog( "Get condition $condition", 'debug' );
unless ( $self->p->HANDLER->safe->reval($condition) ) {
$self->lmLog( "Notification condition not accepted",
'debug' );
# Remove it from XML
$notif->unbindNode();
next;
}
}
$j++;
}
# Go to next file if no notification found
next unless $j;
$i++;
# Transform XML into HTML
my $results = $self->stylesheet->transform( $xml, start => $i );
$form .= $self->stylesheet->output_string($results);
};
if ($@) {
$self->lmLog(
"Bad XML file: a notification for $uid was not done ($@)",
'warn' );
return 0;
}
}
# Stop here if nothing to display
return 0 unless $i;
# Returns HTML fragment
return $form;
} }
sub getNotifBack { sub getNotifBack {
my ( $self, $req, $name ) = @_; my ( $self, $req, $name ) = @_;
# Look if al notifications have been accepted. If not, redirects to # Look if all notifications have been accepted. If not, redirects to
# portal # portal
#TODO
# Else restore args and launch autoredirect # Else uncipher cookie, restore args and launch autoredirect
# TODO # TODO
$self->p->do( $req, [] ); $self->p->do( $req, [] );
} }