lemonldap-ng/lemonldap-ng-common/lib/Lemonldap/NG/Common/Notifications/File.pm

241 lines
6.8 KiB
Perl

## @file
# File storage methods for notifications
## @class
# File storage methods for notifications
package Lemonldap::NG::Common::Notifications::File;
use strict;
use Mouse;
use Time::Local;
use MIME::Base64;
our $VERSION = '2.1.0';
extends 'Lemonldap::NG::Common::Notifications';
sub import {
shift;
return Lemonldap::NG::Common::Notifications->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 notifications corresponding to the user $uid.
# If $ref is set, returns only notification corresponding to this reference.
sub get {
my ( $self, $uid, $ref ) = @_;
return () unless ($uid);
my $ext = $self->extension;
my $fns = $self->{fileNameSeparator};
my $identifier = &getIdentifier( $self, $uid, $ref );
opendir D, $self->{dirName};
my @notif = grep /^\d{8}${fns}${identifier}\S*\.$ext$/, readdir(D);
closedir D;
my $files;
foreach my $file (@notif) {
unless ( open F, '<', $self->{dirName} . "/$file" ) {
$self->logger->error(
"Unable to read notification $self->{dirName}/$file");
next;
}
$files->{$file} = join( '', <F> );
}
return $files;
}
# Returns accepted notification corresponding to the user $uid.
# If $ref is set, returns only notification corresponding to this reference.
sub getAccepted {
my ( $self, $uid, $ref ) = @_;
return () unless ($uid);
my $ext = $self->extension;
my $fns = $self->{fileNameSeparator};
my $identifier = &getIdentifier( $self, $uid, $ref );
opendir D, $self->{dirName};
my @notif = grep /^\d{8}${fns}${identifier}\S*\.(?:done|$ext)$/, readdir(D);
closedir D;
my $files;
foreach my $file (@notif) {
unless ( open F, '<', $self->{dirName} . "/$file" ) {
$self->logger->error(
"Unable to read notification $self->{dirName}/$file");
next;
}
$files->{$file} = join( '', <F> );
}
return $files;
}
## @method hashref getAll()
# Return all pending notifications.
# @return hashref where keys are internal reference and values are hashref with
# keys date, uid, ref and condition.
sub getAll {
my $self = shift;
my $ext = $self->extension;
opendir D, $self->{dirName};
my @notif;
my $fns = $self->{fileNameSeparator};
@notif = 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 // '' )
}
)
: ()
} @notif;
return \%h;
}
## @method hashref getExisting()
# Return all notifications.
# @return hashref where keys are internal reference and values are hashref with
# keys date, uid, ref and condition.
sub getExisting {
my $self = shift;
my $ext = $self->extension;
opendir D, $self->{dirName};
my @notif;
my $fns = $self->{fileNameSeparator};
@notif = grep /^\S*\.$ext$/, readdir(D);
my %h = map {
/^(\d{8})${fns}([^\s${fns}]+)${fns}([^\s${fns}]+)(?:${fns}([^\s${fns}]+))?\.(?:$ext|done)$/
? (
$_ => {
date => $1,
uid => $2,
ref => decode_base64($3),
condition => decode_base64( $4 // '' )
}
)
: ()
} @notif;
return \%h;
}
## @method boolean delete(string myref)
# Mark a notification as done.
# @param $myref identifier returned by get() or getAll()
sub delete {
my ( $self, $myref ) = @_;
my $ext = $self->extension;
my $new = ( $myref =~ /(.*?)(?:\.$ext)$/ )[0] . '.done';
return rename( $self->{dirName} . "/$myref", $self->{dirName} . "/$new" );
}
## @method boolean purge(string myref)
# Purge notification (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 notification
sub newNotif {
my ( $self, $date, $uid, $ref, $condition, $content ) = @_;
my $ext = $self->extension;
my $fns = $self->{fileNameSeparator};
$fns ||= '_';
my @t = split( /\D+/, $date );
$t[1]--;
eval {
timelocal( $t[5] || 0, $t[4] || 0, $t[3] || 0, $t[2], $t[1], $t[0] );
};
return ( 0, "Bad date" ) if ($@);
$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 notification still exists' ) if ( -e $filename );
my $old = ( $filename =~ /(.*?)(?:\.$ext)$/ )[0] . '.done';
return ( 0, 'This notification 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 notification 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 @notif;
my $fns = $self->{fileNameSeparator};
@notif = grep /^\d{8}${fns}\S*\.done$/, readdir(D);
my $res;
foreach my $file (@notif) {
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 notification identifier
# @param $uid uid
# @param $ref ref
# @param $date date
# @return the notification identifier
sub getIdentifier {
my ( $self, $uid, $ref, $date ) = @_;
my $result;
# Special fix to manage purge from notification explorer
return $date if $date;
my $fns = $self->{fileNameSeparator};
if ($date) {
$result .= $date . $fns;
}
$result .= $uid;
if ($ref) {
my $tmp = encode_base64( $ref, '' );
$result .= $fns . $tmp;
}
return $result;
}
1;