From 18b9e3e8931d6588f3e7e722a7f7793f65570ba7 Mon Sep 17 00:00:00 2001 From: vikin91 Date: Fri, 3 Mar 2017 15:31:13 +0100 Subject: [PATCH 01/21] Fix problem with websockets over ssh. Fix bug with bad bibspace.dat path. Add persistence to the statistics. Remove logging of attachment downloads (use statistics instead) --- lib/BibSpace.pm | 10 +- lib/BibSpace/Controller/Display.pm | 15 +- lib/BibSpace/Controller/Publications.pm | 6 - lib/BibSpace/Util/Preferences.pm | 12 +- lib/BibSpace/Util/Statistics.pm | 14 +- .../files/templates/display/log.html.ep | 31 +--- .../files/templates/display/stats.html.ep | 47 +++++ util/init.d-script.sh | 165 ++++++++++++++++++ 8 files changed, 257 insertions(+), 43 deletions(-) create mode 100644 lib/BibSpace/files/templates/display/stats.html.ep create mode 100644 util/init.d-script.sh diff --git a/lib/BibSpace.pm b/lib/BibSpace.pm index 960e0da..46da81d 100644 --- a/lib/BibSpace.pm +++ b/lib/BibSpace.pm @@ -58,11 +58,13 @@ use feature qw( state say ); ## OBJECTS CREATED AND CONTAINED IN HAS METHODS CANNOT BE CHANGED AT RUNTIME! has preferences => sub { - return state $prefs = Preferences->new->load_maybe; + my $self = shift; + return state $prefs = Preferences->new(filename => $self->app->home->rel_file("bibspace_preferences.json"))->load_maybe; }; has statistics => sub { - return state $stats = Statistics->new; + my $self = shift; + return state $stats = Statistics->new(filename => $self->app->home->rel_file("bibspace_stats.json"))->load_maybe; }; @@ -122,7 +124,8 @@ has version => sub { }; has quick_load_fixture_filename => sub { - return 'bibspace.dat'; + my $self = shift; + return $self->app->home->rel_file('bibspace.dat'); }; # don't want to read data form DB and wait to link them every reload? @@ -581,6 +584,7 @@ sub setup_routes { ->name('make_admin'); $manager_user->get('/log')->to('display#show_log')->name('show_log'); + $manager_user->get('/statistics')->to('display#show_stats')->name('show_stats'); # websocket for fun $manager_user->websocket('/log_websocket/:num')->to('display#show_log_ws')->name('show_log_websocket'); $manager_user->websocket('/statistics/:num')->to('display#show_stats_websocket')->name('show_stats_websocket'); diff --git a/lib/BibSpace/Controller/Display.pm b/lib/BibSpace/Controller/Display.pm index c19c0ce..ad7d7cd 100644 --- a/lib/BibSpace/Controller/Display.pm +++ b/lib/BibSpace/Controller/Display.pm @@ -105,9 +105,20 @@ sub show_log_ws { $self->on(finish => sub { my ($c, $code, $reason) = @_; - say "WS closed"; + say "show_log_ws WS closed"; }); } +################################################################################# +sub show_stats { + my $self = shift; + my $num = $self->param('num') // 20; + + my @lines = $self->app->statistics->toLines; + + $self->stash( lines => \@lines, num => $num); + $self->render( template => 'display/stats' ); +} + ################################################################################# sub show_stats_websocket { my $self = shift; @@ -124,7 +135,7 @@ sub show_stats_websocket { $self->on(finish => sub { my ($c, $code, $reason) = @_; - say "WS closed"; + say "show_stats_websocket WS closed"; }); } ################################################################################# diff --git a/lib/BibSpace/Controller/Publications.pm b/lib/BibSpace/Controller/Publications.pm index 307fab3..76b855b 100644 --- a/lib/BibSpace/Controller/Publications.pm +++ b/lib/BibSpace/Controller/Publications.pm @@ -571,10 +571,6 @@ sub download { my $id = $self->param('id'); # entry ID my $filetype = $self->param('filetype'); - $self->app->logger->info( - "Requested to download attachment of type '$filetype' for entry ID '" - . $id - . "'." ); my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); my $file; @@ -590,8 +586,6 @@ sub download { } if ( $file and -e $file ) { - $self->app->logger->info( - "Downloading file download '$filetype' for entry '$id'."); $self->render_file( 'filepath' => $file ); return; } diff --git a/lib/BibSpace/Util/Preferences.pm b/lib/BibSpace/Util/Preferences.pm index d5b61ec..f646b94 100644 --- a/lib/BibSpace/Util/Preferences.pm +++ b/lib/BibSpace/Util/Preferences.pm @@ -17,17 +17,21 @@ use Moose::Util::TypeConstraints; use MooseX::Storage; with Storage( 'format' => 'JSON', 'io' => 'File' ); +has 'filename' =>( is => 'ro', isa => 'Str', default => "bibspace_preferences.json"); # I can't name it load due to deep recursion (direct or indirect) sub load_maybe { my $self = shift; - if ( -e 'bibspace_preferences.json' ) { - say "Loading prefeerences from file 'bibspace_preferences.json'."; - return Preferences->load('bibspace_preferences.json'); + if ( -e $self->filename ) { + return Preferences->load($self->filename); } return $self; } + + + + has 'run_in_demo_mode' => ( is => 'rw', isa => 'Int', default => 0, trigger => \&_pref_changed ); @@ -68,7 +72,7 @@ sub _pref_changed { if ( $prev_val and $curr_val ne $prev_val ) { say "A preference changed to '$curr_val'."; - $self->store('bibspace_preferences.json'); + $self->store($self->filename); } } diff --git a/lib/BibSpace/Util/Statistics.pm b/lib/BibSpace/Util/Statistics.pm index 0bb6086..7b4cbd8 100644 --- a/lib/BibSpace/Util/Statistics.pm +++ b/lib/BibSpace/Util/Statistics.pm @@ -4,7 +4,8 @@ use namespace::autoclean; use feature qw( state say ); use Moose; - +use MooseX::Storage; +with Storage( 'format' => 'JSON', 'io' => 'File' ); has 'url_history' => ( traits => ['Hash'], @@ -24,6 +25,16 @@ has 'url_history' => ( }, ); +has 'filename' =>( is => 'ro', isa => 'Str', default => "bibspace_stats.json"); + +sub load_maybe { + my $self = shift; + if ( -e $self->filename ) { + return Statistics->load($self->filename); + } + return $self; +} + sub log_url { my $self = shift; @@ -36,6 +47,7 @@ sub log_url { my $num = $self->get($url); $self->set($url, $num+1); } + $self->store($self->filename); } sub toString { diff --git a/lib/BibSpace/files/templates/display/log.html.ep b/lib/BibSpace/files/templates/display/log.html.ep index 41f5bbf..ff3dcfc 100644 --- a/lib/BibSpace/files/templates/display/log.html.ep +++ b/lib/BibSpace/files/templates/display/log.html.ep @@ -25,11 +25,11 @@ @@ -58,15 +58,12 @@ $(function () { }; var log_ws = new WebSocket("<%= url_for('show_log_websocket', num => $num )->to_abs %>"); - var stats_ws = new WebSocket("<%= url_for('show_stats_websocket', num => $num )->to_abs %>"); log_ws.onopen = function () { $('#log_lines_pre').text(""); log_ws.send('.*'); }; - stats_ws.onopen = function () { - $('#log_lines_pre').text(""); - }; + log_ws.onmessage = function (msg) { var res = JSON.parse(msg.data); @@ -77,22 +74,9 @@ $(function () { log(lines); }; - stats_ws.onmessage = function (msg) { - var res = JSON.parse(msg.data); - var lines =''; - $.each(res, function(i,o) { - if(o){ lines += o+'\n';} - }); - log(lines); - }; $('#filterexpr').keyup(function (e) { - if(mode == 'log'){ - log_ws.send($('#filterexpr').val()); - } - else{ - stats_ws.send($('#filterexpr').val()); - } + log_ws.send($('#filterexpr').val()); }); $('#log_button').click(function (e) { @@ -102,13 +86,6 @@ $(function () { log_ws.send($('#filterexpr').val()); mode = 'log'; }); - $('#statistics_button').click(function (e) { - e.preventDefault(e); - $('#log_tab').removeClass("active"); - $('#statistics_tab').addClass("active"); - stats_ws.send($('#filterexpr').val()); - mode = 'stats'; - }); }); diff --git a/lib/BibSpace/files/templates/display/stats.html.ep b/lib/BibSpace/files/templates/display/stats.html.ep new file mode 100644 index 0000000..006c23a --- /dev/null +++ b/lib/BibSpace/files/templates/display/stats.html.ep @@ -0,0 +1,47 @@ +% layout 'admin'; +
+
+
+

Statistics

+
+
+ + Newer entries are at the bottom + +
+
+ + +
+
+
+
+ + + + +
+
+ + +
+
+
+
+
+% foreach my $line (@{$lines}){
+<%= $line %>
+% }
+
+
+
+
+ + diff --git a/util/init.d-script.sh b/util/init.d-script.sh new file mode 100644 index 0000000..f515cbd --- /dev/null +++ b/util/init.d-script.sh @@ -0,0 +1,165 @@ +#! /bin/sh +### BEGIN INIT INFO +# Provides: bibspace +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Bibspace +# Description: Publication management system +### END INIT INFO + +# Author: Piotr + +# Do NOT "set -e" + +# PATH should only include /usr/* if it runs after the mountnfs.sh script +PATH=/sbin:/usr/sbin:/bin:/usr/bin +DESC="Description of the service" +NAME=bibspace +DAEMON="/home/piotr/perl/bibspace/bin/$NAME" +DAEMON_ARGS="daemon -m production -l http://*:8083" +PIDFILE=/home/piotr/perl/bibspace/$NAME.pid +SCRIPTNAME=/etc/init.d/$NAME +USER=piotr + +# Exit if the package is not installed +[ -x "$DAEMON" ] || exit 0 + +# Read configuration variable file if it is present +[ -r /etc/default/$NAME ] && . /etc/default/$NAME + +# Load the VERBOSE setting and other rcS variables +. /lib/init/vars.sh + + +VERBOSE=yes +# Define LSB log_* functions. +# Depend on lsb-base (>= 3.2-14) to ensure that this file is present +# and status_of_proc is working. +. /lib/lsb/init-functions + +# +# Function that starts the daemon/service +# +do_start() +{ + # su piotr -c "BIBSPACE_CONFIG=/etc/bibspace.conf $DAEMON $DAEMON_ARGS &" + + export BIBSPACE_CONFIG=/etc/bibspace.conf + # echo "$DAEMON $DAEMON_ARGS &" + # $DAEMON $DAEMON_ARGS & + + # Return + # 0 if daemon has been started + # 1 if daemon was already running + # 2 if daemon could not be started + start-stop-daemon --start --background --chuid $USER --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \ + || return 1 + start-stop-daemon --start --background --chuid $USER --pidfile $PIDFILE --exec $DAEMON -- \ + $DAEMON_ARGS \ + || return 2 + # Add code here, if necessary, that waits for the process to be ready + # to handle requests from services started subsequently which depend + # on this one. As a last resort, sleep for some time. +} + +# +# Function that stops the daemon/service +# +do_stop() +{ + kill -s TERM `pidof perl $DAEMON` + # Return + # 0 if daemon has been stopped + # 1 if daemon was already stopped + # 2 if daemon could not be stopped + # other if a failure occurred + start-stop-daemon --stop --chuid $USER --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME + RETVAL="$?" + [ "$RETVAL" = 2 ] && return 2 + # Wait for children to finish too if this is a daemon that forks + # and if the daemon is only ever run from this initscript. + # If the above conditions are not satisfied then add some other code + # that waits for the process to drop all resources that could be + # needed by services started subsequently. A last resort is to + # sleep for some time. + start-stop-daemon --stop --chuid $USER --oknodo --retry=0/30/KILL/5 --exec $DAEMON + [ "$?" = 2 ] && return 2 + # Many daemons don't delete their pidfiles when they exit. + rm -f $PIDFILE + return "$RETVAL" +} + +# +# Function that sends a SIGHUP to the daemon/service +# +do_reload() { + # + # If the daemon can reload its configuration without + # restarting (for example, when it is sent a SIGHUP), + # then implement that here. + # + echo "Reload on BibSpace is not supported" + #start-stop-daemon --stop --signal 1 --quiet --pidfile $PIDFILE --name $NAME + #return 0 +} + +case "$1" in + start) + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" + do_start + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + stop) + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" + do_stop + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + status) + ps aux | grep "$DAEMON" + status_of_proc "perl $DAEMON" "$NAME" && exit 0 || exit $? + ;; + #reload|force-reload) + # + # If do_reload() is not implemented then leave this commented out + # and leave 'force-reload' as an alias for 'restart'. + # + #log_daemon_msg "Reloading $DESC" "$NAME" + #do_reload + #log_end_msg $? + #;; + restart|force-reload) + # + # If the "reload" option is implemented then remove the + # 'force-reload' alias + # + log_daemon_msg "Restarting $DESC" "$NAME" + do_stop + case "$?" in + 0|1) + do_start + case "$?" in + 0) log_end_msg 0 ;; + 1) log_end_msg 1 ;; # Old process is still running + *) log_end_msg 1 ;; # Failed to start + esac + ;; + *) + # Failed to stop + log_end_msg 1 + ;; + esac + ;; + *) + #echo "Usage: $SCRIPTNAME {start|stop|restart|reload|force-reload}" >&2 + echo "Usage: $SCRIPTNAME {start|stop|status|restart|force-reload}" >&2 + exit 3 + ;; +esac From 26305d4975e39052423e8a14bb9fa009f95f5700 Mon Sep 17 00:00:00 2001 From: vikin91 Date: Fri, 3 Mar 2017 15:37:15 +0100 Subject: [PATCH 02/21] Fix problem with preferences and stats file name --- lib/BibSpace.pm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/BibSpace.pm b/lib/BibSpace.pm index 46da81d..2c6ed1b 100644 --- a/lib/BibSpace.pm +++ b/lib/BibSpace.pm @@ -59,12 +59,12 @@ use feature qw( state say ); ## OBJECTS CREATED AND CONTAINED IN HAS METHODS CANNOT BE CHANGED AT RUNTIME! has preferences => sub { my $self = shift; - return state $prefs = Preferences->new(filename => $self->app->home->rel_file("bibspace_preferences.json"))->load_maybe; + return state $prefs = Preferences->new(filename => "".$self->app->home->rel_file("bibspace_preferences.json"))->load_maybe; }; has statistics => sub { my $self = shift; - return state $stats = Statistics->new(filename => $self->app->home->rel_file("bibspace_stats.json"))->load_maybe; + return state $stats = Statistics->new(filename => "".$self->app->home->rel_file("bibspace_stats.json"))->load_maybe; }; From 6cfbe16047830088a166fd7605bef391dbbb5df5 Mon Sep 17 00:00:00 2001 From: vikin91 Date: Fri, 3 Mar 2017 16:16:41 +0100 Subject: [PATCH 03/21] update debian util scripts --- util/init.d-script.sh | 7 ++++--- util/publiste_watchdog.sh | 1 + util/test_server_OK.sh | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/util/init.d-script.sh b/util/init.d-script.sh index f515cbd..fca42ea 100644 --- a/util/init.d-script.sh +++ b/util/init.d-script.sh @@ -1,11 +1,11 @@ #! /bin/sh ### BEGIN INIT INFO # Provides: bibspace -# Required-Start: $remote_fs $syslog -# Required-Stop: $remote_fs $syslog +# Required-Start: $remote_fs $syslog mysql +# Required-Stop: $remote_fs $syslog mysql # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 -# Short-Description: Bibspace +# Short-Description: BibSpace # Description: Publication management system ### END INIT INFO @@ -47,6 +47,7 @@ do_start() # su piotr -c "BIBSPACE_CONFIG=/etc/bibspace.conf $DAEMON $DAEMON_ARGS &" export BIBSPACE_CONFIG=/etc/bibspace.conf + export BIBSPACE_USE_DUMP=0 # echo "$DAEMON $DAEMON_ARGS &" # $DAEMON $DAEMON_ARGS & diff --git a/util/publiste_watchdog.sh b/util/publiste_watchdog.sh index e65a7ce..d562d26 100755 --- a/util/publiste_watchdog.sh +++ b/util/publiste_watchdog.sh @@ -15,6 +15,7 @@ fi if [ "$RESTART" = true ]; then pkill -9 -f "$DIR/bin/bibspace" export BIBSPACE_CONFIG=/etc/bibspace.conf + # start-stop-daemon --start --chuid piotr --pidfile /home/piotr/perl/bibspace/bibspace.pid --exec /home/piotr/perl/bibspace/bin/bibspace -- daemon -m production -l http:/:8083 /usr/bin/hypnotoad "$DIR/bin/bibspace" fi diff --git a/util/test_server_OK.sh b/util/test_server_OK.sh index aef98c1..2adcc43 100755 --- a/util/test_server_OK.sh +++ b/util/test_server_OK.sh @@ -1,4 +1,4 @@ #!/bin/bash -response=$(curl --write-out %{http_code} --silent -k --output /dev/null https://se2.informatik.uni-wuerzburg.de/pa/test) +response=$(curl --write-out %{http_code} --silent -k --output /dev/null https://se2.informatik.uni-wuerzburg.de/pa/system_status) echo $response From 511416b4e51f6cc148ca7d0e7582bd6355b1c41f Mon Sep 17 00:00:00 2001 From: vikin91 Date: Tue, 7 Mar 2017 15:17:30 +0100 Subject: [PATCH 04/21] fix bug regarding the default location of bibspace_preferences.json file --- lib/BibSpace/Controller/Cron.pm | 3 +- lib/BibSpace/Util/Preferences.pm | 95 +++++++++++++++++++------------- 2 files changed, 60 insertions(+), 38 deletions(-) diff --git a/lib/BibSpace/Controller/Cron.pm b/lib/BibSpace/Controller/Cron.pm index 8ec7f0e..2d08bc2 100644 --- a/lib/BibSpace/Controller/Cron.pm +++ b/lib/BibSpace/Controller/Cron.pm @@ -135,7 +135,6 @@ sub cron_run { } ############ Cron ACTIONS - $self->log_cron_usage($level); $self->app->logger->info("Cron level $level started"); if ( $level == 0 ) { @@ -156,6 +155,8 @@ sub cron_run { else { # do nothing } + # this may cause: [error] Unable to open file (bibspace_preferences.json) for storing : Permission denied at + $self->log_cron_usage($level); $self->app->logger->info("Cron level $level has finished"); return $text_to_render; diff --git a/lib/BibSpace/Util/Preferences.pm b/lib/BibSpace/Util/Preferences.pm index f646b94..4ebbeea 100644 --- a/lib/BibSpace/Util/Preferences.pm +++ b/lib/BibSpace/Util/Preferences.pm @@ -1,6 +1,6 @@ package Preferences; -use v5.16; +use v5.16; use Try::Tiny; use Data::Dumper; use namespace::autoclean; @@ -17,63 +17,84 @@ use Moose::Util::TypeConstraints; use MooseX::Storage; with Storage( 'format' => 'JSON', 'io' => 'File' ); -has 'filename' =>( is => 'ro', isa => 'Str', default => "bibspace_preferences.json"); +has 'filename' => ( + is => 'rw', + isa => 'Str', + default => "bibspace_preferences.json", + traits => ['DoNotSerialize'] +); # I can't name it load due to deep recursion (direct or indirect) sub load_maybe { - my $self = shift; - if ( -e $self->filename ) { - return Preferences->load($self->filename); - } - return $self; + my $self = shift; + if ( -e $self->filename ) { + my $prefs = Preferences->load( $self->filename ); + + # the filename should not be stored, so we need to copy it + $prefs->filename = $self->filename; + return $prefs; + } + return $self; } - - - has 'run_in_demo_mode' => - ( is => 'rw', isa => 'Int', default => 0, trigger => \&_pref_changed ); + ( is => 'rw', isa => 'Int', default => 0, trigger => \&_pref_changed ); -has 'bibitex_html_converter' => - ( is => 'rw', isa => 'Str', default => 'BibStyleConverter', trigger => \&_pref_changed ); +has 'bibitex_html_converter' => ( + is => 'rw', + isa => 'Str', + default => 'BibStyleConverter', + trigger => \&_pref_changed +); # important for Preferences form to set flag "(default)" by the right list item -has 'default_bibitex_html_converter' => ( is => 'ro', isa => 'Str', default => 'BibStyleConverter' ); - -has 'local_time_zone' => ( is => 'rw', isa => 'Str', default => 'Europe/Berlin', trigger => \&_pref_changed ); +has 'default_bibitex_html_converter' => + ( is => 'ro', isa => 'Str', default => 'BibStyleConverter' ); + +has 'local_time_zone' => ( + is => 'rw', + isa => 'Str', + default => 'Europe/Berlin', + trigger => \&_pref_changed +); # http://search.cpan.org/~drolsky/DateTime-1.42/lib/DateTime.pm#strftime_Patterns -has 'output_time_format' => ( is => 'rw', isa => 'Str', default => '%a %d %b %T, %Y', trigger => \&_pref_changed ); +has 'output_time_format' => ( + is => 'rw', + isa => 'Str', + default => '%a %d %b %T, %Y', + trigger => \&_pref_changed +); # cron_level => last_call has 'cron' => ( - traits => ['Hash'], - is => 'rw', - isa => 'HashRef[Str]', - default => sub { {} }, - trigger => \&_pref_changed, - handles => { - cron_set => 'set', - cron_get => 'get', - cron_has => 'exists', - cron_defined => 'defined', - cron_keys => 'keys', - cron_values => 'values', - cron_num => 'count', - cron_pairs => 'kv', - }, + traits => ['Hash'], + is => 'rw', + isa => 'HashRef[Str]', + default => sub { {} }, + trigger => \&_pref_changed, + handles => { + cron_set => 'set', + cron_get => 'get', + cron_has => 'exists', + cron_defined => 'defined', + cron_keys => 'keys', + cron_values => 'values', + cron_num => 'count', + cron_pairs => 'kv', + }, ); ###### METHODS sub _pref_changed { - my ( $self, $curr_val, $prev_val ) = @_; + my ( $self, $curr_val, $prev_val ) = @_; - if ( $prev_val and $curr_val ne $prev_val ) { - say "A preference changed to '$curr_val'."; - $self->store($self->filename); - } + if ( $prev_val and $curr_val ne $prev_val ) { + say "A preference changed to '$curr_val'."; + $self->store( $self->filename ); + } } __PACKAGE__->meta->make_immutable; From 9c2aab8f91963dc1815537b08cc917b0cc477802 Mon Sep 17 00:00:00 2001 From: vikin91 Date: Tue, 7 Mar 2017 15:27:20 +0100 Subject: [PATCH 05/21] fix mistake in copying preferences->filename --- lib/BibSpace/Util/Preferences.pm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/BibSpace/Util/Preferences.pm b/lib/BibSpace/Util/Preferences.pm index 4ebbeea..38cf8a0 100644 --- a/lib/BibSpace/Util/Preferences.pm +++ b/lib/BibSpace/Util/Preferences.pm @@ -31,7 +31,7 @@ sub load_maybe { my $prefs = Preferences->load( $self->filename ); # the filename should not be stored, so we need to copy it - $prefs->filename = $self->filename; + $prefs->filename($self->filename); return $prefs; } return $self; From c9c9eaf00c61faffa79114eb6fe6c054dd563015 Mon Sep 17 00:00:00 2001 From: vikin91 Date: Sun, 11 Jun 2017 15:26:04 +0200 Subject: [PATCH 06/21] provide env variables to set DB connection parameters --- cpanfile | 3 ++- lib/BibSpace.pm | 3 ++- lib/BibSpace/Controller/Helpers.pm | 23 ++++++++++++++--------- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/cpanfile b/cpanfile index b5664fe..3cbe3a8 100644 --- a/cpanfile +++ b/cpanfile @@ -1,9 +1,10 @@ requires 'Array::Utils' , '>= 0.0'; requires 'Apache::DBI' , '>= 0.0'; requires 'Crypt::Eksblowfish::Bcrypt' , '>= 0.0'; +requires 'Math::Pari' , '== 2.01080900'; requires 'Crypt::Random' , '>= 0.0'; requires 'Cwd' , '>= 0.0'; -requires 'DBD::mysql' , '>= 0.0'; +# requires 'DBD::mysql' , '>= 0.0'; requires 'DBI', '>= 1.619'; requires 'DBIx::Connector' , '>= 0.0'; requires 'Data::Dumper' , '>= 0.0'; diff --git a/lib/BibSpace.pm b/lib/BibSpace.pm index 2c6ed1b..cc83cee 100644 --- a/lib/BibSpace.pm +++ b/lib/BibSpace.pm @@ -179,7 +179,7 @@ has layeredRepository => sub { reset_data_callback => undef, is_read => 1 ); - $LR->add_layer($smartArrayLayer); + # $LR->add_layer($smartArrayLayer); if ( !$self->db ) { $self->logger->error( @@ -197,6 +197,7 @@ has layeredRepository => sub { handle => $self->db, reset_data_callback => \&reset_db_data, reset_data_callback_arguments => [ $self->db ], + is_read => 1 ); $LR->add_layer($mySQLLayer); } diff --git a/lib/BibSpace/Controller/Helpers.pm b/lib/BibSpace/Controller/Helpers.pm index 262a067..df81f00 100644 --- a/lib/BibSpace/Controller/Helpers.pm +++ b/lib/BibSpace/Controller/Helpers.pm @@ -60,15 +60,20 @@ sub register { } ); - $app->helper( - db => sub { - my $self = shift; - return db_connect( - $self->app->config->{db_host}, $self->app->config->{db_user}, - $self->app->config->{db_database}, $self->app->config->{db_pass} - ); - } - ); + $app->helper( + db => sub { + my $self = shift; + my $db_host + = $ENV{BIBSPACE_DB_HOST} || $self->app->config->{db_host}; + my $db_user + = $ENV{BIBSPACE_DB_USER} || $self->app->config->{db_user}; + my $db_database + = $ENV{BIBSPACE_DB_DATABASE} || $self->app->config->{db_database}; + my $db_pass + = $ENV{BIBSPACE_DB_PASS} || $self->app->config->{db_pass}; + return db_connect( $db_host, $db_user, $db_database, $db_pass ); + } + ); $app->helper( bst => sub { From 7de16dd1e1ae93b8942440f1f8ed17eeecbec71f Mon Sep 17 00:00:00 2001 From: vikin91 Date: Sun, 11 Jun 2017 15:27:05 +0200 Subject: [PATCH 07/21] enable smart layaer again --- lib/BibSpace.pm | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/BibSpace.pm b/lib/BibSpace.pm index cc83cee..2c6ed1b 100644 --- a/lib/BibSpace.pm +++ b/lib/BibSpace.pm @@ -179,7 +179,7 @@ has layeredRepository => sub { reset_data_callback => undef, is_read => 1 ); - # $LR->add_layer($smartArrayLayer); + $LR->add_layer($smartArrayLayer); if ( !$self->db ) { $self->logger->error( @@ -197,7 +197,6 @@ has layeredRepository => sub { handle => $self->db, reset_data_callback => \&reset_db_data, reset_data_callback_arguments => [ $self->db ], - is_read => 1 ); $LR->add_layer($mySQLLayer); } From ef6a3f1c262b5a456f764523497685bc5e9d583c Mon Sep 17 00:00:00 2001 From: Piotr Date: Mon, 7 Aug 2017 22:07:19 +0200 Subject: [PATCH 08/21] Refactor: tidy (#22) --- .travis.yml | 5 +- cpanfile | 5 +- lib/BibSpace.pm | 1227 +++++----- lib/BibSpace/Controller/Backup.pm | 251 +- lib/BibSpace/Controller/Persistence.pm | 163 +- lib/BibSpace/Controller/Preferences.pm | 3 - lib/BibSpace/Controller/Publications.pm | 2066 +++++++++-------- .../Controller/PublicationsExperimental.pm | 1 - .../Controller/PublicationsLanding.pm | 1 - lib/BibSpace/Functions/Core.pm | 475 ++-- lib/BibSpace/Model/Backup.pm | 234 +- lib/BibSpace/TestManager.pm | 5 +- lib/BibSpace/Util/Preferences.pm | 102 +- lib/BibSpace/Util/Statistics.pm | 89 +- t/000_prepare.t | 57 +- t/100-unit/Author.t | 8 - 16 files changed, 2407 insertions(+), 2285 deletions(-) diff --git a/.travis.yml b/.travis.yml index 5147752..13a5212 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,15 +33,14 @@ before_script: - mojo version - coverage-setup notifications: - slack: - secure: hwihdXrKIOilNPyIunNh5yqBwtgwj4SnheF8DlsmQI2TqpcyFWgjvspnvquiV7+OEBb5mFUSZeEUj5cgIgJ3VaBFIbcij0nq3TTHAvSmx+oTboAxUkg62zdPaaFV+iK9BpCNm6eGsjE1Wfp6LXzBHOZetCXJ1IgQ02mOknkW2ysztNhF4G/MMUXvDe5ApgTHshGYvkaD+StHcLt2c6Quy4DUkKeU6Djun6Dyp3foSBibBRsQK9q0yXo5ooYkoVllEaSvu94FpxBjsF+lBQJveM2Wc090EgM1An/Oc/ptMVvMmSE0mmM8RR9vM5dUZwmZHd/kECo1cvGQG1nVJZ9qWnrCeWcnJS7M9FkFucEBjFJTzh1FL3tgYKnXkS04uQLaj84byQr7f+l4l8ExsJPUriQia9829+OeiRblOdB3mEEMgHleMhE0trfre7iz5Beob5sPwTq9z7ywrRVGX8vKeDXPEmwDi+5gdYXRw3QwNP7sWAePOdpZPzUBc5fUYZh8kJB22b8zoF1T81gxME0MQOc4nCgSCETtLWih3R8sngooAXXo9kYM3zb38ijoLpPwlOmE0BR9rvcSz8BBp1bVhWZ4k4ploe5uTtb+Mg31Amj6d5I5z9/KwYJQ6Mcy3y8Ijozm7+FpDLfjz70ZjAYguurmHFZsDQJQp4fJUcOFabc= + # slack: + # secure: hwihdXrKIOilNPyIunNh5yqBwtgwj4SnheF8DlsmQI2TqpcyFWgjvspnvquiV7+OEBb5mFUSZeEUj5cgIgJ3VaBFIbcij0nq3TTHAvSmx+oTboAxUkg62zdPaaFV+iK9BpCNm6eGsjE1Wfp6LXzBHOZetCXJ1IgQ02mOknkW2ysztNhF4G/MMUXvDe5ApgTHshGYvkaD+StHcLt2c6Quy4DUkKeU6Djun6Dyp3foSBibBRsQK9q0yXo5ooYkoVllEaSvu94FpxBjsF+lBQJveM2Wc090EgM1An/Oc/ptMVvMmSE0mmM8RR9vM5dUZwmZHd/kECo1cvGQG1nVJZ9qWnrCeWcnJS7M9FkFucEBjFJTzh1FL3tgYKnXkS04uQLaj84byQr7f+l4l8ExsJPUriQia9829+OeiRblOdB3mEEMgHleMhE0trfre7iz5Beob5sPwTq9z7ywrRVGX8vKeDXPEmwDi+5gdYXRw3QwNP7sWAePOdpZPzUBc5fUYZh8kJB22b8zoF1T81gxME0MQOc4nCgSCETtLWih3R8sngooAXXo9kYM3zb38ijoLpPwlOmE0BR9rvcSz8BBp1bVhWZ4k4ploe5uTtb+Mg31Amj6d5I5z9/KwYJQ6Mcy3y8Ijozm7+FpDLfjz70ZjAYguurmHFZsDQJQp4fJUcOFabc= email: on_success: always on_failure: always script: - export BIBSPACE_CONFIG=lib/BibSpace/files/config/default.conf - export BIBSPACE_USE_DUMP=1 -# - PERL5OPT=-MDevel::Cover=-coverage,statement,branch,condition,path,subroutine bin/bibspace test - PERL5OPT=-MDevel::Cover=-coverage,statement,branch,condition,path,subroutine prove -lr after_success: - cover -report coveralls diff --git a/cpanfile b/cpanfile index 3cbe3a8..30d30bd 100644 --- a/cpanfile +++ b/cpanfile @@ -1,10 +1,9 @@ requires 'Array::Utils' , '>= 0.0'; requires 'Apache::DBI' , '>= 0.0'; requires 'Crypt::Eksblowfish::Bcrypt' , '>= 0.0'; -requires 'Math::Pari' , '== 2.01080900'; -requires 'Crypt::Random' , '>= 0.0'; +requires 'Bytes::Random' , '>= 0.0'; requires 'Cwd' , '>= 0.0'; -# requires 'DBD::mysql' , '>= 0.0'; +requires 'DBD::mysql' , '>= 0.0'; requires 'DBI', '>= 1.619'; requires 'DBIx::Connector' , '>= 0.0'; requires 'Data::Dumper' , '>= 0.0'; diff --git a/lib/BibSpace.pm b/lib/BibSpace.pm index 2c6ed1b..7323ea7 100644 --- a/lib/BibSpace.pm +++ b/lib/BibSpace.pm @@ -8,7 +8,7 @@ use BibSpace::Functions::MySqlBackupFunctions; use BibSpace::Functions::FDB; use BibSpace::Functions::FPublications; -# on test server: +# on test server: # BIBSPACE_CONFIG=/etc/bibspace_test.conf BIBSPACE_USE_DUMP=0 MOJO_MODE=development hypnotoad bin/bibspace use Mojo::Base 'Mojolicious'; @@ -16,6 +16,7 @@ use Mojo::Base 'Mojolicious::Plugin::Config'; use Data::Dumper; + # use File::Slurp; use POSIX qw/strftime/; use Try::Tiny; @@ -49,42 +50,48 @@ use BibSpace::Util::Preferences; use BibSpace::Util::EntityFactory; # STATE keyword -# state declares a lexically scoped variable, just like my. -# However, those variables will never be reinitialized, -# contrary to lexical variables that are reinitialized each time their enclosing block is entered. +# state declares a lexically scoped variable, just like my. +# However, those variables will never be reinitialized, +# contrary to lexical variables that are reinitialized each time their enclosing block is entered. # See Persistent Private Variables in perlsub for details. use feature qw( state say ); - ## OBJECTS CREATED AND CONTAINED IN HAS METHODS CANNOT BE CHANGED AT RUNTIME! + has preferences => sub { - my $self = shift; - return state $prefs = Preferences->new(filename => "".$self->app->home->rel_file("bibspace_preferences.json"))->load_maybe; + my $self = shift; + my $file + = $self->app->home->rel_file("bibspace_preferences.json"); + return state $prefs + = Preferences->new( filename => "" . $file )->load_maybe; }; has statistics => sub { - my $self = shift; - return state $stats = Statistics->new(filename => "".$self->app->home->rel_file("bibspace_stats.json"))->load_maybe; + my $self = shift; + my $file = $self->app->home->rel_file("bibspace_stats.json"); + say $file; + return state $stats = Statistics->new( filename => "" . $file )->load_maybe; }; has config_file => sub { - my $self = shift; - my $candidate; - $candidate = $ENV{BIBSPACE_CONFIG}; - return $candidate if defined $candidate and -e $candidate; + my $self = shift; + my $candidate; + $candidate = $ENV{BIBSPACE_CONFIG}; + return $candidate if defined $candidate and -e $candidate; - $candidate = $self->app->home->rel_file('/etc/bibspace.conf'); - return $candidate if -e $candidate; + $candidate = $self->app->home->rel_file('/etc/bibspace.conf'); + return $candidate if -e $candidate; - $candidate = $self->app->home->rel_file('lib/BibSpace/files/config/default.conf'); - return $candidate if -e $candidate; + $candidate + = $self->app->home->rel_file('lib/BibSpace/files/config/default.conf'); + return $candidate if -e $candidate; - $candidate = $self->app->home->rel_file('config/default.conf'); # for travis - return $candidate if -e $candidate; + $candidate = $self->app->home->rel_file('config/default.conf'); # for travis + return $candidate if -e $candidate; - die "Cannot find Bibspace config!"; - return; + die "Cannot find Bibspace config!"; + return; }; has get_backups_dir => sub { @@ -109,7 +116,7 @@ has get_log_dir => sub { }; # do not use this - if MySQL server dies during operation, you will be not able to reconnect! -# use helper +# use helper ## OBJECTS CREATED AND CONTAINED IN HAS METHODS CANNOT BE CHANGED AT RUNTIME! # has db => sub { # my $self = shift; @@ -120,681 +127,658 @@ has get_log_dir => sub { # }; has version => sub { - return $BibSpace::VERSION // "0.5.0"; + return $BibSpace::VERSION // "0.5.0"; }; has quick_load_fixture_filename => sub { - my $self = shift; - return $self->app->home->rel_file('bibspace.dat'); + my $self = shift; + return $self->app->home->rel_file('bibspace.dat'); }; # don't want to read data form DB and wait to link them every reload? # use quick_load_fixture! Useful for development and testing. # better disable it for production has use_quick_load_fixture => sub { - my $self = shift; - return if $self->mode eq 'production'; + my $self = shift; + return if $self->mode eq 'production'; - return 1 - if defined $ENV{BIBSPACE_USE_DUMP} and $ENV{BIBSPACE_USE_DUMP} == 1; - return; + return 1 + if defined $ENV{BIBSPACE_USE_DUMP} and $ENV{BIBSPACE_USE_DUMP} == 1; + return; }; -# please use only a single type of logger at once. +# please use only a single type of logger at once. # Using multiple may not be supported currently # if you really want to use multiple different loggers or state-full loggers (please don't), # then you need to move the object construction INTO the LayeredReposity and provide a helper to access it for everywhere. has logger => sub { state $logger = SimpleLogger->new() }; - has smartArrayBackend => sub { - my $self = shift; - return SmartArray->new( logger => $self->logger ); + my $self = shift; + return SmartArray->new( logger => $self->logger ); }; ## I moved this to helpers as app->attr for a while has layeredRepository => sub { - my $self = shift; - $self->app->logger->info("Building layeredRepository"); - - - my $LR = LayeredRepository->new( - logger => $self->logger, - preferences => $self->preferences, - # id_provider_class => 'DummyUidProvider', - id_provider_class => 'IntegerUidProvider', - ); + my $self = shift; + $self->app->logger->info("Building layeredRepository"); - my $smartArrayLayer = RepositoryLayer->new( - name => 'smart', - priority => 1, - creates_on_read => undef, - backendFactoryName => "SmartArrayDAOFactory", - logger => $self->logger, - handle => $self->smartArrayBackend, + + my $LR = LayeredRepository->new( + logger => $self->logger, + preferences => $self->preferences, + + # id_provider_class => 'DummyUidProvider', + id_provider_class => 'IntegerUidProvider', + ); + + my $smartArrayLayer = RepositoryLayer->new( + name => 'smart', + priority => 1, + creates_on_read => undef, + backendFactoryName => "SmartArrayDAOFactory", + logger => $self->logger, + handle => $self->smartArrayBackend, # reset_data_callback must be undef if you want to create and restore backups using Storable. - reset_data_callback => undef, - is_read => 1 + reset_data_callback => undef, + is_read => 1 + ); + $LR->add_layer($smartArrayLayer); + + if ( !$self->db ) { + $self->logger->error( + "You add SQL layer, but there is no connection to the database! Skipping this layer." + . " You need to start MySQL server and restart BibSpace to use this layer" ); - $LR->add_layer($smartArrayLayer); - - if ( !$self->db ) { - $self->logger->error( - "You add SQL layer, but there is no connection to the database! Skipping this layer." - . " You need to start MySQL server and restart BibSpace to use this layer" - ); - } - else { - my $mySQLLayer = RepositoryLayer->new( - name => 'mysql', - priority => 99, - creates_on_read => 1, - backendFactoryName => "MySQLDAOFactory", - logger => $self->logger, - handle => $self->db, - reset_data_callback => \&reset_db_data, - reset_data_callback_arguments => [ $self->db ], - ); - $LR->add_layer($mySQLLayer); - } - return $LR; + } + else { + my $mySQLLayer = RepositoryLayer->new( + name => 'mysql', + priority => 99, + creates_on_read => 1, + backendFactoryName => "MySQLDAOFactory", + logger => $self->logger, + handle => $self->db, + reset_data_callback => \&reset_db_data, + reset_data_callback_arguments => [ $self->db ], + ); + $LR->add_layer($mySQLLayer); + } + return $LR; }; # layeredRepository will not change at runtime => repo neither. has repo => sub { - my $self = shift; - return RepositoryFacade->new( lr => $self->layeredRepository ); + my $self = shift; + return RepositoryFacade->new( lr => $self->layeredRepository ); }; ################################################################ sub startup { - my $self = shift; - $self->app->logger->info("*** Starting BibSpace ***"); - + my $self = shift; + $self->app->logger->info("*** Starting BibSpace ***"); - $self->setup_config; - $self->setup_plugins; - $self->app->preferences->local_time_zone( DateTime::TimeZone->new( name => 'local' )->name ); + $self->setup_config; + $self->setup_plugins; - $self->setup_routes; - $self->setup_hooks; - $self->setup_repositories; - $self->insert_admin; + $self->app->preferences->local_time_zone( + DateTime::TimeZone->new( name => 'local' )->name ); + $self->setup_routes; + $self->setup_hooks; + $self->setup_repositories; + $self->insert_admin; - - $self->app->logger->info("Setup done."); + $self->app->logger->info("Setup done."); - $self->app->logger->info( "Using CONFIG: " . $self->app->config_file ); - $self->app->logger->info( "App home is: " . $self->app->home ); - $self->app->logger->info( "Active bst file is: " . $self->app->bst ); + $self->app->logger->info( "Using CONFIG: " . $self->app->config_file ); + $self->app->logger->info( "App home is: " . $self->app->home ); + $self->app->logger->info( "Active bst file is: " . $self->app->bst ); - ############################### - ########### SANDBOX ########### - ############################### - - # # cool! - # my $author = $self->app->entityFactory->new_Author( uid => "AabakusAston" ); - # say $author->uid; - # say $author->id; - # # say $author->preferences; + ############################### + ########### SANDBOX ########### + ############################### - # say Dumper $author; - # $self->app->repo->authors_save($author); +# # cool! +# my $author = $self->app->entityFactory->new_Author( uid => "AabakusAston" ); +# say $author->uid; +# say $author->id; +# # say $author->preferences; + # say Dumper $author; + # $self->app->repo->authors_save($author); - # my @users = $self->app->repo->users_all; - # $self->logger->warn("All users: ".@users); - # map {say $_->toString } @users; + # my @users = $self->app->repo->users_all; + # $self->logger->warn("All users: ".@users); + # map {say $_->toString } @users; - # my @users_to_delete = $self->app->repo->users_filter(sub{$_->email =~ /\@example.com/}); - # $self->logger->warn("To delete: ".@users_to_delete); - # map {say $_->toString } @users_to_delete; - # $self->app->repo->users_delete(@users_to_delete); +# my @users_to_delete = $self->app->repo->users_filter(sub{$_->email =~ /\@example.com/}); +# $self->logger->warn("To delete: ".@users_to_delete); +# map {say $_->toString } @users_to_delete; +# $self->app->repo->users_delete(@users_to_delete); - # $self->logger->info("this is info"); - # $self->logger->warn("this is warning"); - # $self->logger->error("this is error"); + # $self->logger->info("this is info"); + # $self->logger->warn("this is warning"); + # $self->logger->error("this is error"); } ################################################################ sub insert_admin { - my $self = shift; - $self->app->logger->info("Add startup admin user..."); - - my $admin_exists - = $self->app->repo->users_find( sub { $_->login eq 'pub_admin' } ); - if ( !$admin_exists ) { - my $salt = salt(); - my $hash = encrypt_password( 'asdf', $salt ); - my $new_user = $self->app->entityFactory->new_User( - login => 'pub_admin', - email => 'pub_admin@example.com', - real_name => 'Admin', - rank => 99, - pass => $hash, - pass2 => $salt - ); - $self->app->repo->users_save($new_user); - } - else { - # this email is used in tests! - $admin_exists->email('pub_admin@example.com'); - $admin_exists->make_admin; - $self->app->repo->users_update($admin_exists); - } + my $self = shift; + $self->app->logger->info("Add startup admin user..."); + + my $admin_exists + = $self->app->repo->users_find( sub { $_->login eq 'pub_admin' } ); + if ( !$admin_exists ) { + my $salt = salt(); + my $hash = encrypt_password( 'asdf', $salt ); + my $new_user = $self->app->entityFactory->new_User( + login => 'pub_admin', + email => 'pub_admin@example.com', + real_name => 'Admin', + rank => 99, + pass => $hash, + pass2 => $salt + ); + $self->app->repo->users_save($new_user); + } + else { + # this email is used in tests! + $admin_exists->email('pub_admin@example.com'); + $admin_exists->make_admin; + $self->app->repo->users_update($admin_exists); + } } ################################################################ sub setup_repositories { - my $self = shift; + my $self = shift; - $self->app->logger->info("Setup repositories..."); + $self->app->logger->info("Setup repositories..."); - if ( -e $self->quick_load_fixture_filename - and $self->use_quick_load_fixture ) - { + if ( -e $self->quick_load_fixture_filename + and $self->use_quick_load_fixture ) + { # $self->app->logger->info("Retrieving dump from '".$self->quick_load_fixture_filename."'."); - my $layer = retrieve( $self->quick_load_fixture_filename ); + my $layer = retrieve( $self->quick_load_fixture_filename ); - # reser read layer = not needed, layer empty by start of the app - # $self->app->logger->info("Replacing layer 'smart' with the dump."); - $self->repo->lr->replace_layer( 'smart', $layer ); + # reser read layer = not needed, layer empty by start of the app + # $self->app->logger->info("Replacing layer 'smart' with the dump."); + $self->repo->lr->replace_layer( 'smart', $layer ); # $self->app->logger->debug("State after replacement:".$self->repo->lr->get_summary_table); - } - else { - $self->app->logger->info( "We do not use dump file '" - . $self->quick_load_fixture_filename - . "'." ); - } + } + else { + $self->app->logger->info( "We do not use dump file '" + . $self->quick_load_fixture_filename + . "'." ); + } - # no data, no fun = no need to copy, link, and store - if ( $self->repo->entries_empty ) { - $self->app->logger->info("Repo has no entries. Reseting read_layer."); + # no data, no fun = no need to copy, link, and store + if ( $self->repo->entries_empty ) { + $self->app->logger->info("Repo has no entries. Reseting read_layer."); - $self->repo->lr->copy_data( { from => 'mysql', to => 'smart' } ); + $self->repo->lr->copy_data( { from => 'mysql', to => 'smart' } ); - # Entities and Relations in the smart layer must be linked! - $self->link_data; - - $self->app->logger->info( "Storing current state to dump file '" - . $self->quick_load_fixture_filename - . "'." ); + # Entities and Relations in the smart layer must be linked! + $self->link_data; - # store current state to file - store $self->repo->lr->get_read_layer, - $self->quick_load_fixture_filename; - } + $self->app->logger->info( "Storing current state to dump file '" + . $self->quick_load_fixture_filename + . "'." ); + + # store current state to file + store $self->repo->lr->get_read_layer, $self->quick_load_fixture_filename; + } } ################################################################ sub link_data { - my $self = shift; - $self->app->logger->info("Linking data..."); - - $self->app->logger->info("Linking Authors (N) to (1) Authors."); - foreach my $author ( - $self->repo->authors_filter( sub { $_->id != $_->master_id } ) ) - { - my $master - = $self->repo->authors_find( sub { $_->id == $author->master_id } - ); - if ( $master and $author ) { - $author->set_master($master); - } + my $self = shift; + $self->app->logger->info("Linking data..."); + + $self->app->logger->info("Linking Authors (N) to (1) Authors."); + foreach my $author ( + $self->repo->authors_filter( sub { $_->id != $_->master_id } ) ) + { + my $master + = $self->repo->authors_find( sub { $_->id == $author->master_id } ); + if ( $master and $author ) { + $author->set_master($master); } - - - $self->app->logger->info("Linking Authors (N) to (M) Entries."); - foreach my $auth ( $self->repo->authorships_all ) { - my $entry - = $self->repo->entries_find( sub { $_->id == $auth->entry_id } ); - my $author - = $self->repo->authors_find( sub { $_->id == $auth->author_id } ); - if ( $entry and $author ) { - $auth->entry($entry); - $auth->author($author); - $entry->authorships_add($auth); - $author->authorships_add($auth); - } + } + + + $self->app->logger->info("Linking Authors (N) to (M) Entries."); + foreach my $auth ( $self->repo->authorships_all ) { + my $entry + = $self->repo->entries_find( sub { $_->id == $auth->entry_id } ); + my $author + = $self->repo->authors_find( sub { $_->id == $auth->author_id } ); + if ( $entry and $author ) { + $auth->entry($entry); + $auth->author($author); + $entry->authorships_add($auth); + $author->authorships_add($auth); } - - $self->app->logger->info("Linking Tags (N) to (M) Entries."); - foreach my $labeling ( $self->repo->labelings_all ) { - my $entry - = $self->repo->entries_find( sub { $_->id == $labeling->entry_id } - ); - my $tag - = $self->repo->tags_find( sub { $_->id == $labeling->tag_id } ); - if ( $entry and $tag ) { - $labeling->entry($entry); - $labeling->tag($tag); - $entry->labelings_add($labeling); - $tag->labelings_add($labeling); - } + } + + $self->app->logger->info("Linking Tags (N) to (M) Entries."); + foreach my $labeling ( $self->repo->labelings_all ) { + my $entry + = $self->repo->entries_find( sub { $_->id == $labeling->entry_id } ); + my $tag = $self->repo->tags_find( sub { $_->id == $labeling->tag_id } ); + if ( $entry and $tag ) { + $labeling->entry($entry); + $labeling->tag($tag); + $entry->labelings_add($labeling); + $tag->labelings_add($labeling); } - - $self->app->logger->info( - "Linking Teams (Exceptions) (N) to (M) Entries."); - foreach my $exception ( $self->repo->exceptions_all ) { - my $entry = $self->repo->entries_find( - sub { $_->id == $exception->entry_id } ); - my $team - = $self->repo->teams_find( sub { $_->id == $exception->team_id } - ); - if ( $entry and $team ) { - $exception->entry($entry); - $exception->team($team); - $entry->exceptions_add($exception); - $team->exceptions_add($exception); - } + } + + $self->app->logger->info("Linking Teams (Exceptions) (N) to (M) Entries."); + foreach my $exception ( $self->repo->exceptions_all ) { + my $entry + = $self->repo->entries_find( sub { $_->id == $exception->entry_id } ); + my $team + = $self->repo->teams_find( sub { $_->id == $exception->team_id } ); + if ( $entry and $team ) { + $exception->entry($entry); + $exception->team($team); + $entry->exceptions_add($exception); + $team->exceptions_add($exception); } + } - $self->app->logger->info("Linking Teams (N) to (M) Authors."); - foreach my $membership ( $self->repo->memberships_all ) { - my $author = $self->repo->authors_find( - sub { $_->id == $membership->author_id } ); - my $team - = $self->repo->teams_find( sub { $_->id == $membership->team_id } - ); - if ( defined $author and defined $team ) { - $membership->author($author); - $membership->team($team); - $author->memberships_add($membership); - $team->memberships_add($membership); - } + $self->app->logger->info("Linking Teams (N) to (M) Authors."); + foreach my $membership ( $self->repo->memberships_all ) { + my $author + = $self->repo->authors_find( sub { $_->id == $membership->author_id } + ); + my $team + = $self->repo->teams_find( sub { $_->id == $membership->team_id } ); + if ( defined $author and defined $team ) { + $membership->author($author); + $membership->team($team); + $author->memberships_add($membership); + $team->memberships_add($membership); } + } - $self->app->logger->info("Linking TagTypes (N) to (1) Tags."); - foreach my $tag ( $self->repo->tags_all ) { - my $tagtype - = $self->repo->tagTypes_find( sub { $_->id == $tag->type } ); - if ( $tag and $tagtype ) { - $tag->tagtype($tagtype); - } + $self->app->logger->info("Linking TagTypes (N) to (1) Tags."); + foreach my $tag ( $self->repo->tags_all ) { + my $tagtype = $self->repo->tagTypes_find( sub { $_->id == $tag->type } ); + if ( $tag and $tagtype ) { + $tag->tagtype($tagtype); } + } - $self->app->logger->info("TODO: Linking OurTypes (N) to (1) Entries."); + $self->app->logger->info("TODO: Linking OurTypes (N) to (1) Entries."); - $self->app->logger->info("Linking Finished."); + $self->app->logger->info("Linking Finished."); } ################################################################ sub setup_config { - my $self = shift; - my $app = $self; - $self->app->logger->info("Setup config..."); - $self->plugin( 'Config' => { file => $self->app->config_file } ); - - $ENV{MOJO_MAX_MESSAGE_SIZE} = 40 * 1024 * 1024; - $self->app->logger->info( "Setting max upload size to " - . $ENV{MOJO_MAX_MESSAGE_SIZE} - . " Bytes." ); + my $self = shift; + my $app = $self; + $self->app->logger->info("Setup config..."); + $self->plugin( 'Config' => { file => $self->app->config_file } ); + + $ENV{MOJO_MAX_MESSAGE_SIZE} = 40 * 1024 * 1024; + $self->app->logger->info( + "Setting max upload size to " . $ENV{MOJO_MAX_MESSAGE_SIZE} . " Bytes." ); } ################################################################ sub setup_plugins { - my $self = shift; - $self->app->logger->info("Setup plugins..."); - - $ENV{MOJO_REVERSE_PROXY} = 1; - - $self->app->plugin('InstallablePaths'); - $self->app->plugin('RenderFile'); - $self->plugin('BibSpace::Controller::Helpers'); - - push @{ $self->app->static->paths }, $self->app->home->rel_file('public'); - - # push @{$self->app->static->paths}, $self->config->{backups_dir}; - - $self->app->logger->info("App version: " . $self->app->version); - $self->app->logger->info("Creating directories..."); - for my $dir ( ( $self->app->get_backups_dir, - $self->app->get_upload_dir, - $self->app->get_upload_dir."papers", - $self->app->get_upload_dir."slides", - $self->app->get_log_dir) ) - { - $self->app->logger->debug("Creating directory: $dir"); - try { - Path::Tiny->new($dir)->mkpath; - } - catch { - $self->app->logger->error("Exception: cannot create directory $dir. Msg: $_"); - }; + my $self = shift; + $self->app->logger->info("Setup plugins..."); + + $ENV{MOJO_REVERSE_PROXY} = 1; + + $self->app->plugin('InstallablePaths'); + $self->app->plugin('RenderFile'); + $self->plugin('BibSpace::Controller::Helpers'); + + push @{ $self->app->static->paths }, $self->app->home->rel_file('public'); + + # push @{$self->app->static->paths}, $self->config->{backups_dir}; + + $self->app->logger->info( "App version: " . $self->app->version ); + $self->app->logger->info("Creating directories..."); + for my $dir ( + ( $self->app->get_backups_dir, + $self->app->get_upload_dir, + $self->app->get_upload_dir . "papers", + $self->app->get_upload_dir . "slides", + $self->app->get_log_dir + ) + ) + { + $self->app->logger->debug("Creating directory: $dir"); + try { + Path::Tiny->new($dir)->mkpath; } + catch { + $self->app->logger->error( + "Exception: cannot create directory $dir. Msg: $_"); + }; + } - # set logging dir in the logger - $self->app->logger->debug("Setting log dir to the logger"); - $self->logger->set_log_dir("".Path::Tiny->new($self->get_log_dir) ); + # set logging dir in the logger + $self->app->logger->debug("Setting log dir to the logger"); + $self->logger->set_log_dir( "" . Path::Tiny->new( $self->get_log_dir ) ); + # this is supposed to trigger connection to the DB + $self->app->db; - # this is supposed to trigger connection to the DB - $self->app->db; - - - $self->secrets( [ $self->config->{key_cookie} ] ); - - $self->helper( proxy_prefix => sub { $self->config->{proxy_prefix} } ); + $self->secrets( [ $self->config->{key_cookie} ] ); + $self->helper( proxy_prefix => sub { $self->config->{proxy_prefix} } ); } ################################################################ ################################################################ sub setup_routes { - my $self = shift; - $self->app->logger->info("Setup routes..."); - - my $anyone = $self->routes; - $anyone->get('/')->to('display#index')->name('start'); - - - $anyone->get('/forgot')->to('login#forgot'); - $anyone->post('/forgot/gen')->to('login#post_gen_forgot_token'); - $anyone->get('/forgot/reset/:token')->to('login#token_clicked') - ->name("token_clicked"); - $anyone->post('/forgot/store')->to('login#store_password'); - - $anyone->get('/login_form')->to('login#login_form')->name('login_form'); - $anyone->post('/do_login')->to('login#login')->name('do_login'); - $anyone->get('/youneedtologin')->to('login#not_logged_in') - ->name('youneedtologin'); - $anyone->get('/badpassword')->to('login#bad_password') - ->name('badpassword'); - - $anyone->get('/logout')->to('login#logout')->name('logout'); - - $anyone->any('/test/500')->to('display#test500')->name('error500'); - $anyone->any('/test/404')->to('display#test404')->name('error404'); - - $anyone->get('/register')->to('login#register')->name('register'); - $anyone->post('/register')->to('login#post_do_register') - ->name('post_do_register'); - $anyone->any('/noregister')->to('login#register_disabled'); - - my $logged_user = $anyone->under->to('login#check_is_logged_in'); - my $manager_user - = $logged_user->under->to('login#under_check_is_manager'); - my $admin_user = $logged_user->under->to('login#under_check_is_admin'); - - ################ PREFERENCES ################ - - $manager_user->get('/preferences')->to('preferences#index')->name('preferences'); - $admin_user->post('/preferences')->to('preferences#save')->name('save_preferences'); - - ################ EXPERIMENTAL / PERSISTENCE ################ - - $anyone->get('/system_status')->to('persistence#system_status') - ->name('system_status'); - $admin_user->get('/persistence/load')->to('persistence#load_fixture') - ->name('load_fixture'); - $admin_user->get('/persistence/save')->to('persistence#save_fixture') - ->name('save_fixture'); - $admin_user->get('/persistence/copy_mysql_to_smart') - ->to('persistence#copy_mysql_to_smart')->name('copy_mysql_to_smart'); - $admin_user->get('/persistence/copy_smart_to_mysql') - ->to('persistence#copy_smart_to_mysql')->name('copy_smart_to_mysql'); - - $admin_user->get('/persistence/persistence_status') - ->to('persistence#persistence_status')->name('persistence_status'); - $admin_user->get('/persistence/persistence_status_ajax') - ->to('persistence#persistence_status_ajax')->name('persistence_status_ajax'); - - - $admin_user->get('/persistence/reset_mysql') - ->to('persistence#reset_mysql')->name('reset_mysql'); - $admin_user->get('/persistence/reset_smart') - ->to('persistence#reset_smart')->name('reset_smart'); - $admin_user->get('/persistence/reset_all') - ->to('persistence#reset_all')->name('reset_all'); - - $admin_user->get('/persistence/insert_random_data') - ->to('persistence#insert_random_data')->name('insert_random_data'); - - - - ################ SETTINGS ################ - $logged_user->get('/profile')->to('login#profile'); - $admin_user->get('/manage_users')->to('login#manage_users') - ->name('manage_users'); - $admin_user->get('/profile/:id')->to('login#foreign_profile') - ->name('show_user_profile'); - $admin_user->get('/profile/delete/:id')->to('login#delete_user') - ->name('delete_user'); - - $admin_user->get('/profile/make_user/:id')->to('login#make_user') - ->name('make_user'); - $admin_user->get('/profile/make_manager/:id')->to('login#make_manager') - ->name('make_manager'); - $admin_user->get('/profile/make_admin/:id')->to('login#make_admin') - ->name('make_admin'); - - $manager_user->get('/log')->to('display#show_log')->name('show_log'); - $manager_user->get('/statistics')->to('display#show_stats')->name('show_stats'); - # websocket for fun - $manager_user->websocket('/log_websocket/:num')->to('display#show_log_ws')->name('show_log_websocket'); - $manager_user->websocket('/statistics/:num')->to('display#show_stats_websocket')->name('show_stats_websocket'); - - - + my $self = shift; + $self->app->logger->info("Setup routes..."); + + my $anyone = $self->routes; + $anyone->get('/')->to('display#index')->name('start'); + + + $anyone->get('/forgot')->to('login#forgot'); + $anyone->post('/forgot/gen')->to('login#post_gen_forgot_token'); + $anyone->get('/forgot/reset/:token')->to('login#token_clicked') + ->name("token_clicked"); + $anyone->post('/forgot/store')->to('login#store_password'); + + $anyone->get('/login_form')->to('login#login_form')->name('login_form'); + $anyone->post('/do_login')->to('login#login')->name('do_login'); + $anyone->get('/youneedtologin')->to('login#not_logged_in') + ->name('youneedtologin'); + $anyone->get('/badpassword')->to('login#bad_password')->name('badpassword'); + + $anyone->get('/logout')->to('login#logout')->name('logout'); - $admin_user->get('/settings/fix_months') - ->to('publications#fixMonths') - ->name('fix_all_months'); - - $manager_user->get('/settings/clean_all') - ->to('publications#clean_ugly_bibtex') - ->name('clean_ugly_bibtex'); - - $manager_user->get('/settings/mark_all_to_regenerate') - ->to('publications#mark_all_to_regenerate') - ->name('mark_all_to_regenerate'); - - $manager_user->get('/settings/mark_author_to_regenerate/:author_id') - ->to('publications#mark_author_to_regenerate') - ->name('mark_author_to_regenerate'); - - $manager_user->get('/settings/regenerate_all') - ->to('publications#regenerate_html_for_all') - ->name('regenerate_html_for_all'); - - $logged_user->get('/settings/regenerate_html_in_chunk/:chunk_size') - ->to('publications#regenerate_html_in_chunk') - ->name('regenerate_html_in_chunk'); - - - - - - - $manager_user->get('/backups') - ->to('backup#index')->name('backup_index'); - $manager_user->put('/backups') - ->to('backup#save')->name('backup_do'); - $manager_user->put('/backups/mysql') - ->to('backup#save_mysql')->name('backup_do_mysql'); - $manager_user->get('/backups/:id') - ->to('backup#backup_download')->name('backup_download'); - - $admin_user->delete('/backups/:id') - ->to('backup#delete_backup')->name('backup_delete'); - $admin_user->put('/backups/:id') - ->to('backup#restore_backup')->name('backup_restore'); - $admin_user->delete('/backups') - ->to('backup#cleanup')->name('backup_cleanup'); - - - ################ TYPES ################ - $logged_user->get('/types')->to('types#all_our')->name('all_types'); - $manager_user->get('/types/add')->to('types#add_type') - ->name('add_type_get'); - $manager_user->post('/types/add')->to('types#post_add_type') - ->name('add_type_post'); - $manager_user->get('/types/manage/:name')->to('types#manage') - ->name('edit_type'); - $manager_user->get('/types/delete/:name')->to('types#delete_type') - ->name('delete_type'); - - $manager_user->post('/types/store_description') - ->to('types#post_store_description')->name('update_type_description'); - $manager_user->get('/types/toggle/:name')->to('types#toggle_landing') - ->name('toggle_landing_type'); - - $manager_user->get('/types/:our_type/map/:bibtex_type') - ->to('types#map_types'); - $manager_user->get('/types/:our_type/unmap/:bibtex_type') - ->to('types#unmap_types')->name('unmap_bibtex_type'); - - ################ AUTHORS ################ - - $logged_user->get('/authors/')->to('authors#all_authors') - ->name('all_authors'); - $manager_user->get('/authors/add')->to('authors#add_author') - ->name('add_author'); - $manager_user->post('/authors/add/')->to('authors#add_post'); - - $logged_user->get('/authors/edit/:id')->to('authors#edit_author') - ->name('edit_author'); - $manager_user->post('/authors/edit/')->to('authors#edit_post') - ->name('edit_author_post'); - $manager_user->get('/authors/delete/:id')->to('authors#delete_author') - ->name('delete_author'); - - - $admin_user->get('/authors/delete/:id/force') - ->to('authors#delete_author_force'); - - - - # for dev only!! - $admin_user->get('/authors/decimate') - ->to('authors#delete_invisible_authors'); - - - - - - $manager_user->post('/authors/edit_membership_dates') - ->to('authors#post_edit_membership_dates') - ->name('edit_author_membership_dates'); - - $manager_user->get('/authors/:id/add_to_team/:tid') - ->to('authors#add_to_team')->name('add_author_to_team'); - $manager_user->get('/authors/:id/remove_from_team/:tid') - ->to('authors#remove_from_team')->name('remove_author_from_team'); - $manager_user->get('/authors/:masterid/remove_uid/:uid') - ->to('authors#remove_uid')->name('remove_author_uid'); - - $manager_user->post('/authors/merge/')->to('authors#merge_authors') - ->name('merge_authors'); - - $admin_user->get('/authors/fix_masters') - ->to('authors#fix_masters')->name('fix_masters'); - - $manager_user->get('/authors/reassign') - ->to('authors#reassign_authors_to_entries'); - $admin_user->get('/authors/reassign_and_create') - ->to('authors#reassign_authors_to_entries_and_create_authors'); - - $manager_user->get('/authors/toggle_visibility/:id') - ->to('authors#toggle_visibility')->name('toggle_author_visibility'); - - # $logged_user->get('/authors/toggle_visibility') - # ->to('authors#toggle_visibility'); - - ################ TAG TYPES ################ - # $logged_user->get('/tags/')->to('tags#index')->name("tags_index"); - $logged_user->get('/tagtypes')->to('tagtypes#index') - ->name('all_tag_types'); - $admin_user->get('/tagtypes/add')->to('tagtypes#add') - ->name('add_tag_type'); - $admin_user->post('/tagtypes/add')->to('tagtypes#add_post') - ->name('add_tag_type_post'); - $admin_user->get('/tagtypes/delete/:id')->to('tagtypes#delete') - ->name('delete_tag_type'); - $manager_user->any('/tagtypes/edit/:id')->to('tagtypes#edit') - ->name('edit_tag_type'); - - ################ TAGS ################ - $logged_user->get('/tags/:type')->to( 'tags#index', type => 1 ) - ->name('all_tags'); - $admin_user->get('/tags/add/:type')->to( 'tags#add', type => 1 ) - ->name('add_tag_get'); - $admin_user->post('/tags/add/:type')->to( 'tags#add_post', type => 1 ) - ->name('add_tag_post'); - $logged_user->get('/tags/authors/:id/:type') - ->to( 'tags#get_authors_for_tag', type => 1 ) - ->name('get_authors_for_tag'); - $admin_user->get('/tags/delete/:id')->to('tags#delete') - ->name('delete_tag'); - - ### EDIT TAG FORM GOES WITH GET - WTF!?! - # FIXME: FIX THIS - $manager_user->get('/tags/edit/:id')->to('tags#edit')->name('edit_tag'); - - $anyone->get('/read/authors-for-tag/:tag_id/:team_id') - ->to('tags#get_authors_for_tag_and_team') - ->name('get_authors_for_tag_and_team'); - - #ALIAS - $anyone->get('/r/a4t/:tag_id/:team_id') - ->to('tags#get_authors_for_tag_and_team') - ->name('get_authors_for_tag_and_team'); - - $anyone->get('/read/authors-for-tag/:tag_id/:team_id') - ->to('tags#get_authors_for_tag_and_team') - ->name('get_authors_for_tag_and_team'); - - #ALIAS - $anyone->get('/r/a4t/:tag_id/:team_id') - ->to('tags#get_authors_for_tag_and_team') - ->name('get_authors_for_tag_and_team'); - - $anyone->get('/read/tags-for-author/:author_id') - ->to('tags#get_tags_for_author_read')->name('tags_for_author'); - - #ALIAS - $anyone->get('/r/t4a/:author_id')->to('tags#get_tags_for_author_read'); - - $anyone->get('/read/tags-for-team/:team_id') - ->to('tags#get_tags_for_team_read')->name('tags_for_team'); - - #ALIAS - $anyone->get('/r/t4t/:team_id')->to('tags#get_tags_for_team_read'); - - ################ TEAMS ################ - $logged_user->get('/teams')->to('teams#show')->name('all_teams'); - $logged_user->get('/teams/members/:teamid')->to('teams#team_members'); - - $manager_user->get('/teams/edit/:id')->to('teams#edit') - ->name('edit_team'); - $manager_user->get('/teams/delete/:id')->to('teams#delete_team') - ->name('delete_team'); - $manager_user->get('/teams/delete/:id/force') - ->to('teams#delete_team_force')->name('delete_team_force'); - $logged_user->get('/teams/unrealted_papers/:teamid') - ->to('publications#show_unrelated_to_team') - ->name('unrelated_papers_for_team'); - - $manager_user->get('/teams/add')->to('teams#add_team') - ->name('add_team_get'); - $manager_user->post('/teams/add/')->to('teams#add_team_post'); - - ################ EDITING PUBLICATIONS ################ + $anyone->any('/test/500')->to('display#test500')->name('error500'); + $anyone->any('/test/404')->to('display#test404')->name('error404'); + + $anyone->get('/register')->to('login#register')->name('register'); + $anyone->post('/register')->to('login#post_do_register') + ->name('post_do_register'); + $anyone->any('/noregister')->to('login#register_disabled'); + + my $logged_user = $anyone->under->to('login#check_is_logged_in'); + my $manager_user = $logged_user->under->to('login#under_check_is_manager'); + my $admin_user = $logged_user->under->to('login#under_check_is_admin'); + + ################ PREFERENCES ################ + + $manager_user->get('/preferences')->to('preferences#index') + ->name('preferences'); + $admin_user->post('/preferences')->to('preferences#save') + ->name('save_preferences'); + + ################ EXPERIMENTAL / PERSISTENCE ################ + + $anyone->get('/system_status')->to('persistence#system_status') + ->name('system_status'); + $admin_user->get('/persistence/load')->to('persistence#load_fixture') + ->name('load_fixture'); + $admin_user->get('/persistence/save')->to('persistence#save_fixture') + ->name('save_fixture'); + $admin_user->get('/persistence/copy_mysql_to_smart') + ->to('persistence#copy_mysql_to_smart')->name('copy_mysql_to_smart'); + $admin_user->get('/persistence/copy_smart_to_mysql') + ->to('persistence#copy_smart_to_mysql')->name('copy_smart_to_mysql'); + + $admin_user->get('/persistence/persistence_status') + ->to('persistence#persistence_status')->name('persistence_status'); + $admin_user->get('/persistence/persistence_status_ajax') + ->to('persistence#persistence_status_ajax') + ->name('persistence_status_ajax'); + + + $admin_user->get('/persistence/reset_mysql')->to('persistence#reset_mysql') + ->name('reset_mysql'); + $admin_user->get('/persistence/reset_smart')->to('persistence#reset_smart') + ->name('reset_smart'); + $admin_user->get('/persistence/reset_all')->to('persistence#reset_all') + ->name('reset_all'); + + $admin_user->get('/persistence/insert_random_data') + ->to('persistence#insert_random_data')->name('insert_random_data'); + + + ################ SETTINGS ################ + $logged_user->get('/profile')->to('login#profile'); + $admin_user->get('/manage_users')->to('login#manage_users') + ->name('manage_users'); + $admin_user->get('/profile/:id')->to('login#foreign_profile') + ->name('show_user_profile'); + $admin_user->get('/profile/delete/:id')->to('login#delete_user') + ->name('delete_user'); + + $admin_user->get('/profile/make_user/:id')->to('login#make_user') + ->name('make_user'); + $admin_user->get('/profile/make_manager/:id')->to('login#make_manager') + ->name('make_manager'); + $admin_user->get('/profile/make_admin/:id')->to('login#make_admin') + ->name('make_admin'); + + $manager_user->get('/log')->to('display#show_log')->name('show_log'); + $manager_user->get('/statistics')->to('display#show_stats') + ->name('show_stats'); + + # websocket for fun + $manager_user->websocket('/log_websocket/:num')->to('display#show_log_ws') + ->name('show_log_websocket'); + $manager_user->websocket('/statistics/:num') + ->to('display#show_stats_websocket')->name('show_stats_websocket'); + + + $admin_user->get('/settings/fix_months')->to('publications#fixMonths') + ->name('fix_all_months'); + + $manager_user->get('/settings/clean_all') + ->to('publications#clean_ugly_bibtex')->name('clean_ugly_bibtex'); + + $manager_user->get('/settings/mark_all_to_regenerate') + ->to('publications#mark_all_to_regenerate') + ->name('mark_all_to_regenerate'); + + $manager_user->get('/settings/mark_author_to_regenerate/:author_id') + ->to('publications#mark_author_to_regenerate') + ->name('mark_author_to_regenerate'); + + $manager_user->get('/settings/regenerate_all') + ->to('publications#regenerate_html_for_all') + ->name('regenerate_html_for_all'); + + $logged_user->get('/settings/regenerate_html_in_chunk/:chunk_size') + ->to('publications#regenerate_html_in_chunk') + ->name('regenerate_html_in_chunk'); + + + $manager_user->get('/backups')->to('backup#index')->name('backup_index'); + $manager_user->put('/backups')->to('backup#save')->name('backup_do'); + $manager_user->put('/backups/mysql')->to('backup#save_mysql') + ->name('backup_do_mysql'); + $manager_user->get('/backups/:id')->to('backup#backup_download') + ->name('backup_download'); + + $admin_user->delete('/backups/:id')->to('backup#delete_backup') + ->name('backup_delete'); + $admin_user->put('/backups/:id')->to('backup#restore_backup') + ->name('backup_restore'); + $admin_user->delete('/backups')->to('backup#cleanup') + ->name('backup_cleanup'); + + + ################ TYPES ################ + $logged_user->get('/types')->to('types#all_our')->name('all_types'); + $manager_user->get('/types/add')->to('types#add_type') + ->name('add_type_get'); + $manager_user->post('/types/add')->to('types#post_add_type') + ->name('add_type_post'); + $manager_user->get('/types/manage/:name')->to('types#manage') + ->name('edit_type'); + $manager_user->get('/types/delete/:name')->to('types#delete_type') + ->name('delete_type'); + + $manager_user->post('/types/store_description') + ->to('types#post_store_description')->name('update_type_description'); + $manager_user->get('/types/toggle/:name')->to('types#toggle_landing') + ->name('toggle_landing_type'); + + $manager_user->get('/types/:our_type/map/:bibtex_type') + ->to('types#map_types'); + $manager_user->get('/types/:our_type/unmap/:bibtex_type') + ->to('types#unmap_types')->name('unmap_bibtex_type'); + + ################ AUTHORS ################ + + $logged_user->get('/authors/')->to('authors#all_authors') + ->name('all_authors'); + $manager_user->get('/authors/add')->to('authors#add_author') + ->name('add_author'); + $manager_user->post('/authors/add/')->to('authors#add_post'); + + $logged_user->get('/authors/edit/:id')->to('authors#edit_author') + ->name('edit_author'); + $manager_user->post('/authors/edit/')->to('authors#edit_post') + ->name('edit_author_post'); + $manager_user->get('/authors/delete/:id')->to('authors#delete_author') + ->name('delete_author'); + + + $admin_user->get('/authors/delete/:id/force') + ->to('authors#delete_author_force'); + + + # for dev only!! + $admin_user->get('/authors/decimate') + ->to('authors#delete_invisible_authors'); + + + $manager_user->post('/authors/edit_membership_dates') + ->to('authors#post_edit_membership_dates') + ->name('edit_author_membership_dates'); + + $manager_user->get('/authors/:id/add_to_team/:tid') + ->to('authors#add_to_team')->name('add_author_to_team'); + $manager_user->get('/authors/:id/remove_from_team/:tid') + ->to('authors#remove_from_team')->name('remove_author_from_team'); + $manager_user->get('/authors/:masterid/remove_uid/:uid') + ->to('authors#remove_uid')->name('remove_author_uid'); + + $manager_user->post('/authors/merge/')->to('authors#merge_authors') + ->name('merge_authors'); + + $admin_user->get('/authors/fix_masters')->to('authors#fix_masters') + ->name('fix_masters'); + + $manager_user->get('/authors/reassign') + ->to('authors#reassign_authors_to_entries'); + $admin_user->get('/authors/reassign_and_create') + ->to('authors#reassign_authors_to_entries_and_create_authors'); + + $manager_user->get('/authors/toggle_visibility/:id') + ->to('authors#toggle_visibility')->name('toggle_author_visibility'); + + # $logged_user->get('/authors/toggle_visibility') + # ->to('authors#toggle_visibility'); + + ################ TAG TYPES ################ + # $logged_user->get('/tags/')->to('tags#index')->name("tags_index"); + $logged_user->get('/tagtypes')->to('tagtypes#index')->name('all_tag_types'); + $admin_user->get('/tagtypes/add')->to('tagtypes#add')->name('add_tag_type'); + $admin_user->post('/tagtypes/add')->to('tagtypes#add_post') + ->name('add_tag_type_post'); + $admin_user->get('/tagtypes/delete/:id')->to('tagtypes#delete') + ->name('delete_tag_type'); + $manager_user->any('/tagtypes/edit/:id')->to('tagtypes#edit') + ->name('edit_tag_type'); + + ################ TAGS ################ + $logged_user->get('/tags/:type')->to( 'tags#index', type => 1 ) + ->name('all_tags'); + $admin_user->get('/tags/add/:type')->to( 'tags#add', type => 1 ) + ->name('add_tag_get'); + $admin_user->post('/tags/add/:type')->to( 'tags#add_post', type => 1 ) + ->name('add_tag_post'); + $logged_user->get('/tags/authors/:id/:type') + ->to( 'tags#get_authors_for_tag', type => 1 ) + ->name('get_authors_for_tag'); + $admin_user->get('/tags/delete/:id')->to('tags#delete')->name('delete_tag'); + + ### EDIT TAG FORM GOES WITH GET - WTF!?! + # FIXME: FIX THIS + $manager_user->get('/tags/edit/:id')->to('tags#edit')->name('edit_tag'); + + $anyone->get('/read/authors-for-tag/:tag_id/:team_id') + ->to('tags#get_authors_for_tag_and_team') + ->name('get_authors_for_tag_and_team'); + + #ALIAS + $anyone->get('/r/a4t/:tag_id/:team_id') + ->to('tags#get_authors_for_tag_and_team') + ->name('get_authors_for_tag_and_team'); + + $anyone->get('/read/authors-for-tag/:tag_id/:team_id') + ->to('tags#get_authors_for_tag_and_team') + ->name('get_authors_for_tag_and_team'); + + #ALIAS + $anyone->get('/r/a4t/:tag_id/:team_id') + ->to('tags#get_authors_for_tag_and_team') + ->name('get_authors_for_tag_and_team'); + + $anyone->get('/read/tags-for-author/:author_id') + ->to('tags#get_tags_for_author_read')->name('tags_for_author'); + + #ALIAS + $anyone->get('/r/t4a/:author_id')->to('tags#get_tags_for_author_read'); + + $anyone->get('/read/tags-for-team/:team_id') + ->to('tags#get_tags_for_team_read')->name('tags_for_team'); + + #ALIAS + $anyone->get('/r/t4t/:team_id')->to('tags#get_tags_for_team_read'); + + ################ TEAMS ################ + $logged_user->get('/teams')->to('teams#show')->name('all_teams'); + $logged_user->get('/teams/members/:teamid')->to('teams#team_members'); + + $manager_user->get('/teams/edit/:id')->to('teams#edit')->name('edit_team'); + $manager_user->get('/teams/delete/:id')->to('teams#delete_team') + ->name('delete_team'); + $manager_user->get('/teams/delete/:id/force') + ->to('teams#delete_team_force')->name('delete_team_force'); + $logged_user->get('/teams/unrealted_papers/:teamid') + ->to('publications#show_unrelated_to_team') + ->name('unrelated_papers_for_team'); + + $manager_user->get('/teams/add')->to('teams#add_team') + ->name('add_team_get'); + $manager_user->post('/teams/add/')->to('teams#add_team_post'); + + ################ EDITING PUBLICATIONS ################ #<<< no perltidy here # EXPERIMENTAL @@ -1017,39 +1001,38 @@ sub setup_routes { ################################################################ sub setup_hooks { - my $self = shift; - $self->app->logger->info("Setup hooks..."); - - # $self->hook(after_render => sub { - # my ($c, $args) = @_; - # $c->push_url_history; - # # say "History of visisited URLS ".$c->get_url_history.":\n".join("\n", $c->get_url_history); - # }); - - $self->hook( - before_dispatch => sub { - my $c = shift; - - if( $c->req->headers->header('X-Forwarded-HTTPS') ){ - $c->req->url->base->scheme('https'); - } - $c->app->statistics->log_url($c->req->url); - - - # dirty fix for production deployment in a directory - # config->{proxy_prefix} stores the proxy prefix, e.g., /app - my $proxy_prefix = $self->config->{proxy_prefix}; - if ( $proxy_prefix ne "" ) { - - # we remove the leading slash - $proxy_prefix =~ s!^/!!; - - # and let Mojolicious add it again - push @{ $c->req->url->base->path->trailing_slash(1) }, - $proxy_prefix; - } - } - ); + my $self = shift; + $self->app->logger->info("Setup hooks..."); + +# $self->hook(after_render => sub { +# my ($c, $args) = @_; +# $c->push_url_history; +# # say "History of visisited URLS ".$c->get_url_history.":\n".join("\n", $c->get_url_history); +# }); + + $self->hook( + before_dispatch => sub { + my $c = shift; + + if ( $c->req->headers->header('X-Forwarded-HTTPS') ) { + $c->req->url->base->scheme('https'); + } + $c->app->statistics->log_url( $c->req->url ); + + + # dirty fix for production deployment in a directory + # config->{proxy_prefix} stores the proxy prefix, e.g., /app + my $proxy_prefix = $self->config->{proxy_prefix}; + if ( $proxy_prefix ne "" ) { + + # we remove the leading slash + $proxy_prefix =~ s!^/!!; + + # and let Mojolicious add it again + push @{ $c->req->url->base->path->trailing_slash(1) }, $proxy_prefix; + } + } + ); } 1; diff --git a/lib/BibSpace/Controller/Backup.pm b/lib/BibSpace/Controller/Backup.pm index 81abbeb..e8881d9 100644 --- a/lib/BibSpace/Controller/Backup.pm +++ b/lib/BibSpace/Controller/Backup.pm @@ -4,9 +4,10 @@ use Data::Dumper; use utf8; use Text::BibTeX; # parsing bib files use DateTime; + # use File::Slurp; -use v5.16; +use v5.16; use Try::Tiny; use strict; use warnings; @@ -18,6 +19,7 @@ use List::Util qw(first); use BibSpace::Functions::Core; use BibSpace::Functions::MySqlBackupFunctions; use BibSpace::Functions::BackupFunctions; + # use BibSpace::Functions::FDB; use BibSpace::Model::Backup; @@ -28,154 +30,189 @@ use Mojo::Base 'Mojolicious::Plugin::Config'; use Mojo::Log; - - #################################################################################### sub index { - my $self = shift; - my $dbh = $self->app->db; - - my $backup_dir = $self->app->get_backups_dir; - my $dir_size = get_dir_size($backup_dir); - $dir_size = $dir_size >> 20; - - my @backups_arr = sort {$b->date cmp $a->date} read_backups($backup_dir); - - foreach my $backup (@backups_arr){ - if( $backup->get_age->days >= $self->app->config->{allow_delete_backups_older_than}){ - $backup->allow_delete(1); - } - else{ - $backup->allow_delete(undef); - } + my $self = shift; + my $dbh = $self->app->db; + + my $backup_dir = $self->app->get_backups_dir; + my $dir_size = get_dir_size($backup_dir); + $dir_size = $dir_size >> 20; + + my @backups_arr = sort { $b->date cmp $a->date } read_backups($backup_dir); + + foreach my $backup (@backups_arr) { + if ( $backup->get_age->days + >= $self->app->config->{allow_delete_backups_older_than} ) + { + $backup->allow_delete(1); } + else { + $backup->allow_delete(undef); + } + } - $self->stash( - backups_arr => \@backups_arr, - dir_size => $dir_size - ); - $self->render( template => 'backup/backup' ); + $self->stash( backups_arr => \@backups_arr, dir_size => $dir_size ); + $self->render( template => 'backup/backup' ); } #################################################################################### sub save { - my $self = shift; + my $self = shift; - my $backup = do_storable_backup($self->app); + my $backup = do_storable_backup( $self->app ); - if ( $backup->is_healthy ) { - $self->flash( msg_type=>'success', msg => "Backup created successfully" ); - } - else { - $self->flash(msg_type=>'danger', msg => "Backup create failed!" ); - } - $self->redirect_to( 'backup_index' ); + if ( $backup->is_healthy ) { + $self->flash( + msg_type => 'success', + msg => "Backup created successfully" + ); + } + else { + $self->flash( msg_type => 'danger', msg => "Backup create failed!" ); + } + $self->redirect_to('backup_index'); } #################################################################################### sub save_mysql { - my $self = shift; + my $self = shift; - my $backup = do_mysql_backup($self->app); + my $backup = do_mysql_backup( $self->app ); - if ( $backup->is_healthy ) { - $self->flash( msg_type=>'success', msg => "Backup created successfully" ); - } - else { - $self->flash(msg_type=>'danger', msg => "Backup create failed!" ); - } - $self->redirect_to( 'backup_index' ); + if ( $backup->is_healthy ) { + $self->flash( + msg_type => 'success', + msg => "Backup created successfully" + ); + } + else { + $self->flash( msg_type => 'danger', msg => "Backup create failed!" ); + } + $self->redirect_to('backup_index'); } #################################################################################### sub cleanup { - my $self = shift; - my $age_treshold = $self->config->{backup_age_in_days_to_delete_automatically}; - - my $num_deleted = delete_old_backups($self->app, $age_treshold); - - $self->app->logger->info("Deleting old backups. $num_deleted backups have been cleaned."); - $self->flash( msg_type=>'success', msg => "$num_deleted backups have been cleaned." ); - - # redirecting to referrer here breaks the test if the test supports redirects! why? - # disabling redirects for test and putting here referrer allows test to pass - $self->redirect_to( 'backup_index' ); + my $self = shift; + my $age_treshold + = $self->config->{backup_age_in_days_to_delete_automatically}; + + my $num_deleted = delete_old_backups( $self->app, $age_treshold ); + + $self->app->logger->info( + "Deleting old backups. $num_deleted backups have been cleaned."); + $self->flash( + msg_type => 'success', + msg => "$num_deleted backups have been cleaned." + ); + +# redirecting to referrer here breaks the test if the test supports redirects! why? +# disabling redirects for test and putting here referrer allows test to pass + $self->redirect_to('backup_index'); } #################################################################################### sub backup_download { - my $self = shift; - my $uuid = $self->param('id'); + my $self = shift; + my $uuid = $self->param('id'); + + my $backup = find_backup( $uuid, $self->app->get_backups_dir ); + + if ( $backup and $backup->is_healthy ) { + $self->app->logger->info( "Downloading backup " . $backup->uuid ); + $self->render_file( 'filepath' => $backup->get_path ); + } + else { + $self->flash( + msg_type => 'danger', + msg => "Cannot download backup $uuid - backup not healthy." + ); + $self->redirect_to( $self->get_referrer ); + } +} - my $backup = find_backup($uuid, $self->app->get_backups_dir); +#################################################################################### +sub delete_backup { + my $self = shift; + my $uuid = $self->param('id'); + + my $backup = find_backup( $uuid, $self->app->get_backups_dir ); - if ( $backup and $backup->is_healthy ) { - $self->app->logger->info("Downloading backup ".$backup->uuid); - $self->render_file( 'filepath' => $backup->get_path ); + if ( $backup and $backup->is_healthy ) { + if ( $backup->get_age->days + >= $self->app->config->{allow_delete_backups_older_than} ) + { + $backup->allow_delete(1); } else { - $self->flash( msg_type=>'danger', msg => "Cannot download backup $uuid - backup not healthy." ); - $self->redirect_to( $self->get_referrer ); + $backup->allow_delete(undef); } -} - -#################################################################################### -sub delete_backup { - my $self = shift; - my $uuid = $self->param('id'); - - my $backup = find_backup($uuid, $self->app->get_backups_dir); - - if ( $backup and $backup->is_healthy ) { - if( $backup->get_age->days >= $self->app->config->{allow_delete_backups_older_than}){ - $backup->allow_delete(1); - } - else{ - $backup->allow_delete(undef); - } - if($backup->allow_delete){ - try{ - unlink $backup->get_path; - $self->app->logger->info("Deleting backup ".$backup->uuid); - $self->flash( msg_type=>'success', msg => "Backup id $uuid deleted!" ); - } - catch{ - $self->flash( msg_type=>'danger', msg => "Exception during deleting backup '$uuid': $_." ); - }; - } - else{ - $self->flash( msg_type=>'warning', msg => "Backup $uuid is too young to be deleted!" ); - } + if ( $backup->allow_delete ) { + try { + unlink $backup->get_path; + $self->app->logger->info( "Deleting backup " . $backup->uuid ); + $self->flash( + msg_type => 'success', + msg => "Backup id $uuid deleted!" + ); + } + catch { + $self->flash( + msg_type => 'danger', + msg => "Exception during deleting backup '$uuid': $_." + ); + }; } - else{ - $self->flash( msg_type=>'danger', msg => "Cannot delete backup $uuid - you need to do this manually." ); + else { + $self->flash( + msg_type => 'warning', + msg => "Backup $uuid is too young to be deleted!" + ); } + } + else { + $self->flash( + msg_type => 'danger', + msg => "Cannot delete backup $uuid - you need to do this manually." + ); + } - $self->res->code(303); - $self->redirect_to($self->url_for('backup_index')); + $self->res->code(303); + $self->redirect_to( $self->url_for('backup_index') ); } #################################################################################### sub restore_backup { - my $self = shift; - my $uuid = $self->param('id'); + my $self = shift; + my $uuid = $self->param('id'); - my $backup = find_backup($uuid, $self->app->get_backups_dir); + my $backup = find_backup( $uuid, $self->app->get_backups_dir ); - if($backup and $backup->is_healthy){ + if ( $backup and $backup->is_healthy ) { - restore_storable_backup($backup, $self->app); + restore_storable_backup( $backup, $self->app ); - $self->app->logger->info("Restoring backup ".$backup->uuid); + $self->app->logger->info( "Restoring backup " . $backup->uuid ); - my $status = "Status:
".$self->app->repo->lr->get_summary_table."
"; + my $status + = "Status:
"
+        . $self->app->repo->lr->get_summary_table
+        . "
"; - $self->flash( msg_type=>'success', msg => "Backup restored successfully. Database recreated, persistence layers in sync. $status" ); - } - else { - $self->flash( msg_type=>'danger', msg => "Cannot restore - backup not healthy!" ); - } - $self->redirect_to('backup_index'); + $self->flash( + msg_type => 'success', + msg => + "Backup restored successfully. Database recreated, persistence layers in sync. $status" + ); + } + else { + $self->flash( + msg_type => 'danger', + msg => "Cannot restore - backup not healthy!" + ); + } + $self->redirect_to('backup_index'); } #################################################################################### diff --git a/lib/BibSpace/Controller/Persistence.pm b/lib/BibSpace/Controller/Persistence.pm index e6b1790..fdeae4e 100644 --- a/lib/BibSpace/Controller/Persistence.pm +++ b/lib/BibSpace/Controller/Persistence.pm @@ -21,9 +21,12 @@ use Mojo::Base 'Mojolicious::Controller'; ################################################################################# sub persistence_status { - my $self = shift; + my $self = shift; - my $status = "Status:
" . $self->app->repo->lr->get_summary_table . "
"; + my $status + = "Status:
"
+      . $self->app->repo->lr->get_summary_table
+      . "
"; $self->stash( msg_type => 'success', msg => $status ); $self->flash( msg_type => 'success', msg => $status ); $self->redirect_to( $self->get_referrer ); @@ -31,9 +34,12 @@ sub persistence_status { ################################################################################# sub persistence_status_ajax { - my $self = shift; + my $self = shift; - my $status = "Status:
" . $self->app->repo->lr->get_summary_table . "
"; + my $status + = "Status:
"
+      . $self->app->repo->lr->get_summary_table
+      . "
"; $self->render( text => $status ); } @@ -41,17 +47,27 @@ sub persistence_status_ajax { sub load_fixture { my $self = shift; - $self->app->logger->warn("PERSISTENCE CONTROLLER does: load_fixture"); + my $fixture_file + = $self->app->home->rel_file('fixture/bibspace_fixture.dat'); + $self->app->logger->info( + "Loading fixture from: " . $fixture_file->to_string ); - my $fixture_name = "bibspace_fixture.dat"; - my $fixture_dir = "./fixture/"; - my $fixture = Backup->new( dir => $fixture_dir, filename => $fixture_name ); + my $fixture = Backup->new( + dir => '' . $fixture_file->dirname, + filename => '' . $fixture_file->basename + ); restore_storable_backup( $fixture, $self->app ); - my $status = "Status:
" . $self->app->repo->lr->get_summary_table . "
"; - $self->flash( msg_type => 'success', msg => "Fixture loaded into memory and mysql. $status" ); + my $status + = "Status:
"
+      . $self->app->repo->lr->get_summary_table
+      . "
"; + $self->flash( + msg_type => 'success', + msg => "Fixture loaded into memory and mysql. $status" + ); $self->redirect_to( $self->get_referrer ); } ################################################################################# @@ -60,36 +76,51 @@ sub save_fixture { $self->app->logger->warn("PERSISTENCE CONTROLLER does: save_fixture"); - my $fixture_name = "bibspace_fixture.dat"; - my $fixture_dir = "./fixture/"; - my $backup = Backup->create( 'dummy', "storable" ); - $backup->dir($fixture_dir); - $backup->filename($fixture_name); + my $fixture_file + = $self->app->home->rel_file('fixture/bibspace_fixture.dat'); + + my $backup = Backup->create( 'dummy', "storable" ); + $backup->dir( '' . $fixture_file->dirname ); + $backup->filename( '' . $fixture_file->basename ); my $layer = $self->app->repo->lr->get_read_layer; my $path = "" . $backup->get_path; - $Storable::forgive_me = "do store regexp please, we will not use them anyway"; + $Storable::forgive_me + = "do store regexp please, we will not use them anyway"; - # if you see any exceptions being thrown here, this might be due to REGEXP caused by DateTime pattern. - # this should not happen currently however - I think it is fixed now. +# if you see any exceptions being thrown here, this might be due to REGEXP caused by DateTime pattern. +# this should not happen currently however - I think it is fixed now. Storable::store $layer, $path; - my $status = "Status:
" . $self->app->repo->lr->get_summary_table . "
"; - $self->flash( msg_type => 'success', msg => "Fixture stored to '" . $backup->get_path . "'. $status" ); + my $status + = "Status:
"
+      . $self->app->repo->lr->get_summary_table
+      . "
"; + $self->flash( + msg_type => 'success', + msg => "Fixture stored to '" . $backup->get_path . "'. $status" + ); $self->redirect_to( $self->get_referrer ); } ################################################################################# sub copy_mysql_to_smart { my $self = shift; - $self->app->logger->warn("PERSISTENCE CONTROLLER does: copy_mysql_to_smart"); + $self->app->logger->warn( + "PERSISTENCE CONTROLLER does: copy_mysql_to_smart"); $self->app->repo->lr->copy_data( { from => 'mysql', to => 'smart' } ); $self->app->link_data; - my $status = "Status:
" . $self->app->repo->lr->get_summary_table . "
"; - $self->flash( msg_type => 'success', msg => "Copied mysql => smart. $status" ); + my $status + = "Status:
"
+      . $self->app->repo->lr->get_summary_table
+      . "
"; + $self->flash( + msg_type => 'success', + msg => "Copied mysql => smart. $status" + ); $self->redirect_to( $self->get_referrer ); } ################################################################################# @@ -99,8 +130,14 @@ sub copy_smart_to_mysql { $self->app->repo->lr->copy_data( { from => 'smart', to => 'mysql' } ); - my $status = "Status:
" . $self->app->repo->lr->get_summary_table . "
"; - $self->flash( msg_type => 'success', msg => "Copied smart => mysql. $status" ); + my $status + = "Status:
"
+      . $self->app->repo->lr->get_summary_table
+      . "
"; + $self->flash( + msg_type => 'success', + msg => "Copied smart => mysql. $status" + ); $self->redirect_to( $self->get_referrer ); } @@ -111,49 +148,51 @@ sub insert_random_data { my $str_len = 60; - for (1..$num){ + for ( 1 .. $num ) { my $obj = $self->app->entityFactory->new_User( - login => random_string($str_len), - email => random_string($str_len).'@example.com', - real_name => random_string($str_len), - pass => random_string($str_len), - pass2 => random_string($str_len) + login => random_string($str_len), + email => random_string($str_len) . '@example.com', + real_name => random_string($str_len), + pass => random_string($str_len), + pass2 => random_string($str_len) ); $self->app->repo->users_save($obj); $obj = $self->app->entityFactory->new_Author( - uid => random_string($str_len), - ); + uid => random_string($str_len), ); $self->app->repo->authors_save($obj); $obj = $self->app->entityFactory->new_Entry( - bib => random_string($str_len), - ); + bib => random_string($str_len), ); $self->app->repo->entries_save($obj); $obj = $self->app->entityFactory->new_TagType( - name => random_string($str_len), - ); + name => random_string($str_len), ); $self->app->repo->tagTypes_save($obj); - my $tt = ($self->app->repo->tagTypes_all)[0]; + my $tt = ( $self->app->repo->tagTypes_all )[0]; $obj = $self->app->entityFactory->new_Tag( - name => random_string($str_len), - type => $tt->id + name => random_string($str_len), + type => $tt->id ); $self->app->repo->tags_save($obj); $obj = $self->app->entityFactory->new_Team( - name => random_string($str_len), - ); + name => random_string($str_len), ); $self->app->repo->teams_save($obj); } - my $status = "Status:
" . $self->app->repo->lr->get_summary_table . "
"; - $self->flash( msg_type => 'success', msg => "Copied smart => mysql. $status" ); + my $status + = "Status:
"
+      . $self->app->repo->lr->get_summary_table
+      . "
"; + $self->flash( + msg_type => 'success', + msg => "Copied smart => mysql. $status" + ); $self->redirect_to( $self->get_referrer ); } ################################################################################# @@ -173,10 +212,14 @@ sub reset_smart { # instead, do not insert admin and set system in demo mode $self->app->preferences->run_in_demo_mode(1); - say "setting preferences->run_in_demo_mode to: '".$self->app->preferences->run_in_demo_mode."'"; + say "setting preferences->run_in_demo_mode to: '" + . $self->app->preferences->run_in_demo_mode . "'"; - my $status = "Status:
" . $self->app->repo->lr->get_summary_table . "
"; + my $status + = "Status:
"
+      . $self->app->repo->lr->get_summary_table
+      . "
"; $self->flash( msg_type => 'success', msg => $status ); $self->redirect_to( $self->get_referrer ); } @@ -189,12 +232,21 @@ sub reset_mysql { my $layer = $self->app->repo->lr->get_layer('mysql'); if ($layer) { $layer->reset_data; - my $status = "Status:
" . $self->app->repo->lr->get_summary_table . "
"; + my $status + = "Status:
"
+        . $self->app->repo->lr->get_summary_table
+        . "
"; $self->flash( msg_type => 'success', msg => $status ); } else { - my $status = "Status:
" . $self->app->repo->lr->get_summary_table . "
"; - $self->flash( msg_type => 'danger', msg => "Reset failed - backend handle undefined. " . $status ); + my $status + = "Status:
"
+        . $self->app->repo->lr->get_summary_table
+        . "
"; + $self->flash( + msg_type => 'danger', + msg => "Reset failed - backend handle undefined. " . $status + ); } $self->redirect_to( $self->get_referrer ); @@ -206,10 +258,9 @@ sub reset_all { $self->app->logger->warn("PERSISTENCE CONTROLLER does: reset_all"); my @layers = $self->app->repo->lr->get_all_layers; - foreach (@layers){ $_->reset_data }; + foreach (@layers) { $_->reset_data } $self->app->repo->lr->reset_uid_providers; - - + # no pub_admin user would lock the whole system # if you insert it here, it may will cause clash of IDs @@ -218,7 +269,10 @@ sub reset_all { $self->app->preferences->run_in_demo_mode(1); - my $status = "Status:
" . $self->app->repo->lr->get_summary_table . "
"; + my $status + = "Status:
"
+      . $self->app->repo->lr->get_summary_table
+      . "
"; $self->flash( msg_type => 'success', msg => $status ); $self->redirect_to( $self->get_referrer ); } @@ -233,7 +287,8 @@ sub system_status { my $backup_dir_absolute = $self->config->{backups_dir}; - $backup_dir_absolute =~ s!/*$!/!; # makes sure that there is exactly one / at the end + $backup_dir_absolute + =~ s!/*$!/!; # makes sure that there is exactly one / at the end my $errored = 0; diff --git a/lib/BibSpace/Controller/Preferences.pm b/lib/BibSpace/Controller/Preferences.pm index d391505..35f738a 100644 --- a/lib/BibSpace/Controller/Preferences.pm +++ b/lib/BibSpace/Controller/Preferences.pm @@ -59,9 +59,6 @@ sub save { $self->app->preferences->output_time_format($output_time_format); - # # store to file - # my $json_str = $self->app->preferences->store('bibspace_preferences.json'); - $self->stash( preferences => $self->app->preferences, msg_type=>$msg_type, msg =>$msg ); # $self->render( template => 'display/preferences' ); $self->redirect_to( $self->get_referrer ); diff --git a/lib/BibSpace/Controller/Publications.pm b/lib/BibSpace/Controller/Publications.pm index 76b855b..1b60534 100644 --- a/lib/BibSpace/Controller/Publications.pm +++ b/lib/BibSpace/Controller/Publications.pm @@ -6,7 +6,6 @@ use Text::BibTeX; # parsing bib files use DateTime; use Mojo::IOLoop; -# use File::Slurp; # should be replaced in the future use Path::Tiny; # for creating directories use Try::Tiny; @@ -29,18 +28,18 @@ use Mojo::UserAgent; use Mojo::Log; our %mons = ( - 1 => 'January', - 2 => 'February', - 3 => 'March', - 4 => 'April', - 5 => 'May', - 6 => 'June', - 7 => 'July', - 8 => 'August', - 9 => 'September', - 10 => 'October', - 11 => 'November', - 12 => 'December' + 1 => 'January', + 2 => 'February', + 3 => 'March', + 4 => 'April', + 5 => 'May', + 6 => 'June', + 7 => 'July', + 8 => 'August', + 9 => 'September', + 10 => 'October', + 11 => 'November', + 12 => 'December' ); #################################################################################### # work, but not for now @@ -55,163 +54,174 @@ our %mons = ( # } #################################################################################### sub all { - my $self = shift; + my $self = shift; - my @all = Fget_publications_main_hashed_args( $self, {year=>undef}); - my @filtered = Fget_publications_main_hashed_args( $self, {}, \@all); + my @all = Fget_publications_main_hashed_args( $self, { year => undef } ); + my @filtered = Fget_publications_main_hashed_args( $self, {}, \@all ); - $self->stash( entries => \@filtered, all_entries => \@all); - my $html = $self->render_to_string( template => 'publications/all' ); - $self->render( data => $html ); + $self->stash( entries => \@filtered, all_entries => \@all ); + my $html = $self->render_to_string( template => 'publications/all' ); + $self->render( data => $html ); } #################################################################################### sub all_recently_added { - my $self = shift; - my $num = $self->param('num') // 10; + my $self = shift; + my $num = $self->param('num') // 10; - $self->app->logger->info("Displaying recently added entries."); + $self->app->logger->info("Displaying recently added entries."); - my @all = Fget_publications_main_hashed_args( $self, {year=>undef}); - my @added_entries = sort { $b->creation_time cmp $a->creation_time } @all; - @added_entries = @added_entries[ 0 .. $num - 1 ]; + my @all = Fget_publications_main_hashed_args( $self, { year => undef } ); + my @added_entries = sort { $b->creation_time cmp $a->creation_time } @all; + @added_entries = @added_entries[ 0 .. $num - 1 ]; - my @filtered = Fget_publications_main_hashed_args( $self, {}, \@added_entries); - # special sorting here - @filtered = sort { $b->creation_time cmp $a->creation_time } @filtered; + my @filtered + = Fget_publications_main_hashed_args( $self, {}, \@added_entries ); - $self->stash( entries => \@filtered, all_entries => \@added_entries ); - $self->render( template => 'publications/all' ); + # special sorting here + @filtered = sort { $b->creation_time cmp $a->creation_time } @filtered; + + $self->stash( entries => \@filtered, all_entries => \@added_entries ); + $self->render( template => 'publications/all' ); } #################################################################################### sub all_recently_modified { - my $self = shift; - my $num = $self->param('num') // 10; + my $self = shift; + my $num = $self->param('num') // 10; + + $self->app->logger->info("Displaying recently modified entries."); + - $self->app->logger->info("Displaying recently modified entries."); + my @all = Fget_publications_main_hashed_args( $self, { year => undef } ); + my @modified_entries + = sort { $b->modified_time cmp $a->modified_time } @all; + @modified_entries = @modified_entries[ 0 .. $num - 1 ]; - - my @all = Fget_publications_main_hashed_args( $self, {year=>undef}); - my @modified_entries = sort { $b->modified_time cmp $a->modified_time } @all; - @modified_entries = @modified_entries[ 0 .. $num - 1 ]; + my @filtered + = Fget_publications_main_hashed_args( $self, {}, \@modified_entries ); - my @filtered = Fget_publications_main_hashed_args( $self, {}, \@modified_entries); - # special sorting here - @filtered = sort { $b->modified_time cmp $a->modified_time } @filtered; + # special sorting here + @filtered = sort { $b->modified_time cmp $a->modified_time } @filtered; - $self->stash( entries => \@filtered, all_entries => \@modified_entries ); - $self->render( template => 'publications/all' ); + $self->stash( entries => \@filtered, all_entries => \@modified_entries ); + $self->render( template => 'publications/all' ); } #################################################################################### sub all_without_tag { - my $self = shift; - my $tagtype = $self->param('tagtype') // 1; + my $self = shift; + my $tagtype = $self->param('tagtype') // 1; - # this will filter entries based on query - my @all = Fget_publications_main_hashed_args( $self, {year=>undef} ); - - my @untagged_entries = grep { scalar $_->get_tags($tagtype) == 0 } @all; - my @filtered = Fget_publications_main_hashed_args( $self, {}, \@untagged_entries); + # this will filter entries based on query + my @all = Fget_publications_main_hashed_args( $self, { year => undef } ); + my @untagged_entries = grep { scalar $_->get_tags($tagtype) == 0 } @all; + my @filtered + = Fget_publications_main_hashed_args( $self, {}, \@untagged_entries ); - my $msg - = "This list contains papers that have no tags of type '$tagtype'. Use this list to tag the untagged papers! "; - $self->stash( msg_type => 'info', msg => $msg ); - $self->stash( entries => \@filtered, all_entries => \@untagged_entries ); - $self->render( template => 'publications/all' ); + + my $msg + = "This list contains papers that have no tags of type '$tagtype'. Use this list to tag the untagged papers! "; + $self->stash( msg_type => 'info', msg => $msg ); + $self->stash( entries => \@filtered, all_entries => \@untagged_entries ); + $self->render( template => 'publications/all' ); } #################################################################################### sub all_orphaned { - my $self = shift; + my $self = shift; - my @all = Fget_publications_main_hashed_args( $self, {year=>undef} ); + my @all = Fget_publications_main_hashed_args( $self, { year => undef } ); - my @entries = grep { scalar( $_->get_authors ) == 0 } @all; - - my @filtered = Fget_publications_main_hashed_args( $self, {}, \@entries); + my @entries = grep { scalar( $_->get_authors ) == 0 } @all; - my $msg - = "This list contains papers, that are currently not assigned to any of authors."; - $msg .= ' Click to delete '; + my @filtered = Fget_publications_main_hashed_args( $self, {}, \@entries ); + + my $msg + = "This list contains papers, that are currently not assigned to any of authors."; + $msg + .= ' Click to delete '; - $self->stash( msg_type => 'info', msg => $msg ); - $self->stash(entries => \@filtered, all_entries => \@entries); - $self->render( template => 'publications/all' ); + $self->stash( msg_type => 'info', msg => $msg ); + $self->stash( entries => \@filtered, all_entries => \@entries ); + $self->render( template => 'publications/all' ); } #################################################################################### sub show_unrelated_to_team { - my $self = shift; - my $team_id = $self->param('teamid'); + my $self = shift; + my $team_id = $self->param('teamid'); - $self->app->logger->info( - "Displaying entries unrelated to team '$team_id'."); + $self->app->logger->info( + "Displaying entries unrelated to team '$team_id'."); - my $team_name = ""; - my $team = $self->app->repo->teams_find( sub { $_->id == $team_id } ); - $team_name = $team->name if defined $team; + my $team_name = ""; + my $team = $self->app->repo->teams_find( sub { $_->id == $team_id } ); + $team_name = $team->name if defined $team; - my @all = Fget_publications_main_hashed_args( $self, {year=>undef} ); - my @teamEntres = $team->get_entries; + my @all = Fget_publications_main_hashed_args( $self, { year => undef } ); + my @teamEntres = $team->get_entries; - my %inTeam = map { $_ => 1 } @teamEntres; - my @entriesUnrelated = grep { not $inTeam{$_} } @all; + my %inTeam = map { $_ => 1 } @teamEntres; + my @entriesUnrelated = grep { not $inTeam{$_} } @all; - # hash destroys order! - @entriesUnrelated = sort_publications(@entriesUnrelated); - my @filtered = Fget_publications_main_hashed_args( $self, {}, \@entriesUnrelated); + # hash destroys order! + @entriesUnrelated = sort_publications(@entriesUnrelated); + my @filtered + = Fget_publications_main_hashed_args( $self, {}, \@entriesUnrelated ); - my $msg = "This list contains papers, that are: + my $msg = "This list contains papers, that are:
  • Not assigned to the team " - . $team_name . "
  • + . $team_name . "
  • Not assigned to any author (former or actual) of the team " - . $team_name . "
  • + . $team_name . "
"; - $self->stash( msg_type => 'info', msg => $msg ); - $self->stash( entries => \@filtered, all_entries => \@entriesUnrelated); - $self->render( template => 'publications/all' ); + $self->stash( msg_type => 'info', msg => $msg ); + $self->stash( entries => \@filtered, all_entries => \@entriesUnrelated ); + $self->render( template => 'publications/all' ); } #################################################################################### sub all_with_missing_month { - my $self = shift; + my $self = shift; - $self->app->logger->info("Displaying entries without month"); + $self->app->logger->info("Displaying entries without month"); - my @all = Fget_publications_main_hashed_args( $self, {year=>undef} ); - my @entries = grep { !defined $_->month or $_->month < 1 or $_->month > 12 } @all; + my @all = Fget_publications_main_hashed_args( $self, { year => undef } ); + my @entries + = grep { !defined $_->month or $_->month < 1 or $_->month > 12 } @all; - my @filtered = Fget_publications_main_hashed_args( $self, {}, \@entries); + my @filtered = Fget_publications_main_hashed_args( $self, {}, \@entries ); - my $msg - = "This list contains entries with missing BibTeX field 'month'. "; - $msg .= "Add this data to get the proper chronological sorting."; + my $msg = "This list contains entries with missing BibTeX field 'month'. "; + $msg .= "Add this data to get the proper chronological sorting."; - $self->stash( msg_type => 'info', msg => $msg ); - $self->stash( entries => \@filtered, all_entries => \@entries ); - $self->render( template => 'publications/all' ); + $self->stash( msg_type => 'info', msg => $msg ); + $self->stash( entries => \@filtered, all_entries => \@entries ); + $self->render( template => 'publications/all' ); } #################################################################################### sub all_candidates_to_delete { - my $self = shift; + my $self = shift; - $self->app->logger->info( - "Displaying entries that are candidates_to_delete"); + $self->app->logger->info( + "Displaying entries that are candidates_to_delete"); - my @all = Fget_publications_main_hashed_args( $self, {year=>undef} ); - my @entries = grep { scalar $_->get_tags == 0 } @all; # no tags - @entries = grep { scalar $_->get_teams == 0 } @entries; # no relation to teams - @entries = grep { scalar $_->get_exceptions == 0 } @entries; # no exceptions - my @filtered = Fget_publications_main_hashed_args( $self, {}, \@entries); + my @all = Fget_publications_main_hashed_args( $self, { year => undef } ); + my @entries = grep { scalar $_->get_tags == 0 } @all; # no tags + @entries + = grep { scalar $_->get_teams == 0 } @entries; # no relation to teams + @entries = grep { scalar $_->get_exceptions == 0 } @entries; # no exceptions + my @filtered = Fget_publications_main_hashed_args( $self, {}, \@entries ); - my $msg = "

This list contains papers, that are:

+ my $msg = "

This list contains papers, that are:

  • Not assigned to any team AND
  • have exactly 0 tags AND
  • @@ -220,1014 +230,1012 @@ sub all_candidates_to_delete {

Such entries may wanted to be removed form the system or serve as a help with configuration.

"; - $self->stash( msg_type => 'info', msg => $msg ); - $self->stash( entries => \@filtered, all_entries => \@entries ); - $self->render( template => 'publications/all' ); + $self->stash( msg_type => 'info', msg => $msg ); + $self->stash( entries => \@filtered, all_entries => \@entries ); + $self->render( template => 'publications/all' ); } #################################################################################### #################################################################################### #################################################################################### sub all_bibtex { - my $self = shift; + my $self = shift; - my @objs = Fget_publications_main_hashed_args( $self, { hidden => 0 } ); + my @objs = Fget_publications_main_hashed_args( $self, { hidden => 0 } ); - my $big_str = "
\n";
-    foreach my $obj (@objs) {
-        $big_str .= $obj->{bib};
-        $big_str .= "\n";
-    }
-    $big_str .= "\n
"; - $self->render( text => $big_str ); + my $big_str = "
\n";
+  foreach my $obj (@objs) {
+    $big_str .= $obj->{bib};
+    $big_str .= "\n";
+  }
+  $big_str .= "\n
"; + $self->render( text => $big_str ); } #################################################################################### sub all_read { - my $self = shift; + my $self = shift; - # this function does filtering ! - my @objs = Fget_publications_main_hashed_args( $self, { hidden => 0 } ); + # this function does filtering ! + my @objs = Fget_publications_main_hashed_args( $self, { hidden => 0 } ); - $self->stash( entries => \@objs ); - my $html = $self->render_to_string( template => 'publications/all_read' ); - $self->render( data => $html ); + $self->stash( entries => \@objs ); + my $html = $self->render_to_string( template => 'publications/all_read' ); + $self->render( data => $html ); } #################################################################################### sub single { - my $self = shift; - my $id = $self->param('id'); - - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); - - my @objs; - if ( defined $entry ) { - push @objs, $entry; - } - else { - $self->stash( - msg_type => 'danger', - msg => "Entry $id does not exist." - ); - } - $self->stash( entries => \@objs ); - $self->render( template => 'publications/all' ); + my $self = shift; + my $id = $self->param('id'); + + my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); + + my @objs; + if ( defined $entry ) { + push @objs, $entry; + } + else { + $self->stash( msg_type => 'danger', msg => "Entry $id does not exist." ); + } + $self->stash( entries => \@objs ); + $self->render( template => 'publications/all' ); } #################################################################################### sub single_read { - my $self = shift; - my $id = $self->param('id'); + my $self = shift; + my $id = $self->param('id'); - my @objs = (); + my @objs = (); - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); + my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); - if ( defined $entry and $entry->is_hidden == 0 ) { - push @objs, $entry; - } - $self->stash( entries => \@objs ); - $self->render( template => 'publications/all_read' ); + if ( defined $entry and $entry->is_hidden == 0 ) { + push @objs, $entry; + } + $self->stash( entries => \@objs ); + $self->render( template => 'publications/all_read' ); } #################################################################################### sub fixMonths { - my $self = shift; + my $self = shift; - $self->app->logger->info("Fix months in all entries."); + $self->app->logger->info("Fix months in all entries."); - my @entries = $self->app->repo->entries_all; + my @entries = $self->app->repo->entries_all; - foreach my $entry (@entries) { - $entry->fix_month(); - } - $self->app->repo->entries_save(@entries); + foreach my $entry (@entries) { + $entry->fix_month(); + } + $self->app->repo->entries_save(@entries); - $self->flash( - msg => 'Fixing entries month field finished.', - msg_type => 'info' - ); - $self->redirect_to( $self->get_referrer ); + $self->flash( + msg => 'Fixing entries month field finished.', + msg_type => 'info' + ); + $self->redirect_to( $self->get_referrer ); } #################################################################################### sub toggle_hide { - my $self = shift; - my $id = $self->param('id'); + my $self = shift; + my $id = $self->param('id'); - $self->app->logger->info("Toggle hide entry '$id'."); + $self->app->logger->info("Toggle hide entry '$id'."); - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); + my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); - if ( defined $entry ) { - $entry->toggle_hide; - $self->app->repo->entries_update($entry); - } - else { - $self->flash( msg => "There is no entry with id $id" ); - } + if ( defined $entry ) { + $entry->toggle_hide; + $self->app->repo->entries_update($entry); + } + else { + $self->flash( msg => "There is no entry with id $id" ); + } - $self->redirect_to( $self->get_referrer ); + $self->redirect_to( $self->get_referrer ); } #################################################################################### sub make_paper { - my $self = shift; - my $id = $self->param('id'); + my $self = shift; + my $id = $self->param('id'); - $self->app->logger->info("Make entry '$id' 'paper'."); + $self->app->logger->info("Make entry '$id' 'paper'."); - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); + my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); - if ( defined $entry ) { - $entry->make_paper(); - $self->app->repo->entries_update($entry); - } - else { - $self->flash( msg => "There is no entry with id $id" ); - } + if ( defined $entry ) { + $entry->make_paper(); + $self->app->repo->entries_update($entry); + } + else { + $self->flash( msg => "There is no entry with id $id" ); + } - $self->redirect_to( $self->get_referrer ); + $self->redirect_to( $self->get_referrer ); } #################################################################################### sub make_talk { - my $self = shift; - my $id = $self->param('id'); + my $self = shift; + my $id = $self->param('id'); - $self->app->logger->info("Make entry '$id' 'talk'."); + $self->app->logger->info("Make entry '$id' 'talk'."); - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); + my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); - if ( defined $entry ) { - $entry->make_talk(); - $self->app->repo->entries_update($entry); - } - else { - $self->flash( msg => "There is no entry with id $id" ); - } + if ( defined $entry ) { + $entry->make_talk(); + $self->app->repo->entries_update($entry); + } + else { + $self->flash( msg => "There is no entry with id $id" ); + } - $self->redirect_to( $self->get_referrer ); + $self->redirect_to( $self->get_referrer ); } #################################################################################### sub delete_orphaned { - my $self = shift; - - my @entries = $self->app->repo->entries_filter( - sub { scalar( $_->get_authors ) == 0 } ); - - foreach my $entry (@entries){ - my @au = $entry->authorships_all; - $self->app->repo->authorships_delete(@au); - my @ex = $entry->exceptions_all; - $self->app->repo->exceptions_delete(@ex); - my @la = $entry->labelings_all; - $self->app->repo->labelings_delete(@la); - } + my $self = shift; - my $num_deleted = $self->app->repo->entries_delete(@entries); + my @entries = $self->app->repo->entries_filter( + sub { scalar( $_->get_authors ) == 0 } ); + + foreach my $entry (@entries) { + my @au = $entry->authorships_all; + $self->app->repo->authorships_delete(@au); + my @ex = $entry->exceptions_all; + $self->app->repo->exceptions_delete(@ex); + my @la = $entry->labelings_all; + $self->app->repo->labelings_delete(@la); + } + + my $num_deleted = $self->app->repo->entries_delete(@entries); + + my $msg = "$num_deleted entries have been removed"; + $self->flash( msg => $msg, msg_type => 'info' ); + $self->redirect_to('all_orphaned'); - my $msg - = "$num_deleted entries have been removed"; - $self->flash( msg => $msg, msg_type => 'info' ); - $self->redirect_to( 'all_orphaned' ); - } #################################################################################### sub fix_file_urls { - my $self = shift; - my $id = $self->param('id'); + my $self = shift; + my $id = $self->param('id'); + + $self->app->logger->info("Fixing file urls for all entries."); - $self->app->logger->info("Fixing file urls for all entries."); + my @all_entries; - my @all_entries; + if ($id) { + my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); + push @all_entries, $entry if $entry; + } + else { + @all_entries = $self->app->repo->entries_all; + } + + my $big_str = ".\n"; + my $num_fixes = 0; + my $num_checks = 0; - if ($id) { - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); - push @all_entries, $entry if $entry; + for my $entry (@all_entries) { + + ++$num_checks; + my $str; + $str .= "Entry " . $entry->id . ": "; + $entry->discover_attachments( $self->app->get_upload_dir ); + my @discovered_types = $entry->attachments_keys; + + + $str .= "has types: ("; + foreach (@discovered_types) { + $str .= " $_, "; } - else { - @all_entries = $self->app->repo->entries_all; + $str .= "). Fixed: "; + + # say $str; + my $fixed; + my $file = $entry->get_attachment('paper'); + my $file_url = $self->url_for( + 'download_publication_pdf', + filetype => "paper", + id => $entry->id + )->to_abs; + + if ( $file and $file->exists ) { + $entry->remove_bibtex_fields( ['pdf'] ); + $str .= "\n\t"; + $entry->add_bibtex_field( "pdf", "$file_url" ); + $fixed = 1; + $str .= "Added Bibtex filed PDF " . $file_url; } - my $big_str = ".\n"; - my $num_fixes = 0; - my $num_checks = 0; - - for my $entry (@all_entries) { - - ++$num_checks; - my $str; - $str .= "Entry " . $entry->id . ": "; - $entry->discover_attachments( $self->app->get_upload_dir ); - my @discovered_types = $entry->attachments_keys; - - - - $str .= "has types: ("; - foreach (@discovered_types) { - $str .= " $_, "; - } - $str .= "). Fixed: "; - - # say $str; - my $fixed; - my $file = $entry->get_attachment('paper'); - my $file_url = $self->url_for( - 'download_publication_pdf', - filetype => "paper", - id => $entry->id - )->to_abs; - - if ( $file and $file->exists ) { - $entry->remove_bibtex_fields( ['pdf'] ); - $str .= "\n\t"; - $entry->add_bibtex_field( "pdf", "$file_url" ); - $fixed = 1; - $str .= "Added Bibtex filed PDF " . $file_url; - } - - $file = $entry->get_attachment('slides'); - $file_url = $self->url_for( - 'download_publication', - filetype => "slides", - id => $entry->id - )->to_abs; - - if ( $file and $file->exists ) { - $entry->remove_bibtex_fields( ['slides'] ); - $str .= "\n\t"; - $entry->add_bibtex_field( "slides", "$file_url" ); - $fixed = 1; - $str .= "Added Bibtex filed SLIDES " . $file_url; - } - $str .= "\n"; - - if ($fixed) { - $big_str .= $str; - ++$num_fixes; - $entry->regenerate_html( 0, $self->app->bst, - $self->app->bibtexConverter ); - } + $file = $entry->get_attachment('slides'); + $file_url = $self->url_for( + 'download_publication', + filetype => "slides", + id => $entry->id + )->to_abs; + + if ( $file and $file->exists ) { + $entry->remove_bibtex_fields( ['slides'] ); + $str .= "\n\t"; + $entry->add_bibtex_field( "slides", "$file_url" ); + $fixed = 1; + $str .= "Added Bibtex filed SLIDES " . $file_url; } + $str .= "\n"; - $self->app->logger->info("Url fix results $big_str."); + if ($fixed) { + $big_str .= $str; + ++$num_fixes; + $entry->regenerate_html( 0, $self->app->bst, + $self->app->bibtexConverter ); + } + } - $self->flash( - msg_type => 'info', - msg => - "Checked $num_checks and regenerated $num_fixes entries. You may need to run regenerate HTML force now. Detailed fix results have been saved to log." - ); - $self->redirect_to( $self->get_referrer ); + $self->app->logger->info("Url fix results $big_str."); + + $self->flash( + msg_type => 'info', + msg => + "Checked $num_checks and regenerated $num_fixes entries. You may need to run regenerate HTML force now. Detailed fix results have been saved to log." + ); + $self->redirect_to( $self->get_referrer ); } #################################################################################### sub remove_attachment { - my $self = shift; - my $id = $self->param('id'); # entry ID - my $filetype = $self->param('filetype') // 'paper'; # paper, slides - - $self->app->logger->info( - "Requested to remove attachment of type '$filetype'."); - - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); - my ( $msg, $msg_type ); + my $self = shift; + my $id = $self->param('id'); # entry ID + my $filetype = $self->param('filetype') // 'paper'; # paper, slides - $entry->discover_attachments( $self->app->get_upload_dir ); + $self->app->logger->info( + "Requested to remove attachment of type '$filetype'."); + my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); + my ( $msg, $msg_type ); - if ( $entry->attachments_has($filetype) ) { - $self->app->logger->debug( - "Entry has attachment of type '$filetype'."); + $entry->discover_attachments( $self->app->get_upload_dir ); - if ( $filetype eq 'paper' ) { - $entry->remove_bibtex_fields( ['pdf'] ); - } - elsif ( $filetype eq 'slides' ) { - $entry->remove_bibtex_fields( ['slides'] ); - } - $entry->delete_attachment($filetype); - $entry->regenerate_html( 1, $self->app->bst, - $self->app->bibtexConverter ); - $self->app->repo->entries_save($entry); + if ( $entry->attachments_has($filetype) ) { + $self->app->logger->debug("Entry has attachment of type '$filetype'."); - $msg = "The attachment has been removed for entry '$id'."; - $msg_type = 'success'; - $self->app->logger->info($msg); + if ( $filetype eq 'paper' ) { + $entry->remove_bibtex_fields( ['pdf'] ); } - else { - $self->app->logger->debug( - "Entry has NO attachment of type '$filetype'."); - - $msg - = "File not found. Cannot remove attachment. Filetype '$filetype', entry '$id'."; - $msg_type = 'danger'; - $self->app->logger->error($msg); + elsif ( $filetype eq 'slides' ) { + $entry->remove_bibtex_fields( ['slides'] ); } + $entry->delete_attachment($filetype); - $self->flash( msg_type => $msg_type, msg => $msg ); - $self->redirect_to( $self->get_referrer ); + $entry->regenerate_html( 1, $self->app->bst, + $self->app->bibtexConverter ); + $self->app->repo->entries_save($entry); + + $msg = "The attachment has been removed for entry '$id'."; + $msg_type = 'success'; + $self->app->logger->info($msg); + } + else { + $self->app->logger->debug("Entry has NO attachment of type '$filetype'."); + + $msg + = "File not found. Cannot remove attachment. Filetype '$filetype', entry '$id'."; + $msg_type = 'danger'; + $self->app->logger->error($msg); + } + + $self->flash( msg_type => $msg_type, msg => $msg ); + $self->redirect_to( $self->get_referrer ); } #################################################################################### sub discover_attachments { - my $self = shift; - my $id = $self->param('id'); - my $do = $self->param('do'); + my $self = shift; + my $id = $self->param('id'); + my $do = $self->param('do'); - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); - $self->app->logger->info("Discovery of attachments for entry ID '$id'." ); - - my $msg; - my $msg_type = 'info'; - if ( $entry and $do and $do == 1 ) { - $entry->discover_attachments( $self->app->get_upload_dir ); - $msg .= "Discovery was run for dir '" - . $self->app->get_upload_dir . "'."; - } - elsif( $entry ) { - $msg .= "Just displaying information. "; - $msg + my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); + $self->app->logger->info("Discovery of attachments for entry ID '$id'."); + + my $msg; + my $msg_type = 'info'; + if ( $entry and $do and $do == 1 ) { + $entry->discover_attachments( $self->app->get_upload_dir ); + $msg .= "Discovery was run for dir '" . $self->app->get_upload_dir . "'."; + } + elsif ($entry) { + $msg .= "Just displaying information. "; + $msg .= "Attachments debug:
"
         . $entry->get_attachments_debug_string
         . "
"; - } - else{ - $msg = "Cannot discover, entry '$id' not found."; - $msg_type = 'danger'; - $self->app->logger->error( $msg ); - } - + } + else { + $msg = "Cannot discover, entry '$id' not found."; + $msg_type = 'danger'; + $self->app->logger->error($msg); + } - $self->flash( msg_type => $msg_type, msg => $msg ); - $self->redirect_to( $self->get_referrer ); + + $self->flash( msg_type => $msg_type, msg => $msg ); + $self->redirect_to( $self->get_referrer ); } #################################################################################### sub download { - my $self = shift; - my $id = $self->param('id'); # entry ID - my $filetype = $self->param('filetype'); + my $self = shift; + my $id = $self->param('id'); # entry ID + my $filetype = $self->param('filetype'); - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); - my $file; + my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); + my $file; - if ($entry) { - $entry->discover_attachments( $self->app->get_upload_dir ); - $file = $entry->get_attachment($filetype); - } - else { - $self->app->logger->error("Cannot download - entry '$id' not found."); - $self->render( status => 404, text => "File not found." ); - return; - } - - if ( $file and -e $file ) { - $self->render_file( 'filepath' => $file ); - return; - } - $self->app->logger->error( - "File not found. Requested download for entry '$id', filetype '$filetype'." - ); - $self->render( text => "File not found.", status => 404 ); + if ($entry) { + $entry->discover_attachments( $self->app->get_upload_dir ); + $file = $entry->get_attachment($filetype); + } + else { + $self->app->logger->error("Cannot download - entry '$id' not found."); + $self->render( status => 404, text => "File not found." ); + return; + } + + if ( $file and -e $file ) { + $self->render_file( 'filepath' => $file ); + return; + } + $self->app->logger->error( + "File not found. Requested download for entry '$id', filetype '$filetype'." + ); + $self->render( text => "File not found.", status => 404 ); } #################################################################################### sub add_pdf { - my $self = shift; - my $id = $self->param('id'); - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); + my $self = shift; + my $id = $self->param('id'); + my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); - if ( !defined $entry ) { - $self->flash( msg_type => 'danger', msg => "Entry '$id' not found." ); - $self->redirect_to( $self->get_referrer ); - return; - } - $entry->populate_from_bib(); - $entry->generate_html( $self->app->bst, $self->app->bibtexConverter ); + if ( !defined $entry ) { + $self->flash( msg_type => 'danger', msg => "Entry '$id' not found." ); + $self->redirect_to( $self->get_referrer ); + return; + } + $entry->populate_from_bib(); + $entry->generate_html( $self->app->bst, $self->app->bibtexConverter ); - $self->stash( mentry => $entry ); - $self->render( template => 'publications/pdf_upload' ); + $self->stash( mentry => $entry ); + $self->render( template => 'publications/pdf_upload' ); } #################################################################################### sub add_pdf_post { - my $self = shift; - my $id = $self->param('id'); - my $filetype = $self->param('filetype'); - my $uploaded_file = $self->param('uploaded_file'); - - my $uploads_directory - = Path::Tiny->new( $self->app->get_upload_dir ); - - $self->app->logger->info("Saving attachment for entry '$id'"); - - # Check file size - if ( $self->req->is_limit_exceeded ) { - my $curr_limit_B = $ENV{MOJO_MAX_MESSAGE_SIZE}; - $curr_limit_B ||= 16777216; - my $curr_limit_MB = $curr_limit_B / 1024 / 1024; - $self->app->logger->info( - "Saving attachment for paper id '$id': limit exceeded. Current limit: $curr_limit_MB MB." - ); - $self->flash( - msg => "The File is too big and cannot be saved!", - msg_type => "danger" - ); - $self->redirect_to( $self->get_referrer ); - return; - } - - - if ( !$uploaded_file ) { - $self->flash( - msg => "File upload unsuccessful!", - msg_type => "danger" - ); - $self->app->logger->info( - "Saving attachment for paper id '$id' FAILED. Unknown reason"); - $self->redirect_to( $self->get_referrer ); - return; - } - - my $size = $uploaded_file->size; - my $sizeKB = int( $size / 1024 ); - if ( $size == 0 ) { - $self->flash( - msg => "No file was selected or file has 0 bytes! Not saving!", - msg_type => "danger" - ); - $self->app->logger->info( - "Saving attachment for paper id '$id' FAILED. File size is 0."); - $self->redirect_to( $self->get_referrer ); - return; - } - - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); - - if ( !defined $entry ) { - $self->flash( - msg_type => 'danger', - msg => "Entry '$id' does not exist." - ); - $self->redirect_to( $self->get_referrer ); - return; - } + my $self = shift; + my $id = $self->param('id'); + my $filetype = $self->param('filetype'); + my $uploaded_file = $self->param('uploaded_file'); + my $uploads_directory = Path::Tiny->new( $self->app->get_upload_dir ); - my $name = $uploaded_file->filename; - my @dot_arr = split( /\./, $name ); - my $extension = $dot_arr[-1]; + $self->app->logger->info("Saving attachment for entry '$id'"); + # Check file size + if ( $self->req->is_limit_exceeded ) { + my $curr_limit_B = $ENV{MOJO_MAX_MESSAGE_SIZE}; + $curr_limit_B ||= 16777216; + my $curr_limit_MB = $curr_limit_B / 1024 / 1024; + $self->app->logger->info( + "Saving attachment for paper id '$id': limit exceeded. Current limit: $curr_limit_MB MB." + ); + $self->flash( + msg => "The File is too big and cannot be saved!", + msg_type => "danger" + ); + $self->redirect_to( $self->get_referrer ); + return; + } - my $file_url; - my $destination; - if ( $filetype eq 'paper' ) { - $entry->delete_attachment('paper'); - $destination - = $uploads_directory->path( "papers", "paper-$id.$extension" ); - $uploaded_file->move_to($destination); - $self->app->logger->debug( - "Attachments file has been moved to: $destination."); - - $entry->add_attachment( 'paper', $destination ); - $file_url = $self->url_for( - 'download_publication_pdf', - filetype => "paper", - id => $entry->id - )->to_abs; - $entry->add_bibtex_field( 'pdf', "$file_url" ); - } - elsif ( $filetype eq 'slides' ) { - $entry->delete_attachment('slides'); - $destination = $uploads_directory->path( "slides", - "slides-paper-$id.$extension" ); - $uploaded_file->move_to($destination); - $self->app->logger->debug( - "Attachments file has been moved to: $destination."); - - $entry->add_attachment( 'slides', $destination ); - $file_url = $self->url_for( - 'download_publication', - filetype => "slides", - id => $entry->id - )->to_abs; - $entry->add_bibtex_field( 'slides', "$file_url" ); - } - else { - # ignore - we support only pdf and slides so far - } + if ( !$uploaded_file ) { + $self->flash( msg => "File upload unsuccessful!", msg_type => "danger" ); + $self->app->logger->info( + "Saving attachment for paper id '$id' FAILED. Unknown reason"); + $self->redirect_to( $self->get_referrer ); + return; + } + my $size = $uploaded_file->size; + my $sizeKB = int( $size / 1024 ); + if ( $size == 0 ) { + $self->flash( + msg => "No file was selected or file has 0 bytes! Not saving!", + msg_type => "danger" + ); $self->app->logger->info( - "Saving attachment for entry '$id' under: '$destination'."); + "Saving attachment for paper id '$id' FAILED. File size is 0."); + $self->redirect_to( $self->get_referrer ); + return; + } - my $msg - = "Successfully uploaded the $sizeKB KB file as $filetype. + my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); + + if ( !defined $entry ) { + $self->flash( + msg_type => 'danger', + msg => "Entry '$id' does not exist." + ); + $self->redirect_to( $self->get_referrer ); + return; + } + + + my $name = $uploaded_file->filename; + my @dot_arr = split( /\./, $name ); + my $extension = $dot_arr[-1]; + + + my $file_url; + my $destination; + + if ( $filetype eq 'paper' ) { + $entry->delete_attachment('paper'); + $destination + = $uploads_directory->path( "papers", "paper-$id.$extension" ); + $uploaded_file->move_to($destination); + $self->app->logger->debug( + "Attachments file has been moved to: $destination."); + + $entry->add_attachment( 'paper', $destination ); + $file_url = $self->url_for( + 'download_publication_pdf', + filetype => "paper", + id => $entry->id + )->to_abs; + $entry->add_bibtex_field( 'pdf', "$file_url" ); + } + elsif ( $filetype eq 'slides' ) { + $entry->delete_attachment('slides'); + $destination + = $uploads_directory->path( "slides", "slides-paper-$id.$extension" ); + $uploaded_file->move_to($destination); + $self->app->logger->debug( + "Attachments file has been moved to: $destination."); + + $entry->add_attachment( 'slides', $destination ); + $file_url = $self->url_for( + 'download_publication', + filetype => "slides", + id => $entry->id + )->to_abs; + $entry->add_bibtex_field( 'slides', "$file_url" ); + } + else { + # ignore - we support only pdf and slides so far + } + + $self->app->logger->info( + "Saving attachment for entry '$id' under: '$destination'."); + + my $msg + = "Successfully uploaded the $sizeKB KB file as $filetype. The file was renamed to: " - . $destination->basename . ""; + . $file_url . "\">" . $destination->basename . ""; - $entry->regenerate_html( 1, $self->app->bst, - $self->app->bibtexConverter ); - $self->app->repo->entries_save($entry); + $entry->regenerate_html( 1, $self->app->bst, $self->app->bibtexConverter ); + $self->app->repo->entries_save($entry); - $self->flash( msg_type => 'success', msg => $msg ); - $self->redirect_to( $self->get_referrer ); + $self->flash( msg_type => 'success', msg => $msg ); + $self->redirect_to( $self->get_referrer ); } #################################################################################### sub mark_author_to_regenerate { - my $self = shift; - my $author_id = $self->param('author_id'); - my $converter = $self->app->bibtexConverter; + my $self = shift; + my $author_id = $self->param('author_id'); + my $converter = $self->app->bibtexConverter; - my $author = $self->app->repo->authors_find( sub{$_->id == $author_id} ); + my $author = $self->app->repo->authors_find( sub { $_->id == $author_id } ); - my @entries; + my @entries; - if($author){ - $self->app->logger->info("Marking entries of author '".$author->uid."' for HTML regeneration."); + if ($author) { + $self->app->logger->info( "Marking entries of author '" + . $author->uid + . "' for HTML regeneration." ); - @entries = $author->get_entries; - foreach my $entry (@entries){ - $entry->need_html_regen(1); - } - $self->app->repo->entries_save(@entries); + @entries = $author->get_entries; + foreach my $entry (@entries) { + $entry->need_html_regen(1); } - - my $msg = "".scalar(@entries). " entries have been MARKED for regeneration. "; - $msg .= "Now you may run 'regenerate all' or 'regenerate in chunks'. "; - $msg .= "Regenration in chunks is useful for large set of entries. "; - - $self->app->logger->info($msg); - $self->flash( msg_type => 'info', msg => $msg ); - $self->redirect_to( $self->get_referrer ); + $self->app->repo->entries_save(@entries); + } + + my $msg + = "" + . scalar(@entries) + . " entries have been MARKED for regeneration. "; + $msg .= "Now you may run 'regenerate all' or 'regenerate in chunks'. "; + $msg .= "Regenration in chunks is useful for large set of entries. "; + + $self->app->logger->info($msg); + $self->flash( msg_type => 'info', msg => $msg ); + $self->redirect_to( $self->get_referrer ); } #################################################################################### sub regenerate_html_for_all { - my $self = shift; - my $converter = $self->app->bibtexConverter; + my $self = shift; + my $converter = $self->app->bibtexConverter; - $self->inactivity_timeout(3000); + $self->inactivity_timeout(3000); - $self->app->logger->info("regenerate_html_for_all is running"); + $self->app->logger->info("regenerate_html_for_all is running"); - my @entries = $self->app->repo->entries_filter( sub{$_->need_html_regen == 1} ); - my $num_regen = Fregenerate_html_for_array($self->app, 0, $converter, \@entries); - my $left_todo = scalar(@entries) - $num_regen; + my @entries + = $self->app->repo->entries_filter( sub { $_->need_html_regen == 1 } ); + my $num_regen + = Fregenerate_html_for_array( $self->app, 0, $converter, \@entries ); + my $left_todo = scalar(@entries) - $num_regen; - my $msg = "$num_regen entries have been regenerated. "; - $msg .= "$left_todo furter entries still require regeneration."; - $self->app->logger->info($msg); - $self->flash( msg_type => 'info', msg => $msg ); - $self->redirect_to( $self->get_referrer ); + my $msg = "$num_regen entries have been regenerated. "; + $msg .= "$left_todo furter entries still require regeneration."; + $self->app->logger->info($msg); + $self->flash( msg_type => 'info', msg => $msg ); + $self->redirect_to( $self->get_referrer ); } #################################################################################### sub regenerate_html_in_chunk { - my $self = shift; - my $chunk_size = $self->param('chunk_size') // 30; + my $self = shift; + my $chunk_size = $self->param('chunk_size') // 30; - my $converter = $self->app->bibtexConverter; + my $converter = $self->app->bibtexConverter; - $self->inactivity_timeout(3000); + $self->inactivity_timeout(3000); - $self->app->logger->info("regenerate_html_in_chunk is running, chunk size $chunk_size "); + $self->app->logger->info( + "regenerate_html_in_chunk is running, chunk size $chunk_size "); - my @entries = $self->app->repo->entries_filter( sub{ $_->need_html_regen == 1} ); + my @entries + = $self->app->repo->entries_filter( sub { $_->need_html_regen == 1 } ); - my $last_entry_index = $chunk_size-1; - $last_entry_index = scalar(@entries)-1 if scalar(@entries) < $chunk_size; + my $last_entry_index = $chunk_size - 1; + $last_entry_index = scalar(@entries) - 1 if scalar(@entries) < $chunk_size; - my @portion_of_entries = @entries[ 0 .. $last_entry_index ]; - @portion_of_entries = grep {defined $_} @portion_of_entries; + my @portion_of_entries = @entries[ 0 .. $last_entry_index ]; + @portion_of_entries = grep { defined $_ } @portion_of_entries; - my $num_regen = Fregenerate_html_for_array($self->app, 1, $converter, \@portion_of_entries); - my $left_todo = scalar(@entries) - $num_regen; + my $num_regen = Fregenerate_html_for_array( $self->app, 1, $converter, + \@portion_of_entries ); + my $left_todo = scalar(@entries) - $num_regen; - my $msg = "$num_regen entries have been regenerated. "; - $msg .= "$left_todo furter entries still require regeneration."; - $self->app->logger->info($msg); - $self->flash( msg_type => 'info', msg => $msg ); - $self->redirect_to( $self->get_referrer() ); + my $msg = "$num_regen entries have been regenerated. "; + $msg .= "$left_todo furter entries still require regeneration."; + $self->app->logger->info($msg); + $self->flash( msg_type => 'info', msg => $msg ); + $self->redirect_to( $self->get_referrer() ); } #################################################################################### sub mark_all_to_regenerate { - my $self = shift; - my $converter = $self->app->bibtexConverter; + my $self = shift; + my $converter = $self->app->bibtexConverter; - $self->app->logger->info("Marking all entries for HTML regeneration."); + $self->app->logger->info("Marking all entries for HTML regeneration."); - my @entries = $self->app->repo->entries_all; - foreach my $entry (@entries){ - $entry->need_html_regen(1); - # $self->app->repo->entries_save($entry); - } - $self->app->repo->entries_save(@entries); + my @entries = $self->app->repo->entries_all; + foreach my $entry (@entries) { + $entry->need_html_regen(1); - my $msg = "".scalar(@entries). " entries have been MARKED for regeneration. "; - $msg .= "Now you may run 'regenerate all' or 'regenerate in chunks'. "; - $msg .= "Regenration in chunks is useful for large set of entries. "; - $self->app->logger->info($msg); - $self->flash( msg_type => 'info', msg => $msg ); - $self->redirect_to( $self->get_referrer() ); + # $self->app->repo->entries_save($entry); + } + $self->app->repo->entries_save(@entries); + + my $msg + = "" + . scalar(@entries) + . " entries have been MARKED for regeneration. "; + $msg .= "Now you may run 'regenerate all' or 'regenerate in chunks'. "; + $msg .= "Regenration in chunks is useful for large set of entries. "; + $self->app->logger->info($msg); + $self->flash( msg_type => 'info', msg => $msg ); + $self->redirect_to( $self->get_referrer() ); } #################################################################################### sub regenerate_html { - my $self = shift; - my $converter = $self->app->bibtexConverter; - my $id = $self->param('id'); + my $self = shift; + my $converter = $self->app->bibtexConverter; + my $id = $self->param('id'); - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); - - if ( !defined $entry ) { - $self->flash( - msg => "There is no entry with id $id", - msg_type => 'danger' - ); - $self->redirect_to( $self->get_referrer ); - return; - } - my @entries = ($entry); - my $num_regen = Fregenerate_html_for_array($self->app, 1, $converter, \@entries); - - my $msg; - if($num_regen == 1){ - $msg = "$num_regen entry has been regenerated."; - } - else{ - $msg = "$num_regen entries have been regenerated."; - } - $self->app->logger->info($msg); - $self->flash( msg_type => 'info', msg => $msg ); + my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); + if ( !defined $entry ) { + $self->flash( + msg => "There is no entry with id $id", + msg_type => 'danger' + ); $self->redirect_to( $self->get_referrer ); + return; + } + my @entries = ($entry); + my $num_regen + = Fregenerate_html_for_array( $self->app, 1, $converter, \@entries ); + + my $msg; + if ( $num_regen == 1 ) { + $msg = "$num_regen entry has been regenerated."; + } + else { + $msg = "$num_regen entries have been regenerated."; + } + $self->app->logger->info($msg); + $self->flash( msg_type => 'info', msg => $msg ); + + $self->redirect_to( $self->get_referrer ); } #################################################################################### sub delete_sure { - my $self = shift; - my $id = $self->param('id'); - - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); - - if ( !defined $entry ) { - $self->app->logger->warn( - "Entry '$id' does not exist and thus can't be deleted."); - $self->flash( - mgs_type => 'danger', - msg => "There is no entry with id $id" - ); - $self->redirect_to( $self->get_referrer ); - return; - } + my $self = shift; + my $id = $self->param('id'); - $entry->delete_all_attachments; - my @entry_authorships = $entry->authorships_all; - my @entry_labelings = $entry->labelings_all; - my @entry_exceptions = $entry->exceptions_all; - $self->app->repo->authorships_delete(@entry_authorships); - $self->app->repo->labelings_delete(@entry_labelings); - $self->app->repo->exceptions_delete(@entry_exceptions); - $self->app->repo->entries_delete($entry); + my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); - $self->app->logger->info("Entry '$id' has been deleted."); + if ( !defined $entry ) { + $self->app->logger->warn( + "Entry '$id' does not exist and thus can't be deleted."); + $self->flash( + mgs_type => 'danger', + msg => "There is no entry with id $id" + ); $self->redirect_to( $self->get_referrer ); + return; + } + + $entry->delete_all_attachments; + my @entry_authorships = $entry->authorships_all; + my @entry_labelings = $entry->labelings_all; + my @entry_exceptions = $entry->exceptions_all; + $self->app->repo->authorships_delete(@entry_authorships); + $self->app->repo->labelings_delete(@entry_labelings); + $self->app->repo->exceptions_delete(@entry_exceptions); + $self->app->repo->entries_delete($entry); + + $self->app->logger->info("Entry '$id' has been deleted."); + $self->redirect_to( $self->get_referrer ); } #################################################################################### sub show_authors_of_entry { - my $self = shift; - my $id = $self->param('id'); - $self->app->logger->info("Showing authors of entry id $id"); + my $self = shift; + my $id = $self->param('id'); + $self->app->logger->info("Showing authors of entry id $id"); - my $entry = $self->app->repo->entries_find( sub { $_->{id} == $id } ); + my $entry = $self->app->repo->entries_find( sub { $_->{id} == $id } ); - if ( !defined $entry ) { - $self->flash( msg => "There is no entry with id $id" ); - $self->redirect_to( $self->get_referrer ); - return; - } + if ( !defined $entry ) { + $self->flash( msg => "There is no entry with id $id" ); + $self->redirect_to( $self->get_referrer ); + return; + } - my @authors = map { $_->author } $entry->authorships_all; - my @teams = $entry->get_teams; + my @authors = map { $_->author } $entry->authorships_all; + my @teams = $entry->get_teams; - $self->stash( entry => $entry, authors => \@authors, teams => \@teams ); - $self->render( template => 'publications/show_authors' ); + $self->stash( entry => $entry, authors => \@authors, teams => \@teams ); + $self->render( template => 'publications/show_authors' ); } #################################################################################### #################################################################################### #################################################################################### sub manage_tags { - my $self = shift; - my $id = $self->param('id'); + my $self = shift; + my $id = $self->param('id'); - $self->app->logger->info("Manage tags of entry id $id"); + $self->app->logger->info("Manage tags of entry id $id"); - my $entry = $self->app->repo->entries_find( sub { $_->{id} == $id } ); + my $entry = $self->app->repo->entries_find( sub { $_->{id} == $id } ); - if ( !defined $entry ) { - $self->flash( msg => "There is no entry with id $id" ); - $self->redirect_to( $self->get_referrer ); - return; - } + if ( !defined $entry ) { + $self->flash( msg => "There is no entry with id $id" ); + $self->redirect_to( $self->get_referrer ); + return; + } - my @tags = $entry->get_tags; - my @tag_types = $self->app->repo->tagTypes_all; + my @tags = $entry->get_tags; + my @tag_types = $self->app->repo->tagTypes_all; - $self->stash( entry => $entry, tags => \@tags, tag_types => \@tag_types ); - $self->render( template => 'publications/manage_tags' ); + $self->stash( entry => $entry, tags => \@tags, tag_types => \@tag_types ); + $self->render( template => 'publications/manage_tags' ); } #################################################################################### sub remove_tag { - my $self = shift; - my $entry_id = $self->param('eid'); - my $tag_id = $self->param('tid'); + my $self = shift; + my $entry_id = $self->param('eid'); + my $tag_id = $self->param('tid'); - my $entry = $self->app->repo->entries_find( sub { $_->id == $entry_id } ); - my $tag = $self->app->repo->tags_find( sub { $_->id == $tag_id } ); + my $entry = $self->app->repo->entries_find( sub { $_->id == $entry_id } ); + my $tag = $self->app->repo->tags_find( sub { $_->id == $tag_id } ); - if ( defined $entry and defined $tag ) { + if ( defined $entry and defined $tag ) { - my $search_label = Labeling->new( - entry => $entry, - tag => $tag, - entry_id => $entry->id, - tag_id => $tag->id + my $search_label = Labeling->new( + entry => $entry, + tag => $tag, + entry_id => $entry->id, + tag_id => $tag->id + ); + + my $label + = $self->app->repo->labelings_find( sub { $_->equals($search_label) } ); - my $label = $self->app->repo->labelings_find( - sub { $_->equals($search_label) } ); + if ($label) { - if ($label) { + ## you should always execute all those three commands together - smells like command pattern... + $entry->remove_labeling($label); + $tag->remove_labeling($label); + $self->app->repo->labelings_delete($label); - ## you should always execute all those three commands together - smells like command pattern... - $entry->remove_labeling($label); - $tag->remove_labeling($label); - $self->app->repo->labelings_delete($label); + $self->app->logger->info( + "Removed tag " . $tag->name . " from entry ID " . $entry->id . ". " ); + } + else { + # this paper does not have this tag - do nothing + $self->app->logger->warn( "Cannot remove tag " + . $tag->name + . " from entry ID " + . $entry->id + . " - reason: labeling not found. " ); + } - $self->app->logger->info( "Removed tag " - . $tag->name - . " from entry ID " - . $entry->id - . ". " ); - } - else { - # this paper does not have this tag - do nothing - $self->app->logger->warn( "Cannot remove tag " - . $tag->name - . " from entry ID " - . $entry->id - . " - reason: labeling not found. " ); - } + } - } - - $self->redirect_to( $self->get_referrer ); + $self->redirect_to( $self->get_referrer ); } #################################################################################### sub add_tag { - my $self = shift; - my $entry_id = $self->param('eid'); - my $tag_id = $self->param('tid'); - - my $entry = $self->app->repo->entries_find( sub { $_->id == $entry_id } ); - my $tag = $self->app->repo->tags_find( sub { $_->id == $tag_id } ); - - if ( defined $entry and defined $tag ) { - my $label = Labeling->new( - entry => $entry, - tag => $tag, - entry_id => $entry->id, - tag_id => $tag->id - ); - ## you should always execute all those three commands together - smells like command pattern... - - $self->app->repo->labelings_save($label); - $entry->add_labeling($label); - $tag->add_labeling($label); - } - else { - # this paper does not have this tag - do nothing - $self->app->logger->warn( - "Cannot add tag $tag_id to entry ID $entry_id - reason: tag or entry not found. " - ); - } + my $self = shift; + my $entry_id = $self->param('eid'); + my $tag_id = $self->param('tid'); + + my $entry = $self->app->repo->entries_find( sub { $_->id == $entry_id } ); + my $tag = $self->app->repo->tags_find( sub { $_->id == $tag_id } ); + + if ( defined $entry and defined $tag ) { + my $label = Labeling->new( + entry => $entry, + tag => $tag, + entry_id => $entry->id, + tag_id => $tag->id + ); + ## you should always execute all those three commands together - smells like command pattern... + + $self->app->repo->labelings_save($label); + $entry->add_labeling($label); + $tag->add_labeling($label); + } + else { + # this paper does not have this tag - do nothing + $self->app->logger->warn( + "Cannot add tag $tag_id to entry ID $entry_id - reason: tag or entry not found. " + ); + } - $self->redirect_to( $self->get_referrer ); + $self->redirect_to( $self->get_referrer ); } #################################################################################### #################################################################################### #################################################################################### sub manage_exceptions { - my $self = shift; - my $id = $self->param('id'); + my $self = shift; + my $id = $self->param('id'); - my $entry = $self->app->repo->entries_find( sub { $_->{id} == $id } ); + my $entry = $self->app->repo->entries_find( sub { $_->{id} == $id } ); - if ( !defined $entry ) { - $self->flash( msg => "There is no entry with id $id" ); - $self->redirect_to( $self->get_referrer ); - return; - } + if ( !defined $entry ) { + $self->flash( msg => "There is no entry with id $id" ); + $self->redirect_to( $self->get_referrer ); + return; + } - my @exceptions = $entry->exceptions_all; - my @all_teams = $self->app->repo->teams_all; - my @teams = $entry->get_teams; - my @authors = $entry->get_authors; + my @exceptions = $entry->exceptions_all; + my @all_teams = $self->app->repo->teams_all; + my @teams = $entry->get_teams; + my @authors = $entry->get_authors; - # cannot use objects as keysdue to stringification! - my %exceptions_hash = map { $_->team->id => 1 } @exceptions; - my @unassigned_teams = grep { not $exceptions_hash{ $_->id } } @all_teams; + # cannot use objects as keysdue to stringification! + my %exceptions_hash = map { $_->team->id => 1 } @exceptions; + my @unassigned_teams = grep { not $exceptions_hash{ $_->id } } @all_teams; - $self->stash( - entry => $entry, - exceptions => \@exceptions, - teams => \@teams, - all_teams => \@all_teams, - authors => \@authors, - unassigned_teams => \@unassigned_teams - ); - $self->render( template => 'publications/manage_exceptions' ); + $self->stash( + entry => $entry, + exceptions => \@exceptions, + teams => \@teams, + all_teams => \@all_teams, + authors => \@authors, + unassigned_teams => \@unassigned_teams + ); + $self->render( template => 'publications/manage_exceptions' ); } #################################################################################### sub add_exception { - my $self = shift; - my $entry_id = $self->param('eid'); - my $team_id = $self->param('tid'); + my $self = shift; + my $entry_id = $self->param('eid'); + my $team_id = $self->param('tid'); - my $msg; - my $entry = $self->app->repo->entries_find( sub { $_->id == $entry_id } ); - my $team = $self->app->repo->teams_find( sub { $_->id == $team_id } ); + my $msg; + my $entry = $self->app->repo->entries_find( sub { $_->id == $entry_id } ); + my $team = $self->app->repo->teams_find( sub { $_->id == $team_id } ); - if ( defined $entry and defined $team ) { + if ( defined $entry and defined $team ) { - my $exception = Exception->new( - entry => $entry, - team => $team, - entry_id => $entry->id, - team_id => $team->id - ); - - $entry->add_exception($exception); - $team->add_exception($exception); - $self->app->repo->exceptions_save($exception); - - $msg - = "Exception added! Entry ID " - . $entry->id - . " will be now listed under team '" - . $team->name . "'."; - } - else { - $msg - = "Cannot find entry or team to create exception. Searched team ID: " - . $team_id - . " entry ID: " - . $entry_id . "."; - } + my $exception = Exception->new( + entry => $entry, + team => $team, + entry_id => $entry->id, + team_id => $team->id + ); - $self->flash( msg => $msg ); - $self->app->logger->info($msg); - $self->redirect_to( $self->get_referrer ); + $entry->add_exception($exception); + $team->add_exception($exception); + $self->app->repo->exceptions_save($exception); + + $msg + = "Exception added! Entry ID " + . $entry->id + . " will be now listed under team '" + . $team->name . "'."; + } + else { + $msg + = "Cannot find entry or team to create exception. Searched team ID: " + . $team_id + . " entry ID: " + . $entry_id . "."; + } + + $self->flash( msg => $msg ); + $self->app->logger->info($msg); + $self->redirect_to( $self->get_referrer ); } #################################################################################### sub remove_exception { - my $self = shift; - my $entry_id = $self->param('eid'); - my $team_id = $self->param('tid'); + my $self = shift; + my $entry_id = $self->param('eid'); + my $team_id = $self->param('tid'); - my $entry = $self->app->repo->entries_find( sub { $_->id == $entry_id } ); - my $team = $self->app->repo->teams_find( sub { $_->id == $team_id } ); + my $entry = $self->app->repo->entries_find( sub { $_->id == $entry_id } ); + my $team = $self->app->repo->teams_find( sub { $_->id == $team_id } ); - my $msg; + my $msg; - if ( defined $entry and defined $team ) { + if ( defined $entry and defined $team ) { - my $ex = Exception->new( - team_id => $team_id, - entry_id => $entry_id, - team => $team, - entry => $entry - ); + my $ex = Exception->new( + team_id => $team_id, + entry_id => $entry_id, + team => $team, + entry => $entry + ); - my $exception - = $self->app->repo->exceptions_find( sub { $_->equals($ex) } ); - - if ( defined $exception ) { - $entry->remove_exception($exception); - $team->remove_exception($exception); - $self->app->repo->exceptions_delete($exception); - - $msg - = "Removed exception team '" - . $team->name - . "' from entry ID " - . $entry->id . ". "; - } - else { - $msg - = "Cannot find exception to remove. Searched team '" - . $team->name - . "' entry ID: " - . $entry->id . "."; - } + my $exception + = $self->app->repo->exceptions_find( sub { $_->equals($ex) } ); + + if ( defined $exception ) { + $entry->remove_exception($exception); + $team->remove_exception($exception); + $self->app->repo->exceptions_delete($exception); + + $msg + = "Removed exception team '" + . $team->name + . "' from entry ID " + . $entry->id . ". "; } else { - $msg - = "Cannot find exception to remove. Searched team ID: " - . $team_id - . " entry ID: " - . $entry_id . "."; + $msg + = "Cannot find exception to remove. Searched team '" + . $team->name + . "' entry ID: " + . $entry->id . "."; } + } + else { + $msg + = "Cannot find exception to remove. Searched team ID: " + . $team_id + . " entry ID: " + . $entry_id . "."; + } - $self->flash( msg => $msg ); - $self->app->logger->info($msg); + $self->flash( msg => $msg ); + $self->app->logger->info($msg); - $self->redirect_to( $self->get_referrer ); + $self->redirect_to( $self->get_referrer ); } #################################################################################### #################################################################################### #################################################################################### sub get_adding_editing_message_for_error_code { - my $self = shift; - my $exit_code = shift; - my $existing_id = shift || -1; - - # -1 You have bibtex errors! Not saving!"; - # -2 Displaying preview'; - # 0 Entry added successfully'; - # 1 Entry updated successfully'; - # 2 The proposed key is OK. - # 3 Proposed key exists already - HTML message - - if ( $exit_code eq 'ERR_BIBTEX' ) { - return - "You have bibtex errors! No changes were written to the database."; - } - elsif ( $exit_code eq 'PREVIEW' ) { - return 'Displaying preview. No changes were written to the database.'; - } - elsif ( $exit_code eq 'ADD_OK' ) { - return 'Entry added successfully. Switched to editing mode.'; - } - elsif ( $exit_code eq 'EDIT_OK' ) { - return 'Entry updated successfully.'; - } - elsif ( $exit_code eq 'KEY_OK' ) { - return - 'The proposed key is OK. You may continue with your edits. No changes were written to the database.'; - } - elsif ( $exit_code eq 'KEY_TAKEN' ) { - return - 'The proposed key exists already in DB under ID . + my $self = shift; + my $exit_code = shift; + my $existing_id = shift || -1; + + # -1 You have bibtex errors! Not saving!"; + # -2 Displaying preview'; + # 0 Entry added successfully'; + # 1 Entry updated successfully'; + # 2 The proposed key is OK. + # 3 Proposed key exists already - HTML message + + if ( $exit_code eq 'ERR_BIBTEX' ) { + return "You have bibtex errors! No changes were written to the database."; + } + elsif ( $exit_code eq 'PREVIEW' ) { + return 'Displaying preview. No changes were written to the database.'; + } + elsif ( $exit_code eq 'ADD_OK' ) { + return 'Entry added successfully. Switched to editing mode.'; + } + elsif ( $exit_code eq 'EDIT_OK' ) { + return 'Entry updated successfully.'; + } + elsif ( $exit_code eq 'KEY_OK' ) { + return + 'The proposed key is OK. You may continue with your edits. No changes were written to the database.'; + } + elsif ( $exit_code eq 'KEY_TAKEN' ) { + return + 'The proposed key exists already in DB under ID .
Show me the existing entry ID ' - . $existing_id - . ' in a new window + . $self->url_for( 'edit_publication', id => $existing_id ) + . '" target="_blank">Show me the existing entry ID ' + . $existing_id + . ' in a new window
Entry has not been saved. Please pick another BibTeX key. No changes were written to the database.'; - } - elsif ( defined $exit_code and $exit_code ne '' ) { - return "Unknown exit code: $exit_code"; - } + } + elsif ( defined $exit_code and $exit_code ne '' ) { + return "Unknown exit code: $exit_code"; + } } #################################################################################### sub publications_add_get { - my $self = shift; - $self->app->logger->info("Open Add Publication"); + my $self = shift; + $self->app->logger->info("Open Add Publication"); - my $msg = "Adding mode You operate on an unsaved entry!"; + my $msg = "Adding mode You operate on an unsaved entry!"; - my $bib = '@article{key' . get_current_year() . ', + my $bib = '@article{key' . get_current_year() . ', author = {Johny Example}, journal = {Journal of this and that}, publisher = {Printer-at-home publishing}, @@ -1237,229 +1245,231 @@ sub publications_add_get { day = {1--31}, }'; - my $e_dummy = $self->app->entityFactory->new_Entry( bib => $bib ); + my $e_dummy = $self->app->entityFactory->new_Entry( bib => $bib ); - $e_dummy->populate_from_bib(); - $e_dummy->generate_html( $self->app->bst, $self->app->bibtexConverter ); + $e_dummy->populate_from_bib(); + $e_dummy->generate_html( $self->app->bst, $self->app->bibtexConverter ); - $self->stash( entry => $e_dummy, msg => $msg ); - $self->render( template => 'publications/add_entry' ); + $self->stash( entry => $e_dummy, msg => $msg ); + $self->render( template => 'publications/add_entry' ); } #################################################################################### sub publications_add_post { - my $self = shift; - my $new_bib = $self->param('new_bib'); - my $param_prev = $self->param('preview'); - my $param_save = $self->param('save'); - my $param_check_key = $self->param('check_key'); - - my $action = 'default'; - $action = 'save' if $param_save; # user clicks save - $action = 'preview' if $param_prev; # user clicks preview - $action = 'check_key' if $param_check_key; # user clicks check key - - $self->app->logger->info("Adding publication. Action: > $action <."); - - $new_bib =~ s/^\s+|\s+$//g; - $new_bib =~ s/^\t//g; - - my $status_code_str; - my $existing_id = -1; - my $added_under_id = -1; - - # status_code_strings - # -2 => PREVIEW - # -1 => ERR_BIBTEX - # 0 => ADD_OK - # 1 => EDIT_OK - # 2 => KEY_OK - # 3 => KEY_TAKEN - - - my $entry = $self->app->entityFactory->new_Entry( bib => $new_bib ); - - # any action - if ( !$entry->has_valid_bibtex ) { - $status_code_str = 'ERR_BIBTEX'; - my $msg = get_adding_editing_message_for_error_code( $self, - $status_code_str, $existing_id ); - my $msg_type = 'danger'; - - $self->app->logger->info( - "Adding publication. Action: > $action <. Status code: $status_code_str." - ); - $self->stash( entry => $entry, msg => $msg, msg_type => $msg_type ); - $self->render( template => 'publications/add_entry' ); - return; - } + my $self = shift; + my $new_bib = $self->param('new_bib'); + my $param_prev = $self->param('preview'); + my $param_save = $self->param('save'); + my $param_check_key = $self->param('check_key'); - $entry->generate_html( $self->app->bst, $self->app->bibtexConverter ); - my $bibtex_warnings = FprintBibtexWarnings( $entry->warnings ); - - # any action - my $existing_entry = $self->app->repo->entries_find( - sub { $_->bibtex_key eq $entry->bibtex_key } ); - if ($existing_entry) { - $status_code_str = 'KEY_TAKEN'; - my $msg_type = 'danger'; - $existing_id = $existing_entry->id; - my $msg = get_adding_editing_message_for_error_code( $self, - $status_code_str, $existing_id ); - - $self->app->logger->info( - "Adding publication. Action: > $action <. Status code: $status_code_str." - ); - $self->stash( entry => $entry, msg => $msg, msg_type => $msg_type ); - $self->render( template => 'publications/add_entry' ); - return; - } + my $action = 'default'; + $action = 'save' if $param_save; # user clicks save + $action = 'preview' if $param_prev; # user clicks preview + $action = 'check_key' if $param_check_key; # user clicks check key + $self->app->logger->info("Adding publication. Action: > $action <."); - if ( $action eq 'preview' or $action eq 'check_key' ) { - my $status_code_str = 'PREVIEW'; - my $msg_type = 'info'; - $msg_type = 'warning' if $bibtex_warnings; - my $msg = get_adding_editing_message_for_error_code( $self, - $status_code_str, $existing_id ); - $msg .= $bibtex_warnings; + $new_bib =~ s/^\s+|\s+$//g; + $new_bib =~ s/^\t//g; - $self->app->logger->info( - "Adding publication. Action: > $action <. Status code: $status_code_str." - ); - $self->stash( entry => $entry, msg => $msg, msg_type => $msg_type ); - $self->render( template => 'publications/add_entry' ); - return; - } + my $status_code_str; + my $existing_id = -1; + my $added_under_id = -1; + # status_code_strings + # -2 => PREVIEW + # -1 => ERR_BIBTEX + # 0 => ADD_OK + # 1 => EDIT_OK + # 2 => KEY_OK + # 3 => KEY_TAKEN - if ( $action eq 'save' ) { - $status_code_str = 'ADD_OK'; - $entry->fix_month(); - $entry->generate_html( $self->app->bst, $self->app->bibtexConverter ); + my $entry = $self->app->entityFactory->new_Entry( bib => $new_bib ); - $self->app->repo->entries_save($entry); - $added_under_id = $entry->id; - - ## !!! the entry must be added before executing Freassign_authors_to_entries_given_by_array - ## why? beacuse authorship will be unable to map existing entry to the author - Freassign_authors_to_entries_given_by_array( $self->app, 1, - [$entry] ); + # any action + if ( !$entry->has_valid_bibtex ) { + $status_code_str = 'ERR_BIBTEX'; + my $msg + = get_adding_editing_message_for_error_code( $self, $status_code_str, + $existing_id ); + my $msg_type = 'danger'; + $self->app->logger->info( + "Adding publication. Action: > $action <. Status code: $status_code_str." + ); + $self->stash( entry => $entry, msg => $msg, msg_type => $msg_type ); + $self->render( template => 'publications/add_entry' ); + return; + } + + $entry->generate_html( $self->app->bst, $self->app->bibtexConverter ); + my $bibtex_warnings = FprintBibtexWarnings( $entry->warnings ); + + # any action + my $existing_entry = $self->app->repo->entries_find( + sub { $_->bibtex_key eq $entry->bibtex_key } ); + if ($existing_entry) { + $status_code_str = 'KEY_TAKEN'; + my $msg_type = 'danger'; + $existing_id = $existing_entry->id; + my $msg + = get_adding_editing_message_for_error_code( $self, $status_code_str, + $existing_id ); - my $msg_type = 'success'; - $msg_type = 'warning' if $bibtex_warnings; - my $msg = get_adding_editing_message_for_error_code( $self, - $status_code_str, $existing_id ); - $msg .= $bibtex_warnings; + $self->app->logger->info( + "Adding publication. Action: > $action <. Status code: $status_code_str." + ); + $self->stash( entry => $entry, msg => $msg, msg_type => $msg_type ); + $self->render( template => 'publications/add_entry' ); + return; + } - $self->app->logger->info( - "Adding publication. Action: > $action <. Status code: $status_code_str." - ); - $self->flash( msg => $msg, msg_type => $msg_type ); - $self->redirect_to( - $self->url_for( 'edit_publication', id => $added_under_id ) ); - return; - } + if ( $action eq 'preview' or $action eq 'check_key' ) { + my $status_code_str = 'PREVIEW'; + my $msg_type = 'info'; + $msg_type = 'warning' if $bibtex_warnings; + my $msg + = get_adding_editing_message_for_error_code( $self, $status_code_str, + $existing_id ); + $msg .= $bibtex_warnings; -} -#################################################################################### -sub publications_edit_get { - my $self = shift; - my $id = $self->param('id') || -1; + $self->app->logger->info( + "Adding publication. Action: > $action <. Status code: $status_code_str." + ); + $self->stash( entry => $entry, msg => $msg, msg_type => $msg_type ); + $self->render( template => 'publications/add_entry' ); + return; + } - $self->app->logger->info("Editing publication entry id $id"); - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); + if ( $action eq 'save' ) { - if ( !defined $entry ) { - $self->flash( msg => "There is no entry with id $id" ); - $self->redirect_to( $self->get_referrer ); - return; - } - $entry->populate_from_bib(); + $status_code_str = 'ADD_OK'; + $entry->fix_month(); $entry->generate_html( $self->app->bst, $self->app->bibtexConverter ); - $self->stash( entry => $entry ); - $self->render( template => 'publications/edit_entry' ); -} -#################################################################################### -sub publications_edit_post { - my $self = shift; - my $id = $self->param('id') // -1; - my $new_bib = $self->param('new_bib'); - my $param_prev = $self->param('preview'); - my $param_save = $self->param('save'); - my $param_check_key = $self->param('check_key'); - - my $action = 'save'; # user clicks save - $action = 'preview' if $self->param('preview'); # user clicks preview - $action = 'check_key' - if $self->param('check_key'); # user clicks check key - - $self->app->logger->info( - "Editing publication id $id. Action: > $action <."); + $self->app->repo->entries_save($entry); + $added_under_id = $entry->id; - $new_bib =~ s/^\s+|\s+$//g; - $new_bib =~ s/^\t//g; + ## !!! the entry must be added before executing Freassign_authors_to_entries_given_by_array + ## why? beacuse authorship will be unable to map existing entry to the author + Freassign_authors_to_entries_given_by_array( $self->app, 1, [$entry] ); - my ( $mentry, $status_code_str, $existing_id, $added_under_id ) - = Fhandle_add_edit_publication( $self->app, $new_bib, $id, $action, - $self->app->bst ); - my $adding_msg + my $msg_type = 'success'; + $msg_type = 'warning' if $bibtex_warnings; + my $msg = get_adding_editing_message_for_error_code( $self, $status_code_str, - $existing_id ); + $existing_id ); + $msg .= $bibtex_warnings; $self->app->logger->info( - "Editing publication id $id. Action: > $action <. Status code: $status_code_str." + "Adding publication. Action: > $action <. Status code: $status_code_str." ); + $self->flash( msg => $msg, msg_type => $msg_type ); + $self->redirect_to( + $self->url_for( 'edit_publication', id => $added_under_id ) ); + return; + } + - # status_code_strings - # -2 => PREVIEW - # -1 => ERR_BIBTEX - # 0 => ADD_OK - # 1 => EDIT_OK - # 2 => KEY_OK - # 3 => KEY_TAKEN - - my $bibtex_warnings = FprintBibtexWarnings( $mentry->warnings ); - my $msg = $adding_msg . $bibtex_warnings; - my $msg_type = 'success'; - $msg_type = 'warning' if $bibtex_warnings =~ m/Warning/; - $msg_type = 'danger' - if $status_code_str eq 'ERR_BIBTEX' - or $status_code_str eq 'KEY_TAKEN' - or $bibtex_warnings =~ m/Error/; - - $self->stash( entry => $mentry, msg => $msg, msg_type => $msg_type ); - $self->render( template => 'publications/edit_entry' ); +} +#################################################################################### +sub publications_edit_get { + my $self = shift; + my $id = $self->param('id') || -1; + + $self->app->logger->info("Editing publication entry id $id"); + + my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); + + if ( !defined $entry ) { + $self->flash( msg => "There is no entry with id $id" ); + $self->redirect_to( $self->get_referrer ); + return; + } + $entry->populate_from_bib(); + $entry->generate_html( $self->app->bst, $self->app->bibtexConverter ); + + $self->stash( entry => $entry ); + $self->render( template => 'publications/edit_entry' ); +} +#################################################################################### +sub publications_edit_post { + my $self = shift; + my $id = $self->param('id') // -1; + my $new_bib = $self->param('new_bib'); + my $param_prev = $self->param('preview'); + my $param_save = $self->param('save'); + my $param_check_key = $self->param('check_key'); + + my $action = 'save'; # user clicks save + $action = 'preview' if $self->param('preview'); # user clicks preview + $action = 'check_key' if $self->param('check_key'); # user clicks check key + + $self->app->logger->info( + "Editing publication id $id. Action: > $action <."); + + $new_bib =~ s/^\s+|\s+$//g; + $new_bib =~ s/^\t//g; + + + my ( $mentry, $status_code_str, $existing_id, $added_under_id ) + = Fhandle_add_edit_publication( $self->app, $new_bib, $id, $action, + $self->app->bst ); + my $adding_msg + = get_adding_editing_message_for_error_code( $self, $status_code_str, + $existing_id ); + + $self->app->logger->info( + "Editing publication id $id. Action: > $action <. Status code: $status_code_str." + ); + + # status_code_strings + # -2 => PREVIEW + # -1 => ERR_BIBTEX + # 0 => ADD_OK + # 1 => EDIT_OK + # 2 => KEY_OK + # 3 => KEY_TAKEN + + my $bibtex_warnings = FprintBibtexWarnings( $mentry->warnings ); + my $msg = $adding_msg . $bibtex_warnings; + my $msg_type = 'success'; + $msg_type = 'warning' if $bibtex_warnings =~ m/Warning/; + $msg_type = 'danger' + if $status_code_str eq 'ERR_BIBTEX' + or $status_code_str eq 'KEY_TAKEN' + or $bibtex_warnings =~ m/Error/; + + $self->stash( entry => $mentry, msg => $msg, msg_type => $msg_type ); + $self->render( template => 'publications/edit_entry' ); } #################################################################################### sub clean_ugly_bibtex { - my $self = shift; + my $self = shift; - # TODO: put this into config or preferences! - my @fields_to_clean - = qw(bdsk-url-1 bdsk-url-2 bdsk-url-3 date-added date-modified owner tags); + # TODO: put this into config or preferences! + my @fields_to_clean + = qw(bdsk-url-1 bdsk-url-2 bdsk-url-3 date-added date-modified owner tags); - $self->app->logger->info("Cleaning ugly Bibtex fields for all entries"); + $self->app->logger->info("Cleaning ugly Bibtex fields for all entries"); - my @entries = $self->app->repo->entries_all; - my $num_removed = 0; - foreach my $entry (@entries) { - $num_removed = $num_removed - + $entry->clean_ugly_bibtex_fields( \@fields_to_clean ); - } + my @entries = $self->app->repo->entries_all; + my $num_removed = 0; + foreach my $entry (@entries) { + $num_removed = $num_removed + + $entry->clean_ugly_bibtex_fields( \@fields_to_clean ); + } - $self->flash( - msg_type => 'info', - msg => - "All entries have now their Bibtex cleaned. I have removed $num_removed fields." - ); + $self->flash( + msg_type => 'info', + msg => + "All entries have now their Bibtex cleaned. I have removed $num_removed fields." + ); - $self->redirect_to( $self->get_referrer ); + $self->redirect_to( $self->get_referrer ); } #################################################################################### 1; diff --git a/lib/BibSpace/Controller/PublicationsExperimental.pm b/lib/BibSpace/Controller/PublicationsExperimental.pm index 4595e93..c4ee017 100644 --- a/lib/BibSpace/Controller/PublicationsExperimental.pm +++ b/lib/BibSpace/Controller/PublicationsExperimental.pm @@ -4,7 +4,6 @@ use Data::Dumper; use utf8; use Text::BibTeX; # parsing bib files use DateTime; -# use File::Slurp; # should be replaced in the future use Path::Tiny; # for creating directories use Try::Tiny; diff --git a/lib/BibSpace/Controller/PublicationsLanding.pm b/lib/BibSpace/Controller/PublicationsLanding.pm index 500d892..224eaab 100644 --- a/lib/BibSpace/Controller/PublicationsLanding.pm +++ b/lib/BibSpace/Controller/PublicationsLanding.pm @@ -5,7 +5,6 @@ use utf8; use Text::BibTeX; # parsing bib files use DateTime; -# use File::Slurp; # should be replaced in the future use Path::Tiny; # for creating directories use Try::Tiny; diff --git a/lib/BibSpace/Functions/Core.pm b/lib/BibSpace/Functions/Core.pm index e84718e..38a2ace 100644 --- a/lib/BibSpace/Functions/Core.pm +++ b/lib/BibSpace/Functions/Core.pm @@ -6,6 +6,7 @@ use Data::Dumper; use utf8; use Text::BibTeX; # parsing bib files use DateTime; + # use File::Slurp; use File::Find; @@ -17,7 +18,9 @@ use warnings; ### Security use Crypt::Eksblowfish::Bcrypt qw(bcrypt bcrypt_hash en_base64); -use Crypt::Random; + +# not crypto secure, but causes less problems than Crypt::Random; +use Bytes::Random; use Session::Token; ### Posting to Mailgun @@ -39,266 +42,271 @@ use List::MoreUtils qw(any uniq); # these are exported by default. our @EXPORT = qw( - sort_publications - fix_bibtex_national_characters - get_dir_size - validate_registration_data - check_password_policy - generate_token - encrypt_password - salt - check_password - send_email - split_bibtex_entries - decodeLatex - official_bibtex_types - random_string - create_user_id - uniqlc - get_generic_type_description - nohtml - clean_tag_name - get_month_numeric - get_current_year - get_current_month + sort_publications + fix_bibtex_national_characters + get_dir_size + validate_registration_data + check_password_policy + generate_token + encrypt_password + salt + check_password + send_email + split_bibtex_entries + decodeLatex + official_bibtex_types + random_string + create_user_id + uniqlc + get_generic_type_description + nohtml + clean_tag_name + get_month_numeric + get_current_year + get_current_month ); #################################################################################################### sub sort_publications { my (@pubs) = @_; - return reverse sort{ - $a->year <=> $b->year || - $a->month <=> $b->month || - $a->bibtex_key cmp $b->bibtex_key || - $a->id cmp $b->id + return reverse sort { + $a->year <=> $b->year + || $a->month <=> $b->month + || $a->bibtex_key cmp $b->bibtex_key + || $a->id cmp $b->id } @pubs; } #################################################################################################### + =item fix_bibtex_national_characters This function should help to avoid bibtex=>html warnings of BibStyle, like this: line 5, warning: found " at brace-depth zero in string (TeX accents in BibTeX should be inside braces) =cut + sub fix_bibtex_national_characters { - my $str = shift; - - # matches / not followed by bob: /^\/(?!bob\/)/ - # matches a word that follows a tab /(?<=\t)\w+/ + my $str = shift; - # s/(foo)bar/$1/g; - removes bar after foo - # s/foo\Kbar//g; - removes bar after foo + # matches / not followed by bob: /^\/(?!bob\/)/ + # matches a word that follows a tab /(?<=\t)\w+/ - # /(? sth {\'x} sth - $str =~ s/(? sth {\'x} sth - $str =~ s/\\'\{(\w+)\}/\{\\'$1\}/g; + # makes sth \'x sth --> sth {\'x} sth + $str =~ s/(? sth {\"x} sth - $str =~ s/\\"\{(\w+)\}/\{\\"$1\}/g; + # makes sth \'{x} sth --> sth {\'x} sth + $str =~ s/\\'\{(\w+)\}/\{\\'$1\}/g; - # makes sth \"x sth --> sth {\"x} sth - $str =~ s/(? sth {\"x} sth + $str =~ s/\\"\{(\w+)\}/\{\\"$1\}/g; - # makes sth \^x sth --> sth {\^x} sth - $str =~ s/(? sth {\"x} sth + $str =~ s/(? sth {\~x} sth - $str =~ s/(? sth {\^x} sth + $str =~ s/(? sth {\aa} sth - $str =~ s/(? sth {\~x} sth + $str =~ s/(? sth {\l} sth - $str =~ s/(? sth {\aa} sth + $str =~ s/(? sth {\ss} sth - $str =~ s/(? sth {\l} sth + $str =~ s/(? sth {\ss} sth + $str =~ s/(? 3; - return; + my $pass = shift; + return 1 if length($pass) > 3; + return; } #################################################################################################### sub generate_token { - my $self = shift; - my $token = Session::Token->new( length => 32 )->get; - return $token + my $self = shift; + my $token = Session::Token->new( length => 32 )->get; + return $token; } #################################################################################################### sub encrypt_password { - my $password = shift; - my $salt = shift || salt(); - # Generate a salt if one is not passed - - # Set the cost to 8 and append a NULL - my $settings = '$2a$08$' . $salt; - # Encrypt it - return Crypt::Eksblowfish::Bcrypt::bcrypt( $password, $settings ); + my $password = shift; + my $salt = shift || salt(); + + # Generate a salt if one is not passed + + # Set the cost to 8 and append a NULL + my $settings = '$2a$08$' . $salt; + + # Encrypt it + return Crypt::Eksblowfish::Bcrypt::bcrypt( $password, $settings ); } #################################################################################################### sub salt { - return Crypt::Eksblowfish::Bcrypt::en_base64( - Crypt::Random::makerandom_octet( Length => 16 ) ); + return Crypt::Eksblowfish::Bcrypt::en_base64( random_bytes(16) ); } #################################################################################################### sub check_password { - my $plain_password = shift; - my $hashed_password = shift; - - return if !defined $plain_password or $plain_password eq ''; - - # Regex to extract the salt - if ( $hashed_password =~ m!^\$2a\$\d{2}\$([A-Za-z0-9+\\.\/]{22})! ) { - # Use a letter by letter match rather than a complete string match to avoid timing attacks - my $match = encrypt_password( $plain_password, $1 ); - for ( my $n = 0; $n < length $match; $n++ ) { - if( substr( $match, $n, 1 ) ne substr( $hashed_password, $n, 1 ) ){ - return; - } - } - return 1; + my $plain_password = shift; + my $hashed_password = shift; + + return if !defined $plain_password or $plain_password eq ''; + + # Regex to extract the salt + if ( $hashed_password =~ m!^\$2a\$\d{2}\$([A-Za-z0-9+\\.\/]{22})! ) { + +# Use a letter by letter match rather than a complete string match to avoid timing attacks + my $match = encrypt_password( $plain_password, $1 ); + for ( my $n = 0; $n < length $match; $n++ ) { + if ( substr( $match, $n, 1 ) ne substr( $hashed_password, $n, 1 ) ) + { + return; + } } - return; + return 1; + } + return; } #################################################################################################### #################################################################################################### -sub send_email { - my $config = shift; - - - - my $uri = "https://api.mailgun.net/v3/".$config->{mailgun_domain}."/messages"; - - my $mech = WWW::Mechanize->new( ssl_opts => { SSL_version => 'TLSv1' } ); - $mech->credentials( api => $config->{mailgun_key} ); - $mech->ssl_opts( verify_hostname => 0 ); - $mech->post( $uri, - [ from => $config->{from}, - to => $config->{to}, - subject => $config->{subject}, - html => $config->{content} - ] - ); +sub send_email { + my $config = shift; + + + my $uri + = "https://api.mailgun.net/v3/" + . $config->{mailgun_domain} + . "/messages"; + + my $mech = WWW::Mechanize->new( ssl_opts => { SSL_version => 'TLSv1' } ); + $mech->credentials( api => $config->{mailgun_key} ); + $mech->ssl_opts( verify_hostname => 0 ); + $mech->post( + $uri, + [ from => $config->{from}, + to => $config->{to}, + subject => $config->{subject}, + html => $config->{content} + ] + ); } #################################################################################### sub split_bibtex_entries { - my $input = shift; - - my @bibtex_codes = (); - $input =~ s/^\s*$//g; - $input =~ s/^\s+|\s+$//g; - $input =~ s/^\t+//g; - - - for my $b_code ( split /@/, $input ) { - # skip bad splitting :P - next if length($b_code) < 10; - my $entry_code = "@".$b_code; - - push @bibtex_codes, $entry_code; - } + my $input = shift; + + my @bibtex_codes = (); + $input =~ s/^\s*$//g; + $input =~ s/^\s+|\s+$//g; + $input =~ s/^\t+//g; + - return @bibtex_codes; + for my $b_code ( split /@/, $input ) { + + # skip bad splitting :P + next if length($b_code) < 10; + my $entry_code = "@" . $b_code; + + push @bibtex_codes, $entry_code; + } + + return @bibtex_codes; } ################################################################################ sub decodeLatex { - my $str = shift; - - use TeX::Encode; - $str = decode( 'latex', $str ); - - $str =~ s/\{(\w)\}/$1/g; # makes {x} -> x - $str =~ s/\{\\\"(u)\}/ü/g; # makes {\"x} -> xe - $str =~ s/\{\\\"(U)\}/Ü/g; # makes {\"x} -> xe - $str =~ s/\{\\\"(o)\}/ö/g; # makes {\"x} -> xe - $str =~ s/\{\\\"(O)\}/Ö/g; # makes {\"x} -> xe - $str =~ s/\{\\\"(a)\}/ä/g; # makes {\"x} -> xe - $str =~ s/\{\\\"(A)\}/Ä/g; # makes {\"x} -> xe - - $str =~ s/\{\"(u)\}/ü/g; # makes {"x} -> xe - $str =~ s/\{\"(U)\}/Ü/g; # makes {"x} -> xe - $str =~ s/\{\"(o)\}/ö/g; # makes {"x} -> xe - $str =~ s/\{\"(O)\}/Ö/g; # makes {"x} -> xe - $str =~ s/\{\"(a)\}/ä/g; # makes {"x} -> xe - $str =~ s/\{\"(A)\}/Ä/g; # makes {"x} -> xe - - $str =~ s/\\\"(u)/ü/g; # makes \"{x} -> xe - $str =~ s/\\\"(U)/Ü/g; # makes \"{x} -> xe - $str =~ s/\\\"(o)/ö/g; # makes \"{x} -> xe - $str =~ s/\\\"(O)/Ö/g; # makes \"{x} -> xe - $str =~ s/\\\"(a)/ä/g; # makes \"{x} -> xe - $str =~ s/\\\"(A)/Ä/g; # makes \"{x} -> xe - - - $str =~ s/\{\\\'(\w)\}/$1/g; # makes {\'x} -> x - $str =~ s/\\\'(\w)/$1/g; # makes \'x -> x - $str =~ s/\'\'(\w)/$1/g; # makes ''x -> x - $str =~ s/\"(\w)/$1e/g; # makes "x -> xe - $str =~ s/\{\\ss\}/ss/g; # makes {\ss}-> ss - $str =~ s/\{(.*)\}/$1/g; # makes {abc..def}-> abc..def - $str =~ s/\\\^(\w)(\w)/$1$2/g; # makes \^xx-> xx - $str =~ s/\\\^(\w)/$1/g; # makes \^x-> x - $str =~ s/\\\~(\w)/$1/g; # makes \~x-> x - $str =~ s/\\//g; # removes \ - - - - $str =~ s/\{+//g; - $str =~ s/\}+//g; - return $str; + my $str = shift; + + use TeX::Encode; + $str = decode( 'latex', $str ); + + $str =~ s/\{(\w)\}/$1/g; # makes {x} -> x + $str =~ s/\{\\\"(u)\}/ü/g; # makes {\"x} -> xe + $str =~ s/\{\\\"(U)\}/Ü/g; # makes {\"x} -> xe + $str =~ s/\{\\\"(o)\}/ö/g; # makes {\"x} -> xe + $str =~ s/\{\\\"(O)\}/Ö/g; # makes {\"x} -> xe + $str =~ s/\{\\\"(a)\}/ä/g; # makes {\"x} -> xe + $str =~ s/\{\\\"(A)\}/Ä/g; # makes {\"x} -> xe + + $str =~ s/\{\"(u)\}/ü/g; # makes {"x} -> xe + $str =~ s/\{\"(U)\}/Ü/g; # makes {"x} -> xe + $str =~ s/\{\"(o)\}/ö/g; # makes {"x} -> xe + $str =~ s/\{\"(O)\}/Ö/g; # makes {"x} -> xe + $str =~ s/\{\"(a)\}/ä/g; # makes {"x} -> xe + $str =~ s/\{\"(A)\}/Ä/g; # makes {"x} -> xe + + $str =~ s/\\\"(u)/ü/g; # makes \"{x} -> xe + $str =~ s/\\\"(U)/Ü/g; # makes \"{x} -> xe + $str =~ s/\\\"(o)/ö/g; # makes \"{x} -> xe + $str =~ s/\\\"(O)/Ö/g; # makes \"{x} -> xe + $str =~ s/\\\"(a)/ä/g; # makes \"{x} -> xe + $str =~ s/\\\"(A)/Ä/g; # makes \"{x} -> xe + + + $str =~ s/\{\\\'(\w)\}/$1/g; # makes {\'x} -> x + $str =~ s/\\\'(\w)/$1/g; # makes \'x -> x + $str =~ s/\'\'(\w)/$1/g; # makes ''x -> x + $str =~ s/\"(\w)/$1e/g; # makes "x -> xe + $str =~ s/\{\\ss\}/ss/g; # makes {\ss}-> ss + $str =~ s/\{(.*)\}/$1/g; # makes {abc..def}-> abc..def + $str =~ s/\\\^(\w)(\w)/$1$2/g; # makes \^xx-> xx + $str =~ s/\\\^(\w)/$1/g; # makes \^x-> x + $str =~ s/\\\~(\w)/$1/g; # makes \~x-> x + $str =~ s/\\//g; # removes \ + + + $str =~ s/\{+//g; + $str =~ s/\}+//g; + return $str; } ################################################################################ sub official_bibtex_types { @@ -306,9 +314,10 @@ sub official_bibtex_types { ## defined by bibtex and constant return ( - 'article', 'book', 'booklet', 'conference', 'inbook', - 'incollection', 'inproceedings', 'manual', 'mastersthesis', 'misc', - 'phdthesis', 'proceedings', 'techreport', 'unpublished' + 'article', 'book', 'booklet', 'conference', + 'inbook', 'incollection', 'inproceedings', 'manual', + 'mastersthesis', 'misc', 'phdthesis', 'proceedings', + 'techreport', 'unpublished' ); } #################################################################################### @@ -321,12 +330,14 @@ sub random_string { } ################################################################################ sub get_current_month { - my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime(); + my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) + = localtime(); return ( $mon + 1 ); } ################################################################################ sub get_current_year { - my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) = localtime(); + my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) + = localtime(); return ( $year + 1900 ); } @@ -372,13 +383,14 @@ sub nohtml { my $key = shift // "key-unknown"; my $type = shift // "no-type"; return - "" - . "NO HTML " - . "" - . "($type) $key" . "
"; + "" + . "NO HTML " + . "" + . "($type) $key" . "
"; } + # ################################################################################ -sub get_generic_type_description { +sub get_generic_type_description { my $type_desc = shift; return "Talks " if $type_desc eq 'talk'; return "Publications of type " . $type_desc; @@ -389,7 +401,7 @@ sub create_user_id { my ($name) = @_; my @first_arr = $name->part('first'); - @first_arr = grep {defined $_ } @first_arr; + @first_arr = grep { defined $_ } @first_arr; my $first = join( ' ', @first_arr ); my @von_arr = $name->part('von'); @@ -408,32 +420,33 @@ sub create_user_id { $userID .= $jr if defined $jr; - $userID =~ s/\\k\{a\}/a/g; # makes \k{a} -> a $userID =~ s/\\l/l/g; # makes \l -> l - $userID =~ s/\\r\{u\}/u/g; # makes \r{u} -> u # FIXME: make sure that the letter is caught - # $userID =~ s/\\r{u}/u/g; # makes \r{u} -> u # the same but not escaped - - $userID =~ s/\{(\w)\}/$1/g; # makes {x} -> x - $userID =~ s/\{\\\"(\w)\}/$1e/g; # makes {\"x} -> xe - $userID =~ s/\{\"(\w)\}/$1e/g; # makes {"x} -> xe - $userID =~ s/\\\"(\w)/$1e/g; # makes \"{x} -> xe - $userID =~ s/\{\\\'(\w)\}/$1/g; # makes {\'x} -> x - $userID =~ s/\\\'(\w)/$1/g; # makes \'x -> x - $userID =~ s/\'\'(\w)/$1/g; # makes ''x -> x - $userID =~ s/\"(\w)/$1e/g; # makes "x -> xe - $userID =~ s/\{\\ss\}/ss/g; # makes {\ss}-> ss - $userID =~ s/\{(\w*)\}/$1/g; # makes {abc..def}-> abc..def + $userID =~ s/\\r\{u\}/u/g + ; # makes \r{u} -> u # FIXME: make sure that the letter is caught + # $userID =~ s/\\r{u}/u/g; # makes \r{u} -> u # the same but not escaped + + $userID =~ s/\{(\w)\}/$1/g; # makes {x} -> x + $userID =~ s/\{\\\"(\w)\}/$1e/g; # makes {\"x} -> xe + $userID =~ s/\{\"(\w)\}/$1e/g; # makes {"x} -> xe + $userID =~ s/\\\"(\w)/$1e/g; # makes \"{x} -> xe + $userID =~ s/\{\\\'(\w)\}/$1/g; # makes {\'x} -> x + $userID =~ s/\\\'(\w)/$1/g; # makes \'x -> x + $userID =~ s/\'\'(\w)/$1/g; # makes ''x -> x + $userID =~ s/\"(\w)/$1e/g; # makes "x -> xe + $userID =~ s/\{\\ss\}/ss/g; # makes {\ss}-> ss + $userID =~ s/\{(\w*)\}/$1/g; # makes {abc..def}-> abc..def $userID =~ s/\\\^(\w)(\w)/$1$2/g; # makes \^xx-> xx - # I am not sure if the next one is necessary - $userID =~ s/\\\^(\w)/$1/g; # makes \^x-> x - $userID =~ s/\\\~(\w)/$1/g; # makes \~x-> x - $userID =~ s/\\//g; # removes \ + # I am not sure if the next one is necessary + $userID =~ s/\\\^(\w)/$1/g; # makes \^x-> x + $userID =~ s/\\\~(\w)/$1/g; # makes \~x-> x + $userID =~ s/\\//g; # removes \ - $userID =~ s/\{//g; # removes { - $userID =~ s/\}//g; # removes } + $userID =~ s/\{//g; # removes { + $userID =~ s/\}//g; # removes } - $userID =~ s/\(.*\)//g; # removes everything between the brackets and the brackets also + $userID =~ s/\(.*\)//g + ; # removes everything between the brackets and the brackets also # print "$userID \n"; return $userID; diff --git a/lib/BibSpace/Model/Backup.pm b/lib/BibSpace/Model/Backup.pm index ab28f64..742f908 100644 --- a/lib/BibSpace/Model/Backup.pm +++ b/lib/BibSpace/Model/Backup.pm @@ -3,7 +3,7 @@ package Backup; use Data::Dumper; use utf8; -use v5.16; +use v5.16; use List::MoreUtils qw(any uniq); use List::Util qw(first); @@ -20,9 +20,9 @@ class_has 'date_format_pattern' => has 'uuid' => ( - is => 'rw', - isa => 'Str', - default => sub { create_uuid_as_string(UUID_V4) } + is => 'rw', + isa => 'Str', + default => sub { create_uuid_as_string(UUID_V4) } ); has 'name' => ( is => 'rw', isa => 'Str', default => 'normal' ); has 'type' => ( is => 'rw', isa => 'Str', default => 'storable' ); @@ -30,159 +30,153 @@ has 'filename' => ( is => 'rw', isa => 'Maybe[Str]' ); has 'dir' => ( is => 'rw', isa => 'Maybe[Str]' ); has 'allow_delete' => ( is => 'rw', isa => 'Bool', default => 1 ); has 'date' => ( - is => 'rw', - isa => 'Str', - default => sub { - my $now = DateTime->now( - formatter => DateTime::Format::Strptime->new( - pattern => Backup->date_format_pattern - ) - ); - return "$now"; - }, + is => 'rw', + isa => 'Str', + default => sub { + my $now = DateTime->now( + formatter => DateTime::Format::Strptime->new( + pattern => Backup->date_format_pattern + ) + ); + return "$now"; + }, ); #################################################################################### sub id { - shift->uuid; + shift->uuid; } #################################################################################### sub get_size { - my $self = shift; - my $size = -s $self->get_path; - $size = 0 + $size; - $size = $size / 1024 / 1024; - $size = sprintf( "%.2f", $size ); - return $size; + my $self = shift; + my $size = -s $self->get_path; + $size = 0 + $size; + $size = $size / 1024 / 1024; + $size = sprintf( "%.2f", $size ); + return $size; } #################################################################################### sub get_path { - my $self = shift; - - warn "backup->dir not set!" unless defined $self->dir; + my $self = shift; - my $file_path = $self->dir . $self->filename; - return $file_path; + warn "backup->dir not set!" unless defined $self->dir; + my $dir = $self->dir; + $dir =~ s!/*$!/!; + my $file_path = $dir . $self->filename; + return $file_path; } #################################################################################### sub is_healthy { - my $self = shift; - my $dir = $self->dir; - $dir =~ s!/*$!/!; - my $file_path = $dir . $self->filename; - return -e $file_path; + my $self = shift; + my $dir = $self->dir; + $dir =~ s!/*$!/!; + my $file_path = $dir . $self->filename; + return -e $file_path; } #################################################################################### sub get_date_readable { - my $self = shift; - - # parses from our format to default format - my $date - = DateTime::Format::Strptime->new( - pattern => Backup->date_format_pattern ) - ->parse_datetime( $self->date ); - - # sets readable format for serialization - $date->set_formatter( - DateTime::Format::Strptime->new( pattern => '%d.%m.%Y %H:%M:%S' ) ); - return "$date"; + my $self = shift; + + # parses from our format to default format + my $date = DateTime::Format::Strptime->new( + pattern => Backup->date_format_pattern )->parse_datetime( $self->date ); + + # sets readable format for serialization + $date->set_formatter( + DateTime::Format::Strptime->new( pattern => '%d.%m.%Y %H:%M:%S' ) ); + return "$date"; } #################################################################################### sub get_age { - my $self = shift; - - my $now = DateTime->now( - formatter => DateTime::Format::Strptime->new( - pattern => Backup->date_format_pattern - ) - ); - my $then - = DateTime::Format::Strptime->new( - pattern => Backup->date_format_pattern ) - ->parse_datetime( $self->date ); - - my $diff = $now->subtract_datetime($then); - return $diff; + my $self = shift; + + my $now = DateTime->now( + formatter => DateTime::Format::Strptime->new( + pattern => Backup->date_format_pattern + ) + ); + my $then = DateTime::Format::Strptime->new( + pattern => Backup->date_format_pattern )->parse_datetime( $self->date ); + + my $diff = $now->subtract_datetime($then); + return $diff; } #################################################################################### sub create { - my $self = shift; - my $name = shift; - my $type = shift // 'storable'; - - my $ext = '.dat'; - $ext = '.sql' if $type eq 'mysql'; - - my $uuid = create_uuid_as_string(UUID_V4); - - my $now = "" - . DateTime->now( - formatter => DateTime::Format::Strptime->new( - pattern => Backup->date_format_pattern - ) - ); - my $now_str = "$now"; - - my $filename = "backup_$uuid" . "_$name" . "_$type" . "_$now" . $ext; - - return Backup->new( - filename => $filename, - uuid => $uuid, - name => $name, - type => $type, - date => $now_str - ); + my $self = shift; + my $name = shift; + my $type = shift // 'storable'; + + my $ext = '.dat'; + $ext = '.sql' if $type eq 'mysql'; + + my $uuid = create_uuid_as_string(UUID_V4); + + my $now = "" + . DateTime->now( + formatter => DateTime::Format::Strptime->new( + pattern => Backup->date_format_pattern + ) + ); + my $now_str = "$now"; + + my $filename = "backup_$uuid" . "_$name" . "_$type" . "_$now" . $ext; + + return Backup->new( + filename => $filename, + uuid => $uuid, + name => $name, + type => $type, + date => $now_str + ); } #################################################################################### sub parse { - my $self = shift; - my $filename = shift; + my $self = shift; + my $filename = shift; - # say "Backup->parse: $filename"; + # say "Backup->parse: $filename"; - my @tokens = split( '_', $filename ); - die - "Parse exception: wrong filename format. Probably not BibSpace backup." - unless scalar(@tokens) == 5; - my $prefix = shift @tokens; - my $uuid = shift @tokens; - my $name = shift @tokens; - my $type = shift @tokens; - my $date = shift @tokens; + my @tokens = split( '_', $filename ); + die "Parse exception: wrong filename format. Probably not BibSpace backup." + unless scalar(@tokens) == 5; + my $prefix = shift @tokens; + my $uuid = shift @tokens; + my $name = shift @tokens; + my $type = shift @tokens; + my $date = shift @tokens; - $date =~ s/\.dat//g; - $date =~ s/\.sql//g; + $date =~ s/\.dat//g; + $date =~ s/\.sql//g; # my $now = DateTime->now(formatter => DateTime::Format::Strptime->new( pattern => Backup->date_format_pattern )); - my $now = DateTime::Format::Strptime->new( - pattern => Backup->date_format_pattern )->parse_datetime($date); - $now->set_formatter( - DateTime::Format::Strptime->new( - pattern => Backup->date_format_pattern - ) - ); - my $now_str = "$now"; - - return Backup->new( - filename => $filename, - uuid => $uuid, - name => $name, - type => $type, - date => $now_str - ); + my $now = DateTime::Format::Strptime->new( + pattern => Backup->date_format_pattern )->parse_datetime($date); + $now->set_formatter( + DateTime::Format::Strptime->new( pattern => Backup->date_format_pattern ) + ); + my $now_str = "$now"; + + return Backup->new( + filename => $filename, + uuid => $uuid, + name => $name, + type => $type, + date => $now_str + ); } #################################################################################### sub toString { - my $self = shift; - return "Backup filename '" . $self->filename . "'"; + my $self = shift; + return "Backup filename '" . $self->filename . "'"; } #################################################################################### sub equals { - my $self = shift; - my $obj = shift; - die "Comparing apples to peaches! " . ref($self) . " against " . ref($obj) - unless ref($self) eq ref($obj); - return $self->filename eq $obj->filename; + my $self = shift; + my $obj = shift; + die "Comparing apples to peaches! " . ref($self) . " against " . ref($obj) + unless ref($self) eq ref($obj); + return $self->filename eq $obj->filename; } #################################################################################### diff --git a/lib/BibSpace/TestManager.pm b/lib/BibSpace/TestManager.pm index 77227c3..73e26f4 100644 --- a/lib/BibSpace/TestManager.pm +++ b/lib/BibSpace/TestManager.pm @@ -14,8 +14,9 @@ sub apply_fixture { my $self = shift; my $app = shift; ## THIS SHOULD BE REPEATED FOR EACH TEST! - my $fixture_name = "bibspace_fixture.dat"; - my $fixture_dir = "./fixture/"; + my $fixture_file = $app->home->rel_file('fixture/bibspace_fixture.dat'); + my $fixture_name = ''.$fixture_file->basename; + my $fixture_dir = ''.$fixture_file->dirname; my $fixture = Backup->new(dir => $fixture_dir, filename =>$fixture_name); restore_storable_backup($fixture, $app); } diff --git a/lib/BibSpace/Util/Preferences.pm b/lib/BibSpace/Util/Preferences.pm index 38cf8a0..e9aac83 100644 --- a/lib/BibSpace/Util/Preferences.pm +++ b/lib/BibSpace/Util/Preferences.pm @@ -2,6 +2,7 @@ package Preferences; use v5.16; use Try::Tiny; +use Path::Tiny; use Data::Dumper; use namespace::autoclean; @@ -18,23 +19,28 @@ use MooseX::Storage; with Storage( 'format' => 'JSON', 'io' => 'File' ); has 'filename' => ( - is => 'rw', - isa => 'Str', - default => "bibspace_preferences.json", - traits => ['DoNotSerialize'] + is => 'rw', + isa => 'Str', + default => "bibspace_preferences.json", + traits => ['DoNotSerialize'] ); # I can't name it load due to deep recursion (direct or indirect) sub load_maybe { - my $self = shift; - if ( -e $self->filename ) { - my $prefs = Preferences->load( $self->filename ); - - # the filename should not be stored, so we need to copy it - $prefs->filename($self->filename); - return $prefs; - } - return $self; + my $self = shift; + my $obj = undef; + try { + $obj = Preferences->load( $self->filename ); + $obj->filename( $self->filename ); + } + catch { + $obj = $self; + warn "Cannot load preferences form file " + . $self->filename + . ". Creating new file.\n"; + Path::Tiny->new( $self->filename )->touchpath; + }; + return $obj; } @@ -42,10 +48,10 @@ has 'run_in_demo_mode' => ( is => 'rw', isa => 'Int', default => 0, trigger => \&_pref_changed ); has 'bibitex_html_converter' => ( - is => 'rw', - isa => 'Str', - default => 'BibStyleConverter', - trigger => \&_pref_changed + is => 'rw', + isa => 'Str', + default => 'BibStyleConverter', + trigger => \&_pref_changed ); # important for Preferences form to set flag "(default)" by the right list item @@ -53,48 +59,56 @@ has 'default_bibitex_html_converter' => ( is => 'ro', isa => 'Str', default => 'BibStyleConverter' ); has 'local_time_zone' => ( - is => 'rw', - isa => 'Str', - default => 'Europe/Berlin', - trigger => \&_pref_changed + is => 'rw', + isa => 'Str', + default => 'Europe/Berlin', + trigger => \&_pref_changed ); # http://search.cpan.org/~drolsky/DateTime-1.42/lib/DateTime.pm#strftime_Patterns has 'output_time_format' => ( - is => 'rw', - isa => 'Str', - default => '%a %d %b %T, %Y', - trigger => \&_pref_changed + is => 'rw', + isa => 'Str', + default => '%a %d %b %T, %Y', + trigger => \&_pref_changed ); # cron_level => last_call has 'cron' => ( - traits => ['Hash'], - is => 'rw', - isa => 'HashRef[Str]', - default => sub { {} }, - trigger => \&_pref_changed, - handles => { - cron_set => 'set', - cron_get => 'get', - cron_has => 'exists', - cron_defined => 'defined', - cron_keys => 'keys', - cron_values => 'values', - cron_num => 'count', - cron_pairs => 'kv', - }, + traits => ['Hash'], + is => 'rw', + isa => 'HashRef[Str]', + default => sub { {} }, + trigger => \&_pref_changed, + handles => { + cron_set => 'set', + cron_get => 'get', + cron_has => 'exists', + cron_defined => 'defined', + cron_keys => 'keys', + cron_values => 'values', + cron_num => 'count', + cron_pairs => 'kv', + }, ); ###### METHODS sub _pref_changed { - my ( $self, $curr_val, $prev_val ) = @_; + my ( $self, $curr_val, $prev_val ) = @_; - if ( $prev_val and $curr_val ne $prev_val ) { - say "A preference changed to '$curr_val'."; - $self->store( $self->filename ); + if ( $prev_val and $curr_val ne $prev_val ) { + say "A preference changed to '$curr_val'."; + try { + Path::Tiny->new( $self->filename )->touchpath; + $self->store( $self->filename ); } + catch { + warn "Cannot touch path " + . $self->filename + . ". Preferences will not be saved.\n"; + }; + } } __PACKAGE__->meta->make_immutable; diff --git a/lib/BibSpace/Util/Statistics.pm b/lib/BibSpace/Util/Statistics.pm index 7b4cbd8..3a708d9 100644 --- a/lib/BibSpace/Util/Statistics.pm +++ b/lib/BibSpace/Util/Statistics.pm @@ -3,64 +3,87 @@ use namespace::autoclean; use feature qw( state say ); +use Path::Tiny; +use Try::Tiny; use Moose; use MooseX::Storage; with Storage( 'format' => 'JSON', 'io' => 'File' ); has 'url_history' => ( - traits => ['Hash'], - is => 'ro', - isa => 'HashRef[Int]', - default => sub { {} }, - handles => { - set => 'set', - get => 'get', - has => 'exists', - defined => 'defined', - keys => 'keys', - # values => 'values', - num => 'count', - pairs => 'kv', - _clear => 'clear', - }, + traits => ['Hash'], + is => 'ro', + isa => 'HashRef[Int]', + default => sub { {} }, + handles => { + set => 'set', + get => 'get', + has => 'exists', + defined => 'defined', + keys => 'keys', + + # values => 'values', + num => 'count', + pairs => 'kv', + _clear => 'clear', + }, ); -has 'filename' =>( is => 'ro', isa => 'Str', default => "bibspace_stats.json"); +has 'filename' => ( is => 'rw', isa => 'Str', traits => ['DoNotSerialize'] ); sub load_maybe { my $self = shift; - if ( -e $self->filename ) { - return Statistics->load($self->filename); + my $obj = undef; + try { + $obj = Statistics->load( $self->filename ); + $obj->filename( $self->filename ); } - return $self; + catch { + $obj = $self; + warn "Cannot load statistics form file " + . $self->filename + . ". Creating new file.\n"; + Path::Tiny->new( $self->filename )->touchpath; + }; + + return $obj; } sub log_url { - my $self = shift; - my $url = shift; + my $self = shift; + my $url = shift; - if( !$self->defined($url) ){ - $self->set($url, 1); + if ( !$self->defined($url) ) { + $self->set( $url, 1 ); } - else{ - my $num = $self->get($url); - $self->set($url, $num+1); + else { + my $num = $self->get($url); + $self->set( $url, $num + 1 ); } - $self->store($self->filename); + try { + Path::Tiny->new( $self->filename )->touchpath; + $self->store( $self->filename ); + } + catch { + warn "Cannot touch path " + . $self->filename + . ". Statistics will not be saved.\n"; + }; } sub toString { - my $self = shift; - return join("\n", $self->toLines); + my $self = shift; + return join( "\n", $self->toLines ); } sub toLines { - my $self = shift; + my $self = shift; my @lines; - my @keys = reverse sort { $self->url_history->{$a} <=> $self->url_history->{$b} } keys(%{ $self->url_history }); - foreach my $key (@keys){ + my @keys + = reverse sort { $self->url_history->{$a} <=> $self->url_history->{$b} } + keys( %{ $self->url_history } ); + foreach my $key (@keys) { my $str; my $num_calls = $self->get($key); $str .= sprintf " %-5s ", $num_calls; @@ -72,4 +95,4 @@ sub toLines { __PACKAGE__->meta->make_immutable; no Moose; -1; +1; \ No newline at end of file diff --git a/t/000_prepare.t b/t/000_prepare.t index a3a400c..6ed2dad 100644 --- a/t/000_prepare.t +++ b/t/000_prepare.t @@ -10,7 +10,7 @@ use BibSpace; use BibSpace::Model::Backup; use BibSpace::Functions::BackupFunctions qw(restore_storable_backup); -use BibSpace::Functions::FDB; # TODO: purge DB etc. +use BibSpace::Functions::FDB; # TODO: purge DB etc. `rm log/*.log`; @@ -24,48 +24,55 @@ $t_logged_in->post_ok( form => { user => 'pub_admin', pass => 'asdf' } ); -my $self = $t_logged_in->app; -my $dbh = $self->app->db; +my $self = $t_logged_in->app; +my $dbh = $self->app->db; my $app_config = $t_logged_in->app->config; -my $db_host = $self->config->{db_host}; -my $db_user = $self->config->{db_user}; -my $db_database = $self->config->{db_database}; -my $db_pass = $self->config->{db_pass}; +my $db_host = $ENV{BIBSPACE_DB_HOST} || $self->app->config->{db_host}; +my $db_user = $ENV{BIBSPACE_DB_USER} || $self->app->config->{db_user}; +my $db_database + = $ENV{BIBSPACE_DB_DATABASE} || $self->app->config->{db_database}; +my $db_pass = $ENV{BIBSPACE_DB_PASS} || $self->app->config->{db_pass}; + note "Check if we can talk to MySQL and proper database exists."; -ok(db_connect($db_host, $db_user, $db_database, $db_pass), "Can connect to database"); +ok( db_connect( $db_host, $db_user, $db_database, $db_pass ), + "Can connect to database" ); $dbh = $self->app->db; -my $fixture_name = "bibspace_fixture.dat"; -my $fixture_dir = "./fixture/"; -SKIP: { - note "Drop database and recreate tables"; - skip "System is running in production mode!! Do not test on production!", 1 if $self->mode eq 'production'; - ok( reset_db_data($dbh), "reset_db_data"); -}; +my $fixture_file = $self->app->home->rel_file('fixture/bibspace_fixture.dat'); +my $fixture_name = ''.$fixture_file->basename; +my $fixture_dir = ''.$fixture_file->dirname; +SKIP: { + note "Drop database and recreate tables"; + skip "System is running in production mode!! Do not test on production!", + 1 + if $self->mode eq 'production'; + ok( reset_db_data($dbh), "reset_db_data" ); +} SKIP: { - note "============ APPLY DATABASE FIXTURE ============"; - skip "Directory $fixture_dir does not exist", 1 if !-e $fixture_dir.$fixture_name; + note "============ APPLY DATABASE FIXTURE ============"; + skip "Directory $fixture_dir does not exist", 1 + if !-e $fixture_dir . $fixture_name; - note "Find backup file"; - my $fixture = Backup->new(dir => $fixture_dir, filename =>$fixture_name); - - note "restore_storable_backup - read data into all layers"; - # this restores data to all layers! - restore_storable_backup($fixture, $self->app); + note "Find backup file"; + my $fixture + = Backup->new( dir => $fixture_dir, filename => $fixture_name ); + note "restore_storable_backup - read data into all layers"; -}; + # this restores data to all layers! + restore_storable_backup( $fixture, $self->app ); +} + ok(1); done_testing(); - diff --git a/t/100-unit/Author.t b/t/100-unit/Author.t index 39a59fd..4724fef 100644 --- a/t/100-unit/Author.t +++ b/t/100-unit/Author.t @@ -16,14 +16,6 @@ my $self = $t_anyone->app; use BibSpace::TestManager; TestManager->apply_fixture($self->app); -# ## THIS SHOULD BE REPEATED FOR EACH TEST! -# my $fixture_name = "bibspace_fixture.dat"; -# my $fixture_dir = "./fixture/"; -# use BibSpace::Model::Backup; -# use BibSpace::Functions::BackupFunctions qw(restore_storable_backup); -# my $fixture = Backup->new(dir => $fixture_dir, filename =>$fixture_name); -# restore_storable_backup($fixture, $self->app); - my $repo = $self->app->repo; From cfc261489f3a8ec14b82e4cac89930ccf9a820e1 Mon Sep 17 00:00:00 2001 From: vikin91 Date: Mon, 7 Aug 2017 22:36:52 +0200 Subject: [PATCH 09/21] PerlTidy: allow max 1 blank line --- .perltidyrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.perltidyrc b/.perltidyrc index 7c2cf62..f2ac20c 100644 --- a/.perltidyrc +++ b/.perltidyrc @@ -2,7 +2,7 @@ -w # Show all warnings -iob # Ignore old breakpoints -l=80 # 80 characters per line --mbl=2 # No more than 2 blank lines +-mbl=1 # No more than 2 blank lines -i=2 # Indentation is 2 columns -ci=2 # Continuation indentation is 2 columns -vt=0 # Less vertical tightness From 36a0a7acc626a0aa9b0206d85e194c3b9939f78f Mon Sep 17 00:00:00 2001 From: vikin91 Date: Tue, 8 Aug 2017 00:20:53 +0200 Subject: [PATCH 10/21] Bump version 0.5.1. Update Changelog --- Changelog.md | 4 ++++ lib/BibSpace.pm | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index ddbc318..bb6582d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,9 @@ ### Changelog ### +#### v0.5.1 08.2017 #### +* Add support for docekrizing +* Move json files to json_data directory + #### v0.5.0 05.2016 #### * remove Redis-based caching * Big refactoring diff --git a/lib/BibSpace.pm b/lib/BibSpace.pm index 7323ea7..62c307f 100644 --- a/lib/BibSpace.pm +++ b/lib/BibSpace.pm @@ -1,4 +1,4 @@ -package BibSpace v0.5.0; +package BibSpace v0.5.1; # ABSTRACT: BibSpace is a system to manage Bibtex references for authors and research groups web page. From aa7550ac103bc1eca165753feb60bce4d4765801 Mon Sep 17 00:00:00 2001 From: vikin91 Date: Mon, 7 Aug 2017 22:15:08 +0200 Subject: [PATCH 11/21] Refactor code to ease dockerizing --- README.md | 15 +++++++++++++++ fixture/bibspace_fixture.dat | Bin 962554 -> 962554 bytes lib/BibSpace.pm | 4 ++-- lib/BibSpace/Controller/Cron.pm | 2 +- lib/BibSpace/Controller/Persistence.pm | 1 - lib/BibSpace/Functions/Core.pm | 3 --- lib/BibSpace/Model/Backup.pm | 1 + lib/BibSpace/Util/Preferences.pm | 2 +- 8 files changed, 20 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index f932d4d..674bcfd 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,21 @@ Visit [hex64.com](http://www.hex64.com/) and click backend/frontend demo to have ## Installation ## * See [INSTALL.md](INSTALL.md) +## Using BibSpace with Docker ## + +BibSpace has no official docker image on dockerhub (yet), thus you need to build it manually. However, an image with prerequisites exists to ease the process of building. Here are the commands to build BibSpace using docker. + +``` +# build BibSpace image +docker-compose build +# run it with docker-compose +docker-compose up -d +# thats it! Point you browser to http://localhost:8083/ to open BibSpace +# you may stop the container with +docker-compose down +``` + +Your MySQL data is stored in `db_data`, whereas preferences and stats in `json_data`. ## TODOs ## BibSpace is currently undergoing serious refactoring. I try to keep current status up to date in [BibSpace Trelo Board](https://trello.com/b/yQ2VPiQ3/bibspace) diff --git a/fixture/bibspace_fixture.dat b/fixture/bibspace_fixture.dat index 81d29882cee2f9e6a176b11fa5afa87455d824d5..174a4294e2f92ca26c264ed822f7081260fa2ae7 100644 GIT binary patch delta 90956 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

_L{R1z9uz!ch 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 sub { my $self = shift; my $file - = $self->app->home->rel_file("bibspace_preferences.json"); + = $self->app->home->rel_file("json_data/bibspace_preferences.json"); return state $prefs = Preferences->new( filename => "" . $file )->load_maybe; }; has statistics => sub { my $self = shift; - my $file = $self->app->home->rel_file("bibspace_stats.json"); + my $file = $self->app->home->rel_file("json_data/bibspace_stats.json"); say $file; return state $stats = Statistics->new( filename => "" . $file )->load_maybe; }; diff --git a/lib/BibSpace/Controller/Cron.pm b/lib/BibSpace/Controller/Cron.pm index 2d08bc2..7187c4d 100644 --- a/lib/BibSpace/Controller/Cron.pm +++ b/lib/BibSpace/Controller/Cron.pm @@ -155,7 +155,7 @@ sub cron_run { else { # do nothing } - # this may cause: [error] Unable to open file (bibspace_preferences.json) for storing : Permission denied at + # this may cause: [error] Unable to open file (json_data/bibspace_preferences.json) for storing : Permission denied at $self->log_cron_usage($level); $self->app->logger->info("Cron level $level has finished"); diff --git a/lib/BibSpace/Controller/Persistence.pm b/lib/BibSpace/Controller/Persistence.pm index fdeae4e..18f5fee 100644 --- a/lib/BibSpace/Controller/Persistence.pm +++ b/lib/BibSpace/Controller/Persistence.pm @@ -52,7 +52,6 @@ sub load_fixture { $self->app->logger->info( "Loading fixture from: " . $fixture_file->to_string ); - my $fixture = Backup->new( dir => '' . $fixture_file->dirname, filename => '' . $fixture_file->basename diff --git a/lib/BibSpace/Functions/Core.pm b/lib/BibSpace/Functions/Core.pm index 38a2ace..b1edb3a 100644 --- a/lib/BibSpace/Functions/Core.pm +++ b/lib/BibSpace/Functions/Core.pm @@ -18,7 +18,6 @@ use warnings; ### Security use Crypt::Eksblowfish::Bcrypt qw(bcrypt bcrypt_hash en_base64); - # not crypto secure, but causes less problems than Crypt::Random; use Bytes::Random; use Session::Token; @@ -183,9 +182,7 @@ sub generate_token { sub encrypt_password { my $password = shift; my $salt = shift || salt(); - # Generate a salt if one is not passed - # Set the cost to 8 and append a NULL my $settings = '$2a$08$' . $salt; diff --git a/lib/BibSpace/Model/Backup.pm b/lib/BibSpace/Model/Backup.pm index 742f908..df9c087 100644 --- a/lib/BibSpace/Model/Backup.pm +++ b/lib/BibSpace/Model/Backup.pm @@ -61,6 +61,7 @@ sub get_path { warn "backup->dir not set!" unless defined $self->dir; my $dir = $self->dir; $dir =~ s!/*$!/!; + my $file_path = $dir . $self->filename; return $file_path; } diff --git a/lib/BibSpace/Util/Preferences.pm b/lib/BibSpace/Util/Preferences.pm index e9aac83..c8f1cf2 100644 --- a/lib/BibSpace/Util/Preferences.pm +++ b/lib/BibSpace/Util/Preferences.pm @@ -21,7 +21,7 @@ with Storage( 'format' => 'JSON', 'io' => 'File' ); has 'filename' => ( is => 'rw', isa => 'Str', - default => "bibspace_preferences.json", + default => "json_data/bibspace_preferences.json", traits => ['DoNotSerialize'] ); From cc529728de9641ca7f4dcb067e831ff09be6d395 Mon Sep 17 00:00:00 2001 From: vikin91 Date: Tue, 8 Aug 2017 01:07:33 +0200 Subject: [PATCH 12/21] tidy here --- .perltidyrc | 1 + .tidyallrc | 8 + lib/BibSpace.pm | 357 +- lib/BibSpace/Backend/IBibSpaceBackend.pm | 3 +- lib/BibSpace/Backend/SmartArray.pm | 213 +- lib/BibSpace/Backend/SmartHash.pm | 210 +- lib/BibSpace/Controller/Authors.pm | 447 ++- lib/BibSpace/Controller/Backup.pm | 81 +- lib/BibSpace/Controller/Cron.pm | 77 +- lib/BibSpace/Controller/Display.pm | 131 +- lib/BibSpace/Controller/Helpers.pm | 206 +- lib/BibSpace/Controller/Login.pm | 812 ++-- lib/BibSpace/Controller/Persistence.pm | 175 +- lib/BibSpace/Controller/Preferences.pm | 68 +- lib/BibSpace/Controller/Publications.pm | 721 ++-- .../Controller/PublicationsExperimental.pm | 347 +- .../Controller/PublicationsLanding.pm | 961 ++--- lib/BibSpace/Controller/PublicationsSeo.pm | 447 ++- lib/BibSpace/Controller/Tags.pm | 215 +- lib/BibSpace/Controller/Tagtypes.pm | 160 +- lib/BibSpace/Controller/Teams.pm | 78 +- lib/BibSpace/Controller/Types.pm | 121 +- lib/BibSpace/Converter/BibStyleConverter.pm | 320 +- .../Converter/Bibtex2HtmlConverter.pm | 264 +- .../Converter/IHtmlBibtexConverter.pm | 4 +- lib/BibSpace/DAO/DAOFactory.pm | 47 +- lib/BibSpace/DAO/Interface/IDAO.pm | 11 +- lib/BibSpace/DAO/MySQL/AuthorMySQLDAO.pm | 111 +- lib/BibSpace/DAO/MySQL/AuthorshipMySQLDAO.pm | 143 +- lib/BibSpace/DAO/MySQL/EntryMySQLDAO.pm | 385 +- lib/BibSpace/DAO/MySQL/ExceptionMySQLDAO.pm | 107 +- lib/BibSpace/DAO/MySQL/LabelingMySQLDAO.pm | 128 +- lib/BibSpace/DAO/MySQL/MembershipMySQLDAO.pm | 118 +- lib/BibSpace/DAO/MySQL/TagMySQLDAO.pm | 108 +- lib/BibSpace/DAO/MySQL/TagTypeMySQLDAO.pm | 113 +- lib/BibSpace/DAO/MySQL/TeamMySQLDAO.pm | 120 +- lib/BibSpace/DAO/MySQL/TypeMySQLDAO.pm | 132 +- lib/BibSpace/DAO/MySQL/UserMySQLDAO.pm | 127 +- lib/BibSpace/DAO/MySQLDAOFactory.pm | 116 +- lib/BibSpace/DAO/Redis/AuthorRedisDAO.pm | 75 +- lib/BibSpace/DAO/Redis/AuthorshipRedisDAO.pm | 75 +- lib/BibSpace/DAO/Redis/EntryRedisDAO.pm | 75 +- lib/BibSpace/DAO/Redis/ExceptionRedisDAO.pm | 75 +- lib/BibSpace/DAO/Redis/LabelingRedisDAO.pm | 75 +- lib/BibSpace/DAO/Redis/MembershipRedisDAO.pm | 75 +- lib/BibSpace/DAO/Redis/TagRedisDAO.pm | 75 +- lib/BibSpace/DAO/Redis/TagTypeRedisDAO.pm | 75 +- lib/BibSpace/DAO/Redis/TeamRedisDAO.pm | 75 +- lib/BibSpace/DAO/Redis/TypeRedisDAO.pm | 64 +- lib/BibSpace/DAO/Redis/UserRedisDAO.pm | 75 +- lib/BibSpace/DAO/RedisDAOFactory.pm | 116 +- .../DAO/SmartArray/AuthorSmartArrayDAO.pm | 31 +- .../DAO/SmartArray/AuthorshipSmartArrayDAO.pm | 33 +- .../DAO/SmartArray/EntrySmartArrayDAO.pm | 31 +- .../DAO/SmartArray/ExceptionSmartArrayDAO.pm | 33 +- .../DAO/SmartArray/LabelingSmartArrayDAO.pm | 37 +- .../DAO/SmartArray/MembershipSmartArrayDAO.pm | 31 +- .../DAO/SmartArray/TagSmartArrayDAO.pm | 32 +- .../DAO/SmartArray/TagTypeSmartArrayDAO.pm | 32 +- .../DAO/SmartArray/TeamSmartArrayDAO.pm | 32 +- .../DAO/SmartArray/TypeSmartArrayDAO.pm | 34 +- .../DAO/SmartArray/UserSmartArrayDAO.pm | 31 +- lib/BibSpace/DAO/SmartArrayDAOFactory.pm | 138 +- lib/BibSpace/Functions/BackupFunctions.pm | 160 +- lib/BibSpace/Functions/Core.pm | 177 +- lib/BibSpace/Functions/FDB.pm | 148 +- lib/BibSpace/Functions/FPublications.pm | 683 ++-- .../Functions/MySqlBackupFunctions.pm | 51 +- lib/BibSpace/Model/Author.pm | 404 +- lib/BibSpace/Model/Authorship.pm | 26 +- lib/BibSpace/Model/Backup.pm | 78 +- lib/BibSpace/Model/Entry.pm | 800 ++-- lib/BibSpace/Model/Exception.pm | 84 +- lib/BibSpace/Model/IAuthored.pm | 65 +- lib/BibSpace/Model/IEntity.pm | 40 +- lib/BibSpace/Model/IHavingException.pm | 58 +- lib/BibSpace/Model/ILabeled.pm | 75 +- lib/BibSpace/Model/IMembered.pm | 57 +- lib/BibSpace/Model/IRelation.pm | 7 +- lib/BibSpace/Model/Labeling.pm | 117 +- lib/BibSpace/Model/Membership.pm | 30 +- lib/BibSpace/Model/Tag.pm | 28 +- lib/BibSpace/Model/TagCloud.pm | 14 +- lib/BibSpace/Model/TagType.pm | 13 +- lib/BibSpace/Model/Team.pm | 17 +- lib/BibSpace/Model/Type.pm | 106 +- lib/BibSpace/Model/User.pm | 154 +- lib/BibSpace/Repository/LayeredRepository.pm | 197 +- lib/BibSpace/Repository/RepositoryFacade.pm | 14 +- lib/BibSpace/Repository/RepositoryLayer.pm | 360 +- lib/BibSpace/TestManager.pm | 21 +- lib/BibSpace/Util/DummyUidProvider.pm | 12 +- lib/BibSpace/Util/EntityFactory.pm | 106 +- lib/BibSpace/Util/ILogger.pm | 114 +- lib/BibSpace/Util/IUidProvider.pm | 12 +- lib/BibSpace/Util/IntegerUidProvider.pm | 102 +- lib/BibSpace/Util/Preferences.pm | 30 +- lib/BibSpace/Util/SimpleLogger.pm | 59 +- lib/BibSpace/Util/SmartUidProvider.pm | 154 +- lib/BibSpace/Util/Statistics.pm | 38 +- lib/BibSpace/Util/Thing.pm | 11 +- lib/BibStyle/LocalBibStyle.pm | 3462 ++++++++--------- lib/redis.pl | 28 +- t/000_prepare.t | 49 +- t/100-unit/Author.t | 85 +- t/100-unit/Entry.t | 63 +- t/100-unit/Exception.t | 28 +- t/100-unit/ILogger.t | 2 - t/100-unit/Labeling.t | 28 +- t/100-unit/RepositoryFacade.t | 237 +- t/100-unit/SmartArray.t | 60 +- t/100-unit/SmartHash.t | 59 +- t/100-unit/Tag.t | 2 - t/100-unit/TagCloud.t | 12 +- t/100-unit/TagType.t | 5 - t/100-unit/Team.t | 100 +- t/100-unit/Type.t | 67 +- t/100-unit/User.t | 43 +- t/200-controller/Backup.t | 111 +- t/200-controller/Display.t | 42 +- t/200-controller/Login.t | 54 +- t/200-controller/Persistence.t | 31 +- t/200-controller/Publications.t | 413 +- t/200-controller/PublicationsExperimental.t | 30 +- t/200-controller/Tags.t | 68 +- t/300-functions/FPublications.t | 50 +- t/900-frontend/002_basic_gets.t | 103 +- t/900-frontend/003_login.t | 47 +- t/900-frontend/004_landing_publications.t | 128 +- t/900-frontend/004_publications_single.t | 45 +- t/900-frontend/005_SEO_public.t | 58 +- t/900-frontend/006_tag_clouds.t | 36 +- t/900-frontend/007_user_management_anyone.t | 268 +- t/900-frontend/008_user_management_logged.t | 143 +- t/900-frontend/009_cron_and_rest.t | 23 +- t/99_rest.t | 12 +- util/ArchitectureGeneratorRepository.pl | 173 +- util/ArchitectureGeneratorRepositoryTest.pl | 198 +- 138 files changed, 10777 insertions(+), 9826 deletions(-) create mode 100644 .tidyallrc diff --git a/.perltidyrc b/.perltidyrc index f2ac20c..87d2266 100644 --- a/.perltidyrc +++ b/.perltidyrc @@ -10,3 +10,4 @@ -bt=2 # High brace tightness -sbt=2 # High square bracket tightness -isbc # Don't indent comments without leading space +-nst \ No newline at end of file diff --git a/.tidyallrc b/.tidyallrc new file mode 100644 index 0000000..3b74334 --- /dev/null +++ b/.tidyallrc @@ -0,0 +1,8 @@ +[PerlTidy] +select = **/*.{pl,pm,t} +argv = --profile=$ROOT/.perltidyrc + +;[PerlCritic] +;select = lib/**/*.pm +;ignore = lib/BibStyle/**/*.pm +;argv = -severity 4 \ No newline at end of file diff --git a/lib/BibSpace.pm b/lib/BibSpace.pm index 81ee162..e611e32 100644 --- a/lib/BibSpace.pm +++ b/lib/BibSpace.pm @@ -2,7 +2,6 @@ package BibSpace v0.5.1; # ABSTRACT: BibSpace is a system to manage Bibtex references for authors and research groups web page. - use BibSpace::Functions::Core; use BibSpace::Functions::MySqlBackupFunctions; use BibSpace::Functions::FDB; @@ -14,7 +13,6 @@ use BibSpace::Functions::FPublications; use Mojo::Base 'Mojolicious'; use Mojo::Base 'Mojolicious::Plugin::Config'; - use Data::Dumper; # use File::Slurp; @@ -24,7 +22,6 @@ use Path::Tiny; # for creating directories use Mojo::Home; use File::Spec; - use BibSpace::Backend::SmartArray; use BibSpace::Backend::SmartHash; use BibSpace::Util::SimpleLogger; @@ -60,20 +57,17 @@ use feature qw( state say ); has preferences => sub { my $self = shift; - my $file - = $self->app->home->rel_file("json_data/bibspace_preferences.json"); - return state $prefs - = Preferences->new( filename => "" . $file )->load_maybe; + my $file = $self->app->home->rel_file("json_data/bibspace_preferences.json"); + return state $prefs = Preferences->new(filename => "" . $file)->load_maybe; }; has statistics => sub { my $self = shift; my $file = $self->app->home->rel_file("json_data/bibspace_stats.json"); say $file; - return state $stats = Statistics->new( filename => "" . $file )->load_maybe; + return state $stats = Statistics->new(filename => "" . $file)->load_maybe; }; - has config_file => sub { my $self = shift; my $candidate; @@ -84,10 +78,10 @@ has config_file => sub { return $candidate if -e $candidate; $candidate - = $self->app->home->rel_file('lib/BibSpace/files/config/default.conf'); + = $self->app->home->rel_file('lib/BibSpace/files/config/default.conf'); return $candidate if -e $candidate; - $candidate = $self->app->home->rel_file('config/default.conf'); # for travis + $candidate = $self->app->home->rel_file('config/default.conf'); # for travis return $candidate if -e $candidate; die "Cannot find Bibspace config!"; @@ -142,8 +136,7 @@ has use_quick_load_fixture => sub { my $self = shift; return if $self->mode eq 'production'; - return 1 - if defined $ENV{BIBSPACE_USE_DUMP} and $ENV{BIBSPACE_USE_DUMP} == 1; + return 1 if defined $ENV{BIBSPACE_USE_DUMP} and $ENV{BIBSPACE_USE_DUMP} == 1; return; }; @@ -153,10 +146,9 @@ has use_quick_load_fixture => sub { # then you need to move the object construction INTO the LayeredReposity and provide a helper to access it for everywhere. has logger => sub { state $logger = SimpleLogger->new() }; - has smartArrayBackend => sub { my $self = shift; - return SmartArray->new( logger => $self->logger ); + return SmartArray->new(logger => $self->logger); }; ## I moved this to helpers as app->attr for a while @@ -165,7 +157,6 @@ has layeredRepository => sub { my $self = shift; $self->app->logger->info("Building layeredRepository"); - my $LR = LayeredRepository->new( logger => $self->logger, preferences => $self->preferences, @@ -188,10 +179,10 @@ has layeredRepository => sub { ); $LR->add_layer($smartArrayLayer); - if ( !$self->db ) { + if (!$self->db) { $self->logger->error( "You add SQL layer, but there is no connection to the database! Skipping this layer." - . " You need to start MySQL server and restart BibSpace to use this layer" + . " You need to start MySQL server and restart BibSpace to use this layer" ); } else { @@ -203,7 +194,7 @@ has layeredRepository => sub { logger => $self->logger, handle => $self->db, reset_data_callback => \&reset_db_data, - reset_data_callback_arguments => [ $self->db ], + reset_data_callback_arguments => [$self->db], ); $LR->add_layer($mySQLLayer); } @@ -213,7 +204,7 @@ has layeredRepository => sub { # layeredRepository will not change at runtime => repo neither. has repo => sub { my $self = shift; - return RepositoryFacade->new( lr => $self->layeredRepository ); + return RepositoryFacade->new(lr => $self->layeredRepository); }; @@ -222,26 +213,22 @@ sub startup { my $self = shift; $self->app->logger->info("*** Starting BibSpace ***"); - $self->setup_config; $self->setup_plugins; $self->app->preferences->local_time_zone( - DateTime::TimeZone->new( name => 'local' )->name ); + DateTime::TimeZone->new(name => 'local')->name); $self->setup_routes; $self->setup_hooks; $self->setup_repositories; $self->insert_admin; - $self->app->logger->info("Setup done."); - - $self->app->logger->info( "Using CONFIG: " . $self->app->config_file ); - $self->app->logger->info( "App home is: " . $self->app->home ); - $self->app->logger->info( "Active bst file is: " . $self->app->bst ); - + $self->app->logger->info("Using CONFIG: " . $self->app->config_file); + $self->app->logger->info("App home is: " . $self->app->home); + $self->app->logger->info("Active bst file is: " . $self->app->bst); ############################### ########### SANDBOX ########### @@ -256,12 +243,10 @@ sub startup { # say Dumper $author; # $self->app->repo->authors_save($author); - # my @users = $self->app->repo->users_all; # $self->logger->warn("All users: ".@users); # map {say $_->toString } @users; - # my @users_to_delete = $self->app->repo->users_filter(sub{$_->email =~ /\@example.com/}); # $self->logger->warn("To delete: ".@users_to_delete); # map {say $_->toString } @users_to_delete; @@ -278,10 +263,10 @@ sub insert_admin { $self->app->logger->info("Add startup admin user..."); my $admin_exists - = $self->app->repo->users_find( sub { $_->login eq 'pub_admin' } ); - if ( !$admin_exists ) { + = $self->app->repo->users_find(sub { $_->login eq 'pub_admin' }); + if (!$admin_exists) { my $salt = salt(); - my $hash = encrypt_password( 'asdf', $salt ); + my $hash = encrypt_password('asdf', $salt); my $new_user = $self->app->entityFactory->new_User( login => 'pub_admin', email => 'pub_admin@example.com', @@ -306,42 +291,39 @@ sub setup_repositories { $self->app->logger->info("Setup repositories..."); - if ( -e $self->quick_load_fixture_filename - and $self->use_quick_load_fixture ) - { + if (-e $self->quick_load_fixture_filename and $self->use_quick_load_fixture) { + # $self->app->logger->info("Retrieving dump from '".$self->quick_load_fixture_filename."'."); - my $layer = retrieve( $self->quick_load_fixture_filename ); + my $layer = retrieve($self->quick_load_fixture_filename); # reser read layer = not needed, layer empty by start of the app # $self->app->logger->info("Replacing layer 'smart' with the dump."); - $self->repo->lr->replace_layer( 'smart', $layer ); + $self->repo->lr->replace_layer('smart', $layer); # $self->app->logger->debug("State after replacement:".$self->repo->lr->get_summary_table); } else { - $self->app->logger->info( "We do not use dump file '" - . $self->quick_load_fixture_filename - . "'." ); + $self->app->logger->info( + "We do not use dump file '" . $self->quick_load_fixture_filename . "'."); } # no data, no fun = no need to copy, link, and store - if ( $self->repo->entries_empty ) { + if ($self->repo->entries_empty) { $self->app->logger->info("Repo has no entries. Reseting read_layer."); - $self->repo->lr->copy_data( { from => 'mysql', to => 'smart' } ); + $self->repo->lr->copy_data({from => 'mysql', to => 'smart'}); # Entities and Relations in the smart layer must be linked! $self->link_data; - $self->app->logger->info( "Storing current state to dump file '" - . $self->quick_load_fixture_filename - . "'." ); + $self->app->logger->info("Storing current state to dump file '" + . $self->quick_load_fixture_filename + . "'."); # store current state to file store $self->repo->lr->get_read_layer, $self->quick_load_fixture_filename; } - } ################################################################ sub link_data { @@ -349,24 +331,21 @@ sub link_data { $self->app->logger->info("Linking data..."); $self->app->logger->info("Linking Authors (N) to (1) Authors."); - foreach my $author ( - $self->repo->authors_filter( sub { $_->id != $_->master_id } ) ) + foreach + my $author ($self->repo->authors_filter(sub { $_->id != $_->master_id })) { my $master - = $self->repo->authors_find( sub { $_->id == $author->master_id } ); - if ( $master and $author ) { + = $self->repo->authors_find(sub { $_->id == $author->master_id }); + if ($master and $author) { $author->set_master($master); } } - $self->app->logger->info("Linking Authors (N) to (M) Entries."); - foreach my $auth ( $self->repo->authorships_all ) { - my $entry - = $self->repo->entries_find( sub { $_->id == $auth->entry_id } ); - my $author - = $self->repo->authors_find( sub { $_->id == $auth->author_id } ); - if ( $entry and $author ) { + foreach my $auth ($self->repo->authorships_all) { + my $entry = $self->repo->entries_find(sub { $_->id == $auth->entry_id }); + my $author = $self->repo->authors_find(sub { $_->id == $auth->author_id }); + if ($entry and $author) { $auth->entry($entry); $auth->author($author); $entry->authorships_add($auth); @@ -375,11 +354,11 @@ sub link_data { } $self->app->logger->info("Linking Tags (N) to (M) Entries."); - foreach my $labeling ( $self->repo->labelings_all ) { + foreach my $labeling ($self->repo->labelings_all) { my $entry - = $self->repo->entries_find( sub { $_->id == $labeling->entry_id } ); - my $tag = $self->repo->tags_find( sub { $_->id == $labeling->tag_id } ); - if ( $entry and $tag ) { + = $self->repo->entries_find(sub { $_->id == $labeling->entry_id }); + my $tag = $self->repo->tags_find(sub { $_->id == $labeling->tag_id }); + if ($entry and $tag) { $labeling->entry($entry); $labeling->tag($tag); $entry->labelings_add($labeling); @@ -388,12 +367,11 @@ sub link_data { } $self->app->logger->info("Linking Teams (Exceptions) (N) to (M) Entries."); - foreach my $exception ( $self->repo->exceptions_all ) { + foreach my $exception ($self->repo->exceptions_all) { my $entry - = $self->repo->entries_find( sub { $_->id == $exception->entry_id } ); - my $team - = $self->repo->teams_find( sub { $_->id == $exception->team_id } ); - if ( $entry and $team ) { + = $self->repo->entries_find(sub { $_->id == $exception->entry_id }); + my $team = $self->repo->teams_find(sub { $_->id == $exception->team_id }); + if ($entry and $team) { $exception->entry($entry); $exception->team($team); $entry->exceptions_add($exception); @@ -401,15 +379,12 @@ sub link_data { } } - $self->app->logger->info("Linking Teams (N) to (M) Authors."); - foreach my $membership ( $self->repo->memberships_all ) { + foreach my $membership ($self->repo->memberships_all) { my $author - = $self->repo->authors_find( sub { $_->id == $membership->author_id } - ); - my $team - = $self->repo->teams_find( sub { $_->id == $membership->team_id } ); - if ( defined $author and defined $team ) { + = $self->repo->authors_find(sub { $_->id == $membership->author_id }); + my $team = $self->repo->teams_find(sub { $_->id == $membership->team_id }); + if (defined $author and defined $team) { $membership->author($author); $membership->team($team); $author->memberships_add($membership); @@ -418,9 +393,9 @@ sub link_data { } $self->app->logger->info("Linking TagTypes (N) to (1) Tags."); - foreach my $tag ( $self->repo->tags_all ) { - my $tagtype = $self->repo->tagTypes_find( sub { $_->id == $tag->type } ); - if ( $tag and $tagtype ) { + foreach my $tag ($self->repo->tags_all) { + my $tagtype = $self->repo->tagTypes_find(sub { $_->id == $tag->type }); + if ($tag and $tagtype) { $tag->tagtype($tagtype); } } @@ -434,11 +409,11 @@ sub setup_config { my $self = shift; my $app = $self; $self->app->logger->info("Setup config..."); - $self->plugin( 'Config' => { file => $self->app->config_file } ); + $self->plugin('Config' => {file => $self->app->config_file}); $ENV{MOJO_MAX_MESSAGE_SIZE} = 40 * 1024 * 1024; $self->app->logger->info( - "Setting max upload size to " . $ENV{MOJO_MAX_MESSAGE_SIZE} . " Bytes." ); + "Setting max upload size to " . $ENV{MOJO_MAX_MESSAGE_SIZE} . " Bytes."); } ################################################################ sub setup_plugins { @@ -451,20 +426,21 @@ sub setup_plugins { $self->app->plugin('RenderFile'); $self->plugin('BibSpace::Controller::Helpers'); - push @{ $self->app->static->paths }, $self->app->home->rel_file('public'); + push @{$self->app->static->paths}, $self->app->home->rel_file('public'); # push @{$self->app->static->paths}, $self->config->{backups_dir}; - $self->app->logger->info( "App version: " . $self->app->version ); + $self->app->logger->info("App version: " . $self->app->version); $self->app->logger->info("Creating directories..."); for my $dir ( - ( $self->app->get_backups_dir, + ( + $self->app->get_backups_dir, $self->app->get_upload_dir, $self->app->get_upload_dir . "papers", $self->app->get_upload_dir . "slides", $self->app->get_log_dir ) - ) + ) { $self->app->logger->debug("Creating directory: $dir"); try { @@ -478,17 +454,14 @@ sub setup_plugins { # set logging dir in the logger $self->app->logger->debug("Setting log dir to the logger"); - $self->logger->set_log_dir( "" . Path::Tiny->new( $self->get_log_dir ) ); - + $self->logger->set_log_dir("" . Path::Tiny->new($self->get_log_dir)); # this is supposed to trigger connection to the DB $self->app->db; + $self->secrets([$self->config->{key_cookie}]); - $self->secrets( [ $self->config->{key_cookie} ] ); - - $self->helper( proxy_prefix => sub { $self->config->{proxy_prefix} } ); - + $self->helper(proxy_prefix => sub { $self->config->{proxy_prefix} }); } ################################################################ @@ -500,17 +473,16 @@ sub setup_routes { my $anyone = $self->routes; $anyone->get('/')->to('display#index')->name('start'); - $anyone->get('/forgot')->to('login#forgot'); $anyone->post('/forgot/gen')->to('login#post_gen_forgot_token'); $anyone->get('/forgot/reset/:token')->to('login#token_clicked') - ->name("token_clicked"); + ->name("token_clicked"); $anyone->post('/forgot/store')->to('login#store_password'); $anyone->get('/login_form')->to('login#login_form')->name('login_form'); $anyone->post('/do_login')->to('login#login')->name('do_login'); $anyone->get('/youneedtologin')->to('login#not_logged_in') - ->name('youneedtologin'); + ->name('youneedtologin'); $anyone->get('/badpassword')->to('login#bad_password')->name('badpassword'); $anyone->get('/logout')->to('login#logout')->name('logout'); @@ -520,7 +492,7 @@ sub setup_routes { $anyone->get('/register')->to('login#register')->name('register'); $anyone->post('/register')->to('login#post_do_register') - ->name('post_do_register'); + ->name('post_do_register'); $anyone->any('/noregister')->to('login#register_disabled'); my $logged_user = $anyone->under->to('login#check_is_logged_in'); @@ -530,176 +502,164 @@ sub setup_routes { ################ PREFERENCES ################ $manager_user->get('/preferences')->to('preferences#index') - ->name('preferences'); + ->name('preferences'); $admin_user->post('/preferences')->to('preferences#save') - ->name('save_preferences'); + ->name('save_preferences'); ################ EXPERIMENTAL / PERSISTENCE ################ $anyone->get('/system_status')->to('persistence#system_status') - ->name('system_status'); + ->name('system_status'); $admin_user->get('/persistence/load')->to('persistence#load_fixture') - ->name('load_fixture'); + ->name('load_fixture'); $admin_user->get('/persistence/save')->to('persistence#save_fixture') - ->name('save_fixture'); + ->name('save_fixture'); $admin_user->get('/persistence/copy_mysql_to_smart') - ->to('persistence#copy_mysql_to_smart')->name('copy_mysql_to_smart'); + ->to('persistence#copy_mysql_to_smart')->name('copy_mysql_to_smart'); $admin_user->get('/persistence/copy_smart_to_mysql') - ->to('persistence#copy_smart_to_mysql')->name('copy_smart_to_mysql'); + ->to('persistence#copy_smart_to_mysql')->name('copy_smart_to_mysql'); $admin_user->get('/persistence/persistence_status') - ->to('persistence#persistence_status')->name('persistence_status'); + ->to('persistence#persistence_status')->name('persistence_status'); $admin_user->get('/persistence/persistence_status_ajax') - ->to('persistence#persistence_status_ajax') - ->name('persistence_status_ajax'); - + ->to('persistence#persistence_status_ajax') + ->name('persistence_status_ajax'); $admin_user->get('/persistence/reset_mysql')->to('persistence#reset_mysql') - ->name('reset_mysql'); + ->name('reset_mysql'); $admin_user->get('/persistence/reset_smart')->to('persistence#reset_smart') - ->name('reset_smart'); + ->name('reset_smart'); $admin_user->get('/persistence/reset_all')->to('persistence#reset_all') - ->name('reset_all'); + ->name('reset_all'); $admin_user->get('/persistence/insert_random_data') - ->to('persistence#insert_random_data')->name('insert_random_data'); - + ->to('persistence#insert_random_data')->name('insert_random_data'); ################ SETTINGS ################ $logged_user->get('/profile')->to('login#profile'); $admin_user->get('/manage_users')->to('login#manage_users') - ->name('manage_users'); + ->name('manage_users'); $admin_user->get('/profile/:id')->to('login#foreign_profile') - ->name('show_user_profile'); + ->name('show_user_profile'); $admin_user->get('/profile/delete/:id')->to('login#delete_user') - ->name('delete_user'); + ->name('delete_user'); $admin_user->get('/profile/make_user/:id')->to('login#make_user') - ->name('make_user'); + ->name('make_user'); $admin_user->get('/profile/make_manager/:id')->to('login#make_manager') - ->name('make_manager'); + ->name('make_manager'); $admin_user->get('/profile/make_admin/:id')->to('login#make_admin') - ->name('make_admin'); + ->name('make_admin'); $manager_user->get('/log')->to('display#show_log')->name('show_log'); $manager_user->get('/statistics')->to('display#show_stats') - ->name('show_stats'); + ->name('show_stats'); # websocket for fun $manager_user->websocket('/log_websocket/:num')->to('display#show_log_ws') - ->name('show_log_websocket'); + ->name('show_log_websocket'); $manager_user->websocket('/statistics/:num') - ->to('display#show_stats_websocket')->name('show_stats_websocket'); - + ->to('display#show_stats_websocket')->name('show_stats_websocket'); $admin_user->get('/settings/fix_months')->to('publications#fixMonths') - ->name('fix_all_months'); + ->name('fix_all_months'); $manager_user->get('/settings/clean_all') - ->to('publications#clean_ugly_bibtex')->name('clean_ugly_bibtex'); + ->to('publications#clean_ugly_bibtex')->name('clean_ugly_bibtex'); $manager_user->get('/settings/mark_all_to_regenerate') - ->to('publications#mark_all_to_regenerate') - ->name('mark_all_to_regenerate'); + ->to('publications#mark_all_to_regenerate')->name('mark_all_to_regenerate'); $manager_user->get('/settings/mark_author_to_regenerate/:author_id') - ->to('publications#mark_author_to_regenerate') - ->name('mark_author_to_regenerate'); + ->to('publications#mark_author_to_regenerate') + ->name('mark_author_to_regenerate'); $manager_user->get('/settings/regenerate_all') - ->to('publications#regenerate_html_for_all') - ->name('regenerate_html_for_all'); + ->to('publications#regenerate_html_for_all') + ->name('regenerate_html_for_all'); $logged_user->get('/settings/regenerate_html_in_chunk/:chunk_size') - ->to('publications#regenerate_html_in_chunk') - ->name('regenerate_html_in_chunk'); - + ->to('publications#regenerate_html_in_chunk') + ->name('regenerate_html_in_chunk'); $manager_user->get('/backups')->to('backup#index')->name('backup_index'); $manager_user->put('/backups')->to('backup#save')->name('backup_do'); $manager_user->put('/backups/mysql')->to('backup#save_mysql') - ->name('backup_do_mysql'); + ->name('backup_do_mysql'); $manager_user->get('/backups/:id')->to('backup#backup_download') - ->name('backup_download'); + ->name('backup_download'); $admin_user->delete('/backups/:id')->to('backup#delete_backup') - ->name('backup_delete'); + ->name('backup_delete'); $admin_user->put('/backups/:id')->to('backup#restore_backup') - ->name('backup_restore'); - $admin_user->delete('/backups')->to('backup#cleanup') - ->name('backup_cleanup'); - + ->name('backup_restore'); + $admin_user->delete('/backups')->to('backup#cleanup')->name('backup_cleanup'); ################ TYPES ################ $logged_user->get('/types')->to('types#all_our')->name('all_types'); - $manager_user->get('/types/add')->to('types#add_type') - ->name('add_type_get'); + $manager_user->get('/types/add')->to('types#add_type')->name('add_type_get'); $manager_user->post('/types/add')->to('types#post_add_type') - ->name('add_type_post'); + ->name('add_type_post'); $manager_user->get('/types/manage/:name')->to('types#manage') - ->name('edit_type'); + ->name('edit_type'); $manager_user->get('/types/delete/:name')->to('types#delete_type') - ->name('delete_type'); + ->name('delete_type'); $manager_user->post('/types/store_description') - ->to('types#post_store_description')->name('update_type_description'); + ->to('types#post_store_description')->name('update_type_description'); $manager_user->get('/types/toggle/:name')->to('types#toggle_landing') - ->name('toggle_landing_type'); + ->name('toggle_landing_type'); $manager_user->get('/types/:our_type/map/:bibtex_type') - ->to('types#map_types'); + ->to('types#map_types'); $manager_user->get('/types/:our_type/unmap/:bibtex_type') - ->to('types#unmap_types')->name('unmap_bibtex_type'); + ->to('types#unmap_types')->name('unmap_bibtex_type'); ################ AUTHORS ################ $logged_user->get('/authors/')->to('authors#all_authors') - ->name('all_authors'); + ->name('all_authors'); $manager_user->get('/authors/add')->to('authors#add_author') - ->name('add_author'); + ->name('add_author'); $manager_user->post('/authors/add/')->to('authors#add_post'); $logged_user->get('/authors/edit/:id')->to('authors#edit_author') - ->name('edit_author'); + ->name('edit_author'); $manager_user->post('/authors/edit/')->to('authors#edit_post') - ->name('edit_author_post'); + ->name('edit_author_post'); $manager_user->get('/authors/delete/:id')->to('authors#delete_author') - ->name('delete_author'); - + ->name('delete_author'); $admin_user->get('/authors/delete/:id/force') - ->to('authors#delete_author_force'); - + ->to('authors#delete_author_force'); # for dev only!! - $admin_user->get('/authors/decimate') - ->to('authors#delete_invisible_authors'); - + $admin_user->get('/authors/decimate')->to('authors#delete_invisible_authors'); $manager_user->post('/authors/edit_membership_dates') - ->to('authors#post_edit_membership_dates') - ->name('edit_author_membership_dates'); + ->to('authors#post_edit_membership_dates') + ->name('edit_author_membership_dates'); $manager_user->get('/authors/:id/add_to_team/:tid') - ->to('authors#add_to_team')->name('add_author_to_team'); + ->to('authors#add_to_team')->name('add_author_to_team'); $manager_user->get('/authors/:id/remove_from_team/:tid') - ->to('authors#remove_from_team')->name('remove_author_from_team'); + ->to('authors#remove_from_team')->name('remove_author_from_team'); $manager_user->get('/authors/:masterid/remove_uid/:uid') - ->to('authors#remove_uid')->name('remove_author_uid'); + ->to('authors#remove_uid')->name('remove_author_uid'); $manager_user->post('/authors/merge/')->to('authors#merge_authors') - ->name('merge_authors'); + ->name('merge_authors'); $admin_user->get('/authors/fix_masters')->to('authors#fix_masters') - ->name('fix_masters'); + ->name('fix_masters'); $manager_user->get('/authors/reassign') - ->to('authors#reassign_authors_to_entries'); + ->to('authors#reassign_authors_to_entries'); $admin_user->get('/authors/reassign_and_create') - ->to('authors#reassign_authors_to_entries_and_create_authors'); + ->to('authors#reassign_authors_to_entries_and_create_authors'); $manager_user->get('/authors/toggle_visibility/:id') - ->to('authors#toggle_visibility')->name('toggle_author_visibility'); + ->to('authors#toggle_visibility')->name('toggle_author_visibility'); # $logged_user->get('/authors/toggle_visibility') # ->to('authors#toggle_visibility'); @@ -709,22 +669,21 @@ sub setup_routes { $logged_user->get('/tagtypes')->to('tagtypes#index')->name('all_tag_types'); $admin_user->get('/tagtypes/add')->to('tagtypes#add')->name('add_tag_type'); $admin_user->post('/tagtypes/add')->to('tagtypes#add_post') - ->name('add_tag_type_post'); + ->name('add_tag_type_post'); $admin_user->get('/tagtypes/delete/:id')->to('tagtypes#delete') - ->name('delete_tag_type'); + ->name('delete_tag_type'); $manager_user->any('/tagtypes/edit/:id')->to('tagtypes#edit') - ->name('edit_tag_type'); + ->name('edit_tag_type'); ################ TAGS ################ - $logged_user->get('/tags/:type')->to( 'tags#index', type => 1 ) - ->name('all_tags'); - $admin_user->get('/tags/add/:type')->to( 'tags#add', type => 1 ) - ->name('add_tag_get'); - $admin_user->post('/tags/add/:type')->to( 'tags#add_post', type => 1 ) - ->name('add_tag_post'); + $logged_user->get('/tags/:type')->to('tags#index', type => 1) + ->name('all_tags'); + $admin_user->get('/tags/add/:type')->to('tags#add', type => 1) + ->name('add_tag_get'); + $admin_user->post('/tags/add/:type')->to('tags#add_post', type => 1) + ->name('add_tag_post'); $logged_user->get('/tags/authors/:id/:type') - ->to( 'tags#get_authors_for_tag', type => 1 ) - ->name('get_authors_for_tag'); + ->to('tags#get_authors_for_tag', type => 1)->name('get_authors_for_tag'); $admin_user->get('/tags/delete/:id')->to('tags#delete')->name('delete_tag'); ### EDIT TAG FORM GOES WITH GET - WTF!?! @@ -732,31 +691,31 @@ sub setup_routes { $manager_user->get('/tags/edit/:id')->to('tags#edit')->name('edit_tag'); $anyone->get('/read/authors-for-tag/:tag_id/:team_id') - ->to('tags#get_authors_for_tag_and_team') - ->name('get_authors_for_tag_and_team'); + ->to('tags#get_authors_for_tag_and_team') + ->name('get_authors_for_tag_and_team'); #ALIAS $anyone->get('/r/a4t/:tag_id/:team_id') - ->to('tags#get_authors_for_tag_and_team') - ->name('get_authors_for_tag_and_team'); + ->to('tags#get_authors_for_tag_and_team') + ->name('get_authors_for_tag_and_team'); $anyone->get('/read/authors-for-tag/:tag_id/:team_id') - ->to('tags#get_authors_for_tag_and_team') - ->name('get_authors_for_tag_and_team'); + ->to('tags#get_authors_for_tag_and_team') + ->name('get_authors_for_tag_and_team'); #ALIAS $anyone->get('/r/a4t/:tag_id/:team_id') - ->to('tags#get_authors_for_tag_and_team') - ->name('get_authors_for_tag_and_team'); + ->to('tags#get_authors_for_tag_and_team') + ->name('get_authors_for_tag_and_team'); $anyone->get('/read/tags-for-author/:author_id') - ->to('tags#get_tags_for_author_read')->name('tags_for_author'); + ->to('tags#get_tags_for_author_read')->name('tags_for_author'); #ALIAS $anyone->get('/r/t4a/:author_id')->to('tags#get_tags_for_author_read'); $anyone->get('/read/tags-for-team/:team_id') - ->to('tags#get_tags_for_team_read')->name('tags_for_team'); + ->to('tags#get_tags_for_team_read')->name('tags_for_team'); #ALIAS $anyone->get('/r/t4t/:team_id')->to('tags#get_tags_for_team_read'); @@ -767,15 +726,14 @@ sub setup_routes { $manager_user->get('/teams/edit/:id')->to('teams#edit')->name('edit_team'); $manager_user->get('/teams/delete/:id')->to('teams#delete_team') - ->name('delete_team'); - $manager_user->get('/teams/delete/:id/force') - ->to('teams#delete_team_force')->name('delete_team_force'); + ->name('delete_team'); + $manager_user->get('/teams/delete/:id/force')->to('teams#delete_team_force') + ->name('delete_team_force'); $logged_user->get('/teams/unrealted_papers/:teamid') - ->to('publications#show_unrelated_to_team') - ->name('unrelated_papers_for_team'); + ->to('publications#show_unrelated_to_team') + ->name('unrelated_papers_for_team'); - $manager_user->get('/teams/add')->to('teams#add_team') - ->name('add_team_get'); + $manager_user->get('/teams/add')->to('teams#add_team')->name('add_team_get'); $manager_user->post('/teams/add/')->to('teams#add_team_post'); ################ EDITING PUBLICATIONS ################ @@ -1014,22 +972,21 @@ sub setup_hooks { before_dispatch => sub { my $c = shift; - if ( $c->req->headers->header('X-Forwarded-HTTPS') ) { + if ($c->req->headers->header('X-Forwarded-HTTPS')) { $c->req->url->base->scheme('https'); } - $c->app->statistics->log_url( $c->req->url ); - + $c->app->statistics->log_url($c->req->url); # dirty fix for production deployment in a directory # config->{proxy_prefix} stores the proxy prefix, e.g., /app my $proxy_prefix = $self->config->{proxy_prefix}; - if ( $proxy_prefix ne "" ) { + if ($proxy_prefix ne "") { # we remove the leading slash $proxy_prefix =~ s!^/!!; # and let Mojolicious add it again - push @{ $c->req->url->base->path->trailing_slash(1) }, $proxy_prefix; + push @{$c->req->url->base->path->trailing_slash(1)}, $proxy_prefix; } } ); diff --git a/lib/BibSpace/Backend/IBibSpaceBackend.pm b/lib/BibSpace/Backend/IBibSpaceBackend.pm index d09b088..7c18bea 100644 --- a/lib/BibSpace/Backend/IBibSpaceBackend.pm +++ b/lib/BibSpace/Backend/IBibSpaceBackend.pm @@ -1,5 +1,5 @@ package IBibSpaceBackend; -use v5.16; +use v5.16; use Try::Tiny; use Data::Dumper; use namespace::autoclean; @@ -16,6 +16,5 @@ requires 'delete'; requires 'filter'; requires 'find'; - 1; diff --git a/lib/BibSpace/Backend/SmartArray.pm b/lib/BibSpace/Backend/SmartArray.pm index 0a5ee7a..16f62fb 100644 --- a/lib/BibSpace/Backend/SmartArray.pm +++ b/lib/BibSpace/Backend/SmartArray.pm @@ -1,6 +1,6 @@ package SmartArray; -use v5.16; +use v5.16; use Try::Tiny; use Data::Dumper; use namespace::autoclean; @@ -19,8 +19,7 @@ use List::MoreUtils qw(first_index); use feature qw( say ); use MooseX::Storage; -with Storage( format => 'JSON', 'io' => 'File' ); - +with Storage(format => 'JSON', 'io' => 'File'); =item This is a in-memory data structure (hash) to hold all objects of BibSpace. @@ -30,159 +29,161 @@ with Storage( format => 'JSON', 'io' => 'File' ); String "TypeName" => { Integer UID => Object with type TypeName}. =cut -has 'logger' => ( is => 'ro', does => 'ILogger', required => 1, traits => ['DoNotSerialize']); +has 'logger' => + (is => 'ro', does => 'ILogger', required => 1, traits => ['DoNotSerialize']); has 'data' => ( - traits => ['Hash'], - is => 'ro', - isa => 'HashRef[ArrayRef[BibSpace::Model::IEntity]]', - default => sub { {} }, - handles => { - set => 'set', - get => 'get', - has => 'exists', - defined => 'defined', - keys => 'keys', - # values => 'values', - num => 'count', - pairs => 'kv', - _clear => 'clear', - }, + traits => ['Hash'], + is => 'ro', + isa => 'HashRef[ArrayRef[BibSpace::Model::IEntity]]', + default => sub { {} }, + handles => { + set => 'set', + get => 'get', + has => 'exists', + defined => 'defined', + keys => 'keys', + + # values => 'values', + num => 'count', + pairs => 'kv', + _clear => 'clear', + }, ); sub reset_data { - my $self = shift; - $self->logger->warn("Resetting SmartArray"); - $self->_clear; + my $self = shift; + $self->logger->warn("Resetting SmartArray"); + $self->_clear; } sub dump { - my $self = shift; - $self->logger->debug("SmartArray keys: ".join(', ', $self->keys)); + my $self = shift; + $self->logger->debug("SmartArray keys: " . join(', ', $self->keys)); } sub _init { - my ($self, $type) = @_; - die "_init requires a type!" unless $type; - if(!$self->defined($type)){ - $self->set($type, []); - } + my ($self, $type) = @_; + die "_init requires a type!" unless $type; + if (!$self->defined($type)) { + $self->set($type, []); + } } sub all { - my ($self, $type) = @_; + my ($self, $type) = @_; - $self->logger->error("SmartArray->all requires a type! Type: $type.") unless $type; - $self->_init($type); - my $aref = $self->get($type); - return @{ $aref }; + $self->logger->error("SmartArray->all requires a type! Type: $type.") + unless $type; + $self->_init($type); + my $aref = $self->get($type); + return @{$aref}; } sub _add { - my ($self, @objects) = @_; - my $type = ref($objects[0]); - $self->_init($type); - push @{$self->get($type)}, @objects; + my ($self, @objects) = @_; + my $type = ref($objects[0]); + $self->_init($type); + push @{$self->get($type)}, @objects; } sub save { - my ($self, @objects) = @_; - my $added = 0; - - # if there are multiple objects to add and the array is empty -> do it quicker! - if( @objects > 0 ){ - my $type = ref($objects[0]); - - if( $self->empty($type) ){ - $self->_add(@objects); - $added = scalar @objects; - return $added; - } + my ($self, @objects) = @_; + my $added = 0; + + # if there are multiple objects to add and the array is empty -> do it quicker! + if (@objects > 0) { + my $type = ref($objects[0]); + + if ($self->empty($type)) { + $self->_add(@objects); + $added = scalar @objects; + return $added; + } + } + + foreach my $obj (@objects) { + if (!$self->exists($obj)) { + ++$added; + $self->_add($obj); } - - - foreach my $obj(@objects){ - if( !$self->exists($obj)){ - ++$added; - $self->_add($obj); - } - else{ - $self->update($obj); - } + else { + $self->update($obj); } - - return $added; + } + + return $added; } -sub count { - my ($self, $type) = @_; - die "all requires a type!" unless $type; - return scalar $self->all($type); +sub count { + my ($self, $type) = @_; + die "all requires a type!" unless $type; + return scalar $self->all($type); } -sub empty { - my ($self, $type) = @_; - return $self->count($type) == 0; +sub empty { + my ($self, $type) = @_; + return $self->count($type) == 0; } ## this is mega slow for relations!!! -sub exists { - my ($self, $object) = @_; - my $type = ref($object); - $self->logger->error("SmartArray->exists requires a type! Object: '$object', type: '$type'.") unless $type; - my $found = first {$_->equals($object)} $self->all($type); - return defined $found; +sub exists { + my ($self, $object) = @_; + my $type = ref($object); + $self->logger->error( + "SmartArray->exists requires a type! Object: '$object', type: '$type'.") + unless $type; + my $found = first { $_->equals($object) } $self->all($type); + return defined $found; } -sub update { - my ($self, @objects) = @_; - # should happen automatically beacuse array keeps references to objects +sub update { + my ($self, @objects) = @_; + + # should happen automatically beacuse array keeps references to objects } -sub delete { - my ($self, @objects) = @_; - my $type = ref($objects[0]); - my $aref = $self->get($type); - my @removed; - foreach my $obj (@objects){ - my $idx = first_index { $_ == $obj } @{$aref}; - push @removed, splice( @{$aref}, $idx, 1) if $idx > -1; - } - return @removed; +sub delete { + my ($self, @objects) = @_; + my $type = ref($objects[0]); + my $aref = $self->get($type); + my @removed; + foreach my $obj (@objects) { + my $idx = first_index { $_ == $obj } @{$aref}; + push @removed, splice(@{$aref}, $idx, 1) if $idx > -1; + } + return @removed; } +sub filter { + my ($self, $type, $coderef) = @_; + + my $t0 = [gettimeofday]; -sub filter { - my ($self, $type, $coderef) = @_; + return () if $self->empty($type); + my @arr = grep &{$coderef}, $self->all($type); - my $t0 = [gettimeofday]; - - return () if $self->empty($type); - my @arr = grep &{$coderef}, $self->all($type); - - my $dur = tv_interval ( $t0, [gettimeofday]); - say "Filtering in SArray '$type': $dur" if $dur > 0.01; - return @arr; + my $dur = tv_interval($t0, [gettimeofday]); + say "Filtering in SArray '$type': $dur" if $dur > 0.01; + return @arr; } -sub find { +sub find { my ($self, $type, $coderef) = @_; my $t0 = [gettimeofday]; - + return undef if $self->empty($type); my $obj = first \&{$coderef}, $self->all($type); - - my $dur = tv_interval ( $t0, [gettimeofday]); + + my $dur = tv_interval($t0, [gettimeofday]); say "Finding in SArray '$type': $dur" if $dur > 0.01; - + return $obj; } # Moose::Meta::Attribute::Native::Trait::Array - - __PACKAGE__->meta->make_immutable; no Moose; -1; \ No newline at end of file +1; diff --git a/lib/BibSpace/Backend/SmartHash.pm b/lib/BibSpace/Backend/SmartHash.pm index a9789aa..b037645 100644 --- a/lib/BibSpace/Backend/SmartHash.pm +++ b/lib/BibSpace/Backend/SmartHash.pm @@ -1,6 +1,6 @@ package SmartHash; -use v5.16; +use v5.16; use Try::Tiny; use Data::Dumper; use namespace::autoclean; @@ -27,157 +27,157 @@ use feature qw( say ); String "TypeName" => { Integer UID => Object with type TypeName}. =cut -has 'logger' => ( is => 'ro', does => 'ILogger', required => 1); +has 'logger' => (is => 'ro', does => 'ILogger', required => 1); has 'data' => ( - traits => ['Hash'], - is => 'ro', - isa => 'HashRef[HashRef[BibSpace::Model::IEntity]]', - default => sub { {} }, - handles => { - set => 'set', - get => 'get', - has => 'exists', - defined => 'defined', - keys => 'keys', - # values => 'values', - num => 'count', - pairs => 'kv', - _clear => 'clear', - }, + traits => ['Hash'], + is => 'ro', + isa => 'HashRef[HashRef[BibSpace::Model::IEntity]]', + default => sub { {} }, + handles => { + set => 'set', + get => 'get', + has => 'exists', + defined => 'defined', + keys => 'keys', + + # values => 'values', + num => 'count', + pairs => 'kv', + _clear => 'clear', + }, ); sub reset_data { - my $self = shift; - $self->logger->warn("Resetting SmartHash"); - $self->_clear; + my $self = shift; + $self->logger->warn("Resetting SmartHash"); + $self->_clear; } sub dump { - my $self = shift; - $self->logger->debug("SmartHash keys: ".join(', ', $self->keys)); + my $self = shift; + $self->logger->debug("SmartHash keys: " . join(', ', $self->keys)); } sub _init { - my ($self, $type) = @_; - die "_init requires a type!" unless $type; - if(!$self->defined($type)){ - $self->set($type, {}); - } + my ($self, $type) = @_; + die "_init requires a type!" unless $type; + if (!$self->defined($type)) { + $self->set($type, {}); + } } sub all { - my ($self, $type) = @_; - die "all requires a type!" unless $type; - $self->_init($type); - my $href = $self->get($type); - - my @result; - if($href){ - @result = values %$href; - } - return @result; + my ($self, $type) = @_; + die "all requires a type!" unless $type; + $self->_init($type); + my $href = $self->get($type); + + my @result; + if ($href) { + @result = values %$href; + } + return @result; } sub _add { - my ($self, @objects) = @_; - return if scalar(@objects) == 0; - - my $type = ref($objects[0]); - - $self->_init($type); - my $href = $self->get($type); - - my $num_added = 0; - foreach my $obj (@objects){ - $href->{$obj->id} = $obj; - $num_added++; - } - return $num_added; + my ($self, @objects) = @_; + return if scalar(@objects) == 0; + + my $type = ref($objects[0]); + + $self->_init($type); + my $href = $self->get($type); + + my $num_added = 0; + foreach my $obj (@objects) { + $href->{$obj->id} = $obj; + $num_added++; + } + return $num_added; } sub save { - my ($self, @objects) = @_; - return $self->_add(@objects); + my ($self, @objects) = @_; + return $self->_add(@objects); } -sub count { - my ($self, $type) = @_; - die "all requires a type!" unless $type; - return 0 if $self->empty($type); +sub count { + my ($self, $type) = @_; + die "all requires a type!" unless $type; + return 0 if $self->empty($type); - $self->_init($type); - my $href = $self->get($type); - return scalar keys %$href; + $self->_init($type); + my $href = $self->get($type); + return scalar keys %$href; } -sub empty { - my ($self, $type) = @_; - $self->_init($type); - my $href = $self->get($type); - return scalar(keys %$href) == 0; +sub empty { + my ($self, $type) = @_; + $self->_init($type); + my $href = $self->get($type); + return scalar(keys %$href) == 0; } -sub exists { - my ($self, $object) = @_; - my $type = ref($object); - $self->logger->error("SmartHash->exists requires a type! Object: '$object', type: '$type'.") unless $type; - my $href = $self->get($type); - return exists $href->{$object->id}; +sub exists { + my ($self, $object) = @_; + my $type = ref($object); + $self->logger->error( + "SmartHash->exists requires a type! Object: '$object', type: '$type'.") + unless $type; + my $href = $self->get($type); + return exists $href->{$object->id}; } -sub update { - my ($self, @objects) = @_; - return $self->_add(@objects); +sub update { + my ($self, @objects) = @_; + return $self->_add(@objects); } -sub delete { - my ($self, @objects) = @_; - my $type = ref($objects[0]); - my $href = $self->get($type); - - my @removed; - foreach my $obj (@objects){ - push @removed, delete $href->{$obj->id}; - } - - return @removed; +sub delete { + my ($self, @objects) = @_; + my $type = ref($objects[0]); + my $href = $self->get($type); + + my @removed; + foreach my $obj (@objects) { + push @removed, delete $href->{$obj->id}; + } + + return @removed; } - -sub filter { - my ($self, $type, $coderef) = @_; +sub filter { + my ($self, $type, $coderef) = @_; - my $t0 = [gettimeofday]; + my $t0 = [gettimeofday]; - return () if $self->empty($type); - my @arr = grep &{$coderef}, $self->all($type); + return () if $self->empty($type); + my @arr = grep &{$coderef}, $self->all($type); - my $dur = tv_interval ( $t0, [gettimeofday]); - say "Filtering in SHash '$type': $dur" if $dur > 0.01; + my $dur = tv_interval($t0, [gettimeofday]); + say "Filtering in SHash '$type': $dur" if $dur > 0.01; - return @arr; + return @arr; } -sub find { - my ($self, $type, $coderef) = @_; +sub find { + my ($self, $type, $coderef) = @_; - my $t0 = [gettimeofday]; + my $t0 = [gettimeofday]; - return undef if $self->empty($type); - my $obj = first \&{$coderef}, $self->all($type); + return undef if $self->empty($type); + my $obj = first \&{$coderef}, $self->all($type); - my $dur = tv_interval ( $t0, [gettimeofday]); - say "Finding in SHash '$type': $dur" if $dur > 0.01; + my $dur = tv_interval($t0, [gettimeofday]); + say "Finding in SHash '$type': $dur" if $dur > 0.01; - return $obj; + return $obj; } # Moose::Meta::Attribute::Native::Trait::Array - - __PACKAGE__->meta->make_immutable; no Moose; -1; \ No newline at end of file +1; diff --git a/lib/BibSpace/Controller/Authors.pm b/lib/BibSpace/Controller/Authors.pm index 03ce101..852eaca 100644 --- a/lib/BibSpace/Controller/Authors.pm +++ b/lib/BibSpace/Controller/Authors.pm @@ -1,18 +1,17 @@ package BibSpace::Controller::Authors; - use Data::Dumper; use utf8; use Text::BibTeX; # parsing bib files use DateTime; use Try::Tiny; + # use File::Slurp; use v5.16; #because of ~~ use strict; use warnings; - use List::MoreUtils qw(any uniq); use BibSpace::Functions::Core; @@ -23,49 +22,51 @@ use BibSpace::Functions::FPublications; use Mojo::Base 'Mojolicious::Controller'; use Mojo::Base 'Mojolicious::Plugin::Config'; - ############################################################################################################## sub all_authors { # refactored my $self = shift; my $visible = $self->param('visible'); my $search = $self->param('search'); my $letter = $self->param('letter'); - my @authors = $self->app->repo->authors_all; - if ( defined $visible ) { + if (defined $visible) { @authors = grep { $_->display == $visible } @authors; } - + @authors = grep { $_->is_master } @authors; - if ( $letter) { - @authors = grep { ( substr( $_->master, 0, 1 ) cmp $letter ) == 0 } @authors; + if ($letter) { + @authors = grep { (substr($_->master, 0, 1) cmp $letter) == 0 } @authors; } my @letters; - if( defined $visible){ - @letters = map { substr( $_->master, 0, 1 ) } $self->app->repo->authors_filter(sub{ $_->display == $visible }); + if (defined $visible) { + @letters = map { substr($_->master, 0, 1) } + $self->app->repo->authors_filter(sub { $_->display == $visible }); } - else{ - @letters = map { substr( $_->master, 0, 1 ) } $self->app->repo->authors_all; + else { + @letters = map { substr($_->master, 0, 1) } $self->app->repo->authors_all; } @letters = uniq @letters; @letters = sort @letters; + @authors = sort { $a->uid cmp $b->uid } @authors; + $self->stash( + authors => \@authors, + letters => \@letters, + selected_letter => $letter, + visible => $visible + ); - @authors = sort {$a->uid cmp $b->uid} @authors; - - $self->stash( authors => \@authors, letters => \@letters, selected_letter => $letter, visible => $visible ); - - $self->render( template => 'authors/authors' ); + $self->render(template => 'authors/authors'); } ############################################################################################################## sub add_author { my $self = shift; - $self->stash( master => '', id => '' ); - $self->render( template => 'authors/add_author' ); + $self->stash(master => '', id => ''); + $self->render(template => 'authors/add_author'); } ############################################################################################################## @@ -73,67 +74,74 @@ sub add_post { my $self = shift; my $new_master = $self->param('new_master'); - if ( defined $new_master and length($new_master) > 0 ) { + if (defined $new_master and length($new_master) > 0) { - my $author = $self->app->repo->authors_find( sub { $_->master eq $new_master } ); + my $author + = $self->app->repo->authors_find(sub { $_->master eq $new_master }); - if ( !defined $author ) { # no such user exists yet + if (!defined $author) { # no such user exists yet - $author = $self->app->entityFactory->new_Author( uid => $new_master ); + $author = $self->app->entityFactory->new_Author(uid => $new_master); $self->app->repo->authors_save($author); - if ( !defined $author->id ) { + if (!defined $author->id) { $self->flash( msg_type => 'danger', - msg => "Error saving author. Saving to the database returned no insert row id." + msg => + "Error saving author. Saving to the database returned no insert row id." ); - $self->redirect_to( $self->url_for('add_author') ); + $self->redirect_to($self->url_for('add_author')); return; } - $self->app->logger->info( "Added new author with master: $new_master. Author id is " . $author->{id} ); - $self->flash( msg_type => 'success', msg => "Author added successfully!" ); - $self->redirect_to( $self->url_for( 'edit_author', id => $author->{id} ) ); + $self->app->logger->info( + "Added new author with master: $new_master. Author id is " + . $author->{id}); + $self->flash(msg_type => 'success', msg => "Author added successfully!"); + $self->redirect_to($self->url_for('edit_author', id => $author->{id})); return; } else { # such user already exists! - $self->app->logger->info("Author with master: $new_master already exists!"); + $self->app->logger->info( + "Author with master: $new_master already exists!"); $self->flash( msg_type => 'warning', - msg => "Author with proposed master: $new_master already exists! Pick a different one." + msg => + "Author with proposed master: $new_master already exists! Pick a different one." ); - $self->redirect_to( $self->url_for('add_author') ); + $self->redirect_to($self->url_for('add_author')); return; } } - $self->flash( msg_type => 'warning', msg => "Bad input." ); - $self->redirect_to( $self->url_for('add_author') ); + $self->flash(msg_type => 'warning', msg => "Bad input."); + $self->redirect_to($self->url_for('add_author')); } ############################################################################################################## sub edit_author { my $self = shift; my $id = $self->param('id'); - my $author = $self->app->repo->authors_find( sub { $_->id == $id } ); + my $author = $self->app->repo->authors_find(sub { $_->id == $id }); - - if ( !defined $author ) { - $self->flash( msg => "Author with id $id does not exist!", msg_type => "danger" ); - $self->redirect_to( $self->url_for('all_authors') ); + if (!defined $author) { + $self->flash( + msg => "Author with id $id does not exist!", + msg_type => "danger" + ); + $self->redirect_to($self->url_for('all_authors')); } else { - my @all_teams = $self->app->repo->teams_all; my @author_teams = $author->get_teams; my @author_tags = $author->get_tags; # cannot use objects as keys due to hash stringification! my %author_teams_hash = map { $_->id => 1 } @author_teams; - my @unassigned_teams = grep { not $author_teams_hash{ $_->id } } @all_teams; - + my @unassigned_teams = grep { not $author_teams_hash{$_->id} } @all_teams; - my @minor_authors = $self->app->repo->authors_filter( sub { $_->is_minion_of($author) } ); + my @minor_authors + = $self->app->repo->authors_filter(sub { $_->is_minion_of($author) }); # $author->all_author_user_ids($dbh); @@ -145,7 +153,7 @@ sub edit_author { all_teams => \@all_teams, unassigned_teams => \@unassigned_teams ); - $self->render( template => 'authors/edit_author' ); + $self->render(template => 'authors/edit_author'); } } ############################################################################################################## @@ -154,10 +162,10 @@ sub add_to_team { my $master_id = $self->param('id'); my $team_id = $self->param('tid'); - my $author = $self->app->repo->authors_find( sub { $_->id == $master_id } ); - my $team = $self->app->repo->teams_find( sub { $_->id == $team_id } ); + my $author = $self->app->repo->authors_find(sub { $_->id == $master_id }); + my $team = $self->app->repo->teams_find(sub { $_->id == $team_id }); - if ( defined $author and defined $team ) { + if (defined $author and defined $team) { my $membership = Membership->new( author => $author->get_master, team => $team, @@ -169,14 +177,20 @@ sub add_to_team { $author->add_membership($membership); $self->flash( - msg => "Author " . $author->uid . " has just joined team " . $team->name . "", + msg => "Author " + . $author->uid + . " has just joined team " + . $team->name . "", msg_type => "success" ); } else { - $self->flash( msg => "Author or team does does not exist!", msg_type => "danger" ); + $self->flash( + msg => "Author or team does does not exist!", + msg_type => "danger" + ); } - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } ############################################################################################################## sub remove_from_team { @@ -184,73 +198,91 @@ sub remove_from_team { my $master_id = $self->param('id'); my $team_id = $self->param('tid'); - my $author = $self->app->repo->authors_find( sub { $_->id == $master_id } ); - my $team = $self->app->repo->teams_find( sub { $_->id == $team_id } ); + my $author = $self->app->repo->authors_find(sub { $_->id == $master_id }); + my $team = $self->app->repo->teams_find(sub { $_->id == $team_id }); - if ( defined $author and defined $team ) { + if (defined $author and defined $team) { my $membership = $author->memberships_find(sub { $_->team->equals($team) }); $author->remove_membership($membership); $team->remove_membership($membership); $self->app->repo->memberships_delete($membership); $self->flash( - msg => "Author " . $author->uid . " has just left team " . $team->name . "", + msg => "Author " + . $author->uid + . " has just left team " + . $team->name . "", msg_type => "success" ); } else { - $self->flash( msg => "Author or team does does not exist!", msg_type => "danger" ); + $self->flash( + msg => "Author or team does does not exist!", + msg_type => "danger" + ); } - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } ############################################################################################################## sub remove_uid { my $self = shift; my $master_id = $self->param('masterid'); my $minor_id = $self->param('uid'); - - my $author_master = $self->app->repo->authors_find( sub { $_->id == $master_id } ); - my $author_minor = $self->app->repo->authors_find( sub { $_->id == $minor_id } ); + my $author_master + = $self->app->repo->authors_find(sub { $_->id == $master_id }); + my $author_minor + = $self->app->repo->authors_find(sub { $_->id == $minor_id }); - if ( !defined $author_minor ) { - $self->flash( msg => "Cannot remove user_id $minor_id. Reason: such author deos not exist.", msg_type => "danger" ); + if (!defined $author_minor) { + $self->flash( + msg => + "Cannot remove user_id $minor_id. Reason: such author deos not exist.", + msg_type => "danger" + ); } - elsif ( $author_minor->is_master ) { - $self->flash( msg => "Cannot remove user_id $minor_id. Reason: it is a master_id.", msg_type => "warning" ); + elsif ($author_minor->is_master) { + $self->flash( + msg => "Cannot remove user_id $minor_id. Reason: it is a master_id.", + msg_type => "warning" + ); } else { my @master_entries = $author_master->get_entries; # remove master authorships from both authors - foreach my $master_authorship ( $author_master->authorships_all ){ - $author_master->remove_authorship($master_authorship); - $author_minor->remove_authorship($master_authorship); + foreach my $master_authorship ($author_master->authorships_all) { + $author_master->remove_authorship($master_authorship); + $author_minor->remove_authorship($master_authorship); - $master_authorship->entry->remove_authorship($master_authorship); - $self->app->repo->authorships_delete($master_authorship); + $master_authorship->entry->remove_authorship($master_authorship); + $self->app->repo->authorships_delete($master_authorship); } + # remove minion authorships from both authors - foreach my $minion_authorship ( $author_minor->authorships_all ){ - $author_minor->remove_authorship($minion_authorship); - $author_master->remove_authorship($minion_authorship); + foreach my $minion_authorship ($author_minor->authorships_all) { + $author_minor->remove_authorship($minion_authorship); + $author_master->remove_authorship($minion_authorship); - $minion_authorship->entry->remove_authorship($minion_authorship); - $self->app->repo->authorships_delete($minion_authorship); + $minion_authorship->entry->remove_authorship($minion_authorship); + $self->app->repo->authorships_delete($minion_authorship); } + # unlink authors $author_minor->remove_master; + # save changes (minor should be enough) $self->app->repo->authors_update($author_master); $self->app->repo->authors_update($author_minor); # calculate proper authorships automatically - Freassign_authors_to_entries_given_by_array($self->app, 0, \@master_entries); + Freassign_authors_to_entries_given_by_array($self->app, 0, + \@master_entries); } - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } ############################################################################################################## sub merge_authors { @@ -258,35 +290,44 @@ sub merge_authors { my $destination_id = $self->param('author_to'); my $source_id = $self->param('author_from'); - my $author_destination = $self->app->repo->authors_find( sub { $_->id == $destination_id } ); - $author_destination ||= $self->app->repo->authors_find( sub { $_->uid eq $destination_id } ); + my $author_destination + = $self->app->repo->authors_find(sub { $_->id == $destination_id }); + $author_destination + ||= $self->app->repo->authors_find(sub { $_->uid eq $destination_id }); - my $author_source = $self->app->repo->authors_find( sub { $_->id == $source_id } ); - $author_source ||= $self->app->repo->authors_find( sub { $_->uid eq $source_id } ); + my $author_source + = $self->app->repo->authors_find(sub { $_->id == $source_id }); + $author_source + ||= $self->app->repo->authors_find(sub { $_->uid eq $source_id }); my $copy_name = $author_source->uid; my $success = 0; - if ( defined $author_source and defined $author_destination ) { - if ( $author_destination->can_merge_authors($author_source) ) { + if (defined $author_source and defined $author_destination) { + if ($author_destination->can_merge_authors($author_source)) { my @src_authorships = $author_source->authorships_all; - foreach my $src_authorship ( @src_authorships ){ - # Removing the authorship from the source author - $src_authorship->author->remove_authorship($src_authorship); - # authorships cannot be updated, so we need to delete and add later - $self->app->repo->authorships_delete($src_authorship); - # Changing the authorship to point to a new author - $src_authorship->author($author_destination); - # store changes the authorship in the repo - $self->app->repo->authorships_save($src_authorship); - # Adding the authorship to the new author - $author_destination->add_authorship($src_authorship); + foreach my $src_authorship (@src_authorships) { + + # Removing the authorship from the source author + $src_authorship->author->remove_authorship($src_authorship); + + # authorships cannot be updated, so we need to delete and add later + $self->app->repo->authorships_delete($src_authorship); + + # Changing the authorship to point to a new author + $src_authorship->author($author_destination); + + # store changes the authorship in the repo + $self->app->repo->authorships_save($src_authorship); + + # Adding the authorship to the new author + $author_destination->add_authorship($src_authorship); } $author_source->memberships_clear; $author_source->set_master($author_destination); - + $self->app->repo->authors_save($author_destination); $self->app->repo->authors_save($author_source); @@ -300,18 +341,23 @@ sub merge_authors { ); } else { - $self->flash( msg => "An author cannot be merged with its self. ", msg_type => "danger" ); + $self->flash( + msg => "An author cannot be merged with its self. ", + msg_type => "danger" + ); } } else { - $self->flash( msg => "Authors cannot be merged. One or both authors do not exist.", msg_type => "danger" ); + $self->flash( + msg => "Authors cannot be merged. One or both authors do not exist.", + msg_type => "danger" + ); } - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } - ############################################################################################################## sub edit_post { my $self = shift; @@ -320,55 +366,60 @@ sub edit_post { my $new_user_id = $self->param('new_user_id'); my $visibility = $self->param('visibility'); - my $author = $self->app->repo->authors_find( sub { $_->id == $id } ); + my $author = $self->app->repo->authors_find(sub { $_->id == $id }); - if ( defined $author ) { - if ( defined $new_master ) { + if (defined $author) { + if (defined $new_master) { - my $existing = $self->app->repo->authors_find( sub { ( $_->master cmp $new_master ) == 0 } ); + my $existing = $self->app->repo->authors_find( + sub { ($_->master cmp $new_master) == 0 }); - if ( !defined $existing ) { + if (!defined $existing) { $author->update_master_name($new_master); $self->app->repo->authors_save($author); - $self->flash( msg => "Master name has been updated successfully.", msg_type => "success" ); - $self->redirect_to( $self->url_for( 'edit_author', id => $author->id ) ); + $self->flash( + msg => "Master name has been updated successfully.", + msg_type => "success" + ); + $self->redirect_to($self->url_for('edit_author', id => $author->id)); } else { $self->flash( msg => "This master name is already taken by url_for( 'edit_author', id => $existing->id ) . "\">" + . $self->url_for('edit_author', id => $existing->id) . "\">" . $existing->master . ".", msg_type => "danger" ); - $self->redirect_to( $self->url_for( 'edit_author', id => $id ) ); + $self->redirect_to($self->url_for('edit_author', id => $id)); } - } - elsif ( defined $visibility ) { + elsif (defined $visibility) { $author->toggle_visibility; $self->app->repo->authors_save($author); } - elsif ( defined $new_user_id ) { + elsif (defined $new_user_id) { - my $existing_author = $self->app->repo->authors_find( sub { $_->uid eq $new_user_id } ); + my $existing_author + = $self->app->repo->authors_find(sub { $_->uid eq $new_user_id }); - if ( defined $existing_author ) { + if (defined $existing_author) { $self->flash( - msg => "Cannot add user ID $new_user_id. Such ID already exist. Maybe you wan to merge authors?", + msg => + "Cannot add user ID $new_user_id. Such ID already exist. Maybe you wan to merge authors?", msg_type => "warning" ); } else { - my $minion = $self->app->entityFactory->new_Author( uid => $new_user_id ); + my $minion = $self->app->entityFactory->new_Author(uid => $new_user_id); $author->add_minion($minion); $self->app->repo->authors_save($author); $self->app->repo->authors_save($minion); } } } - $self->redirect_to( $self->url_for( 'edit_author', id => $id ) ); + $self->redirect_to($self->url_for('edit_author', id => $id)); } ############################################################################################################## sub post_edit_membership_dates { @@ -378,34 +429,41 @@ sub post_edit_membership_dates { my $new_start = $self->param('new_start'); my $new_stop = $self->param('new_stop'); - my $author = $self->app->repo->authors_find( sub { $_->id == $master_id } ); - my $team = $self->app->repo->teams_find( sub { $_->id == $team_id } ); + my $author = $self->app->repo->authors_find(sub { $_->id == $master_id }); + my $team = $self->app->repo->teams_find(sub { $_->id == $team_id }); - if ( $author and $team ) { + if ($author and $team) { my $search_mem = Membership->new( - author => $author->get_master, - team => $team, - author_id => $author->get_master->id, - team_id => $team->id - ); - my $membership = $self->app->repo->memberships_find( sub { $_->equals($search_mem) } ); + author => $author->get_master, + team => $team, + author_id => $author->get_master->id, + team_id => $team->id + ); + my $membership + = $self->app->repo->memberships_find(sub { $_->equals($search_mem) }); + + if ($membership) { - if( $membership ){ - $membership->start($new_start); $membership->stop($new_stop); - $self->app->repo->memberships_update($membership); - $self->flash( msg => "Membership updated successfully.", msg_type => "success" ); + $self->app->repo->memberships_update($membership); + $self->flash( + msg => "Membership updated successfully.", + msg_type => "success" + ); } - else{ - $self->flash( msg => "Cannot find membership.", msg_type => "danger" ); + else { + $self->flash(msg => "Cannot find membership.", msg_type => "danger"); } - $self->redirect_to( $self->url_for( 'edit_author', id => $author->id ) ); + $self->redirect_to($self->url_for('edit_author', id => $author->id)); return; } - - $self->flash( msg => "Cannot update membership: author or team not found.", msg_type => "danger" ); - $self->redirect_to( $self->get_referrer ); + + $self->flash( + msg => "Cannot update membership: author or team not found.", + msg_type => "danger" + ); + $self->redirect_to($self->get_referrer); } ############################################################################################################## @@ -413,16 +471,16 @@ sub delete_author { my $self = shift; my $id = $self->param('id'); - my $author = $self->app->repo->authors_find( sub { $_->{id} == $id } ); + my $author = $self->app->repo->authors_find(sub { $_->{id} == $id }); - if ( $author and $author->can_be_deleted() ) { + if ($author and $author->can_be_deleted()) { $self->delete_author_force(); } else { - $self->flash( msg => "Cannot delete author ID $id.", msg_type => "danger" ); + $self->flash(msg => "Cannot delete author ID $id.", msg_type => "danger"); } - $self->redirect_to( $self->url_for('all_authors') ); + $self->redirect_to($self->url_for('all_authors')); } ############################################################################################################## @@ -430,7 +488,7 @@ sub delete_author_force { my $self = shift; my $id = $self->param('id'); - my $author = $self->app->repo->authors_find( sub { $_->{id} == $id } ); + my $author = $self->app->repo->authors_find(sub { $_->{id} == $id }); if ($author) { @@ -438,45 +496,51 @@ sub delete_author_force { ## Deleting memberships my @memberships = $author->memberships_all; + # for each team, remove membership in this team - foreach my $membership ( @memberships ){ - $membership->team->remove_membership($membership); + foreach my $membership (@memberships) { + $membership->team->remove_membership($membership); } $self->app->repo->memberships_delete(@memberships); + # remove all memberships for this team $author->memberships_clear; ## Deleting authorships my @authorships = $author->authorships_all; + # for each team, remove authorship in this team - foreach my $authorship ( @authorships ){ - $authorship->entry->remove_authorship($authorship); + foreach my $authorship (@authorships) { + $authorship->entry->remove_authorship($authorship); } $self->app->repo->authorships_delete(@authorships); + # remove all authorships for this team $author->authorships_clear; # finally delete author $self->app->repo->authors_delete($author); - $self->app->logger->info( "Author " . $author->uid . " ID $id has been deleted." ); - $self->flash( msg => "Author " . $author->uid . " ID $id removed successfully.", msg_type => "success" ); + $self->app->logger->info( + "Author " . $author->uid . " ID $id has been deleted."); + $self->flash( + msg => "Author " . $author->uid . " ID $id removed successfully.", + msg_type => "success" + ); } else { - $self->flash( msg => "Cannot delete author ID $id.", msg_type => "danger" ); + $self->flash(msg => "Cannot delete author ID $id.", msg_type => "danger"); } - - $self->redirect_to( $self->url_for('all_authors') ); + $self->redirect_to($self->url_for('all_authors')); } - ############################################################################################################## ## do not use this on production! this is for making the tests faster!! sub delete_invisible_authors { my $self = shift; - my @authors = $self->app->repo->authors_filter( sub { !$_->is_visible } ); + my @authors = $self->app->repo->authors_filter(sub { !$_->is_visible }); foreach my $author (@authors) { @@ -484,34 +548,39 @@ sub delete_invisible_authors { ## Deleting memberships my @memberships = $author->memberships_all; + # for each team, remove membership in this team - foreach my $membership ( @memberships ){ - $membership->team->remove_membership($membership); + foreach my $membership (@memberships) { + $membership->team->remove_membership($membership); } $self->app->repo->memberships_delete(@memberships); + # remove all memberships for this team $author->memberships_clear; ## Deleting authorships my @authorships = $author->authorships_all; + # for each team, remove authorship in this team - foreach my $authorship ( @authorships ){ - # my $entry = $authorship->entry; - $authorship->entry->remove_authorship($authorship); - # $self->app->repo->entries_delete($entry); + foreach my $authorship (@authorships) { + + # my $entry = $authorship->entry; + $authorship->entry->remove_authorship($authorship); + + # $self->app->repo->entries_delete($entry); } $self->app->repo->authorships_delete(@authorships); + # remove all authorships for this team $author->authorships_clear; # finally delete author $self->app->repo->authors_delete($author); - $self->flash( msg => "Authors decimated! ", msg_type => "success" ); + $self->flash(msg => "Authors decimated! ", msg_type => "success"); } - - $self->redirect_to( $self->url_for('all_authors') ); + $self->redirect_to($self->url_for('all_authors')); } @@ -520,12 +589,15 @@ sub reassign_authors_to_entries { my $self = shift; my $create_new = shift // 0; - my @all_entries = $self->app->repo->entries_all; - my $num_authors_created = Freassign_authors_to_entries_given_by_array($self->app, $create_new, \@all_entries); + my @all_entries = $self->app->repo->entries_all; + my $num_authors_created + = Freassign_authors_to_entries_given_by_array($self->app, $create_new, + \@all_entries); - $self->flash( msg => - "Reassignment with author creation has finished. $num_authors_created authors have been created or assigned." ); - $self->redirect_to( $self->get_referrer ); + $self->flash(msg => + "Reassignment with author creation has finished. $num_authors_created authors have been created or assigned." + ); + $self->redirect_to($self->get_referrer); } ############################################################################################################## sub reassign_authors_to_entries_and_create_authors { @@ -536,57 +608,70 @@ sub reassign_authors_to_entries_and_create_authors { sub fix_masters { my $self = shift; - my @all_authors = $self->app->repo->authors_all; + my @all_authors = $self->app->repo->authors_all; + + my @broken_authors_0 + = grep { $_->is_minion and !defined $_->masterObj } @all_authors; - my @broken_authors_0 = grep { $_->is_minion and !defined $_->masterObj } @all_authors; # masterObj not set although it should be - my @broken_authors_1 = grep { !defined $_->masterObj and $_->master_id != $_->id } @all_authors; + my @broken_authors_1 + = grep { !defined $_->masterObj and $_->master_id != $_->id } @all_authors; + # masterObj set incorrectly - my @broken_authors_2 = grep { $_->masterObj and $_->master_id != $_->masterObj->id } @all_authors; + my @broken_authors_2 + = grep { $_->masterObj and $_->master_id != $_->masterObj->id } + @all_authors; my $num_fixes_0 = @broken_authors_0; my $num_fixes_1 = @broken_authors_1; my $num_fixes_2 = @broken_authors_2; - - my $msg_type = ($num_fixes_0 + $num_fixes_1 + $num_fixes_2) == 0 ? 'success' : 'danger'; + my $msg_type + = ($num_fixes_0 + $num_fixes_1 + $num_fixes_2) == 0 ? 'success' : 'danger'; my $msg = "Analysis is finished. Authors broken:

    -
  • ".scalar(@broken_authors_0)." of type 0 (is minion but master undefined)
  • -
  • ".scalar(@broken_authors_1)." of type 1 (masterObj not set although it should)
  • -
  • ".scalar(@broken_authors_2)." of type 2 (masterObj set incorrectly)
  • +
  • " + . scalar(@broken_authors_0) + . " of type 0 (is minion but master undefined)
  • +
  • " + . scalar(@broken_authors_1) + . " of type 1 (masterObj not set although it should)
  • +
  • " + . scalar(@broken_authors_2) . " of type 2 (masterObj set incorrectly)
"; # we cure all problems with the same medicine... - foreach my $author ( (@broken_authors_0, @broken_authors_1, @broken_authors_2) ){ - my $master = $self->app->repo->authors_find( sub { $_->id == $author->master_id } ); - if(defined $master){ + foreach my $author ((@broken_authors_0, @broken_authors_1, @broken_authors_2)) + { + my $master + = $self->app->repo->authors_find(sub { $_->id == $author->master_id }); + if (defined $master) { $author->masterObj($master); ++$num_fixes_0; ++$num_fixes_1; ++$num_fixes_2; } } - $msg .= "
Fixing is finished. Masters were re-added to the authors. Fixed: + $msg + .= "
Fixing is finished. Masters were re-added to the authors. Fixed:
  • $num_fixes_0 of type 0 (is minion but master undefined)
  • $num_fixes_1 of type 1 (masterObj not set although it should)
  • $num_fixes_2 of type 2 (masterObj set incorrectly)
"; - - $self->flash( msg => $msg, msg_type => $msg_type ); - $self->redirect_to( $self->get_referrer ); + $self->flash(msg => $msg, msg_type => $msg_type); + $self->redirect_to($self->get_referrer); } ############################################################################################################## sub toggle_visibility { my $self = shift; my $id = $self->param('id'); - my $author = $self->app->repo->authors_find( sub { $_->id == $id } ); + my $author = $self->app->repo->authors_find(sub { $_->id == $id }); $author->toggle_visibility(); $self->app->repo->authors_update($author); - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } ############################################################################################################## diff --git a/lib/BibSpace/Controller/Backup.pm b/lib/BibSpace/Controller/Backup.pm index e8881d9..10bc637 100644 --- a/lib/BibSpace/Controller/Backup.pm +++ b/lib/BibSpace/Controller/Backup.pm @@ -29,7 +29,6 @@ use Mojo::Base 'Mojolicious::Controller'; use Mojo::Base 'Mojolicious::Plugin::Config'; use Mojo::Log; - #################################################################################### sub index { my $self = shift; @@ -42,8 +41,8 @@ sub index { my @backups_arr = sort { $b->date cmp $a->date } read_backups($backup_dir); foreach my $backup (@backups_arr) { - if ( $backup->get_age->days - >= $self->app->config->{allow_delete_backups_older_than} ) + if ($backup->get_age->days + >= $self->app->config->{allow_delete_backups_older_than}) { $backup->allow_delete(1); } @@ -52,24 +51,21 @@ sub index { } } - $self->stash( backups_arr => \@backups_arr, dir_size => $dir_size ); - $self->render( template => 'backup/backup' ); + $self->stash(backups_arr => \@backups_arr, dir_size => $dir_size); + $self->render(template => 'backup/backup'); } #################################################################################### sub save { my $self = shift; - my $backup = do_storable_backup( $self->app ); + my $backup = do_storable_backup($self->app); - if ( $backup->is_healthy ) { - $self->flash( - msg_type => 'success', - msg => "Backup created successfully" - ); + if ($backup->is_healthy) { + $self->flash(msg_type => 'success', msg => "Backup created successfully"); } else { - $self->flash( msg_type => 'danger', msg => "Backup create failed!" ); + $self->flash(msg_type => 'danger', msg => "Backup create failed!"); } $self->redirect_to('backup_index'); } @@ -77,16 +73,13 @@ sub save { sub save_mysql { my $self = shift; - my $backup = do_mysql_backup( $self->app ); + my $backup = do_mysql_backup($self->app); - if ( $backup->is_healthy ) { - $self->flash( - msg_type => 'success', - msg => "Backup created successfully" - ); + if ($backup->is_healthy) { + $self->flash(msg_type => 'success', msg => "Backup created successfully"); } else { - $self->flash( msg_type => 'danger', msg => "Backup create failed!" ); + $self->flash(msg_type => 'danger', msg => "Backup create failed!"); } $self->redirect_to('backup_index'); } @@ -94,9 +87,9 @@ sub save_mysql { sub cleanup { my $self = shift; my $age_treshold - = $self->config->{backup_age_in_days_to_delete_automatically}; + = $self->config->{backup_age_in_days_to_delete_automatically}; - my $num_deleted = delete_old_backups( $self->app, $age_treshold ); + my $num_deleted = delete_old_backups($self->app, $age_treshold); $self->app->logger->info( "Deleting old backups. $num_deleted backups have been cleaned."); @@ -116,18 +109,18 @@ sub backup_download { my $self = shift; my $uuid = $self->param('id'); - my $backup = find_backup( $uuid, $self->app->get_backups_dir ); + my $backup = find_backup($uuid, $self->app->get_backups_dir); - if ( $backup and $backup->is_healthy ) { - $self->app->logger->info( "Downloading backup " . $backup->uuid ); - $self->render_file( 'filepath' => $backup->get_path ); + if ($backup and $backup->is_healthy) { + $self->app->logger->info("Downloading backup " . $backup->uuid); + $self->render_file('filepath' => $backup->get_path); } else { $self->flash( msg_type => 'danger', msg => "Cannot download backup $uuid - backup not healthy." ); - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } } @@ -136,25 +129,22 @@ sub delete_backup { my $self = shift; my $uuid = $self->param('id'); - my $backup = find_backup( $uuid, $self->app->get_backups_dir ); + my $backup = find_backup($uuid, $self->app->get_backups_dir); - if ( $backup and $backup->is_healthy ) { - if ( $backup->get_age->days - >= $self->app->config->{allow_delete_backups_older_than} ) + if ($backup and $backup->is_healthy) { + if ($backup->get_age->days + >= $self->app->config->{allow_delete_backups_older_than}) { $backup->allow_delete(1); } else { $backup->allow_delete(undef); } - if ( $backup->allow_delete ) { + if ($backup->allow_delete) { try { unlink $backup->get_path; - $self->app->logger->info( "Deleting backup " . $backup->uuid ); - $self->flash( - msg_type => 'success', - msg => "Backup id $uuid deleted!" - ); + $self->app->logger->info("Deleting backup " . $backup->uuid); + $self->flash(msg_type => 'success', msg => "Backup id $uuid deleted!"); } catch { $self->flash( @@ -178,7 +168,7 @@ sub delete_backup { } $self->res->code(303); - $self->redirect_to( $self->url_for('backup_index') ); + $self->redirect_to($self->url_for('backup_index')); } #################################################################################### @@ -186,24 +176,23 @@ sub restore_backup { my $self = shift; my $uuid = $self->param('id'); + my $backup = find_backup($uuid, $self->app->get_backups_dir); - my $backup = find_backup( $uuid, $self->app->get_backups_dir ); - - if ( $backup and $backup->is_healthy ) { + if ($backup and $backup->is_healthy) { - restore_storable_backup( $backup, $self->app ); + restore_storable_backup($backup, $self->app); - $self->app->logger->info( "Restoring backup " . $backup->uuid ); + $self->app->logger->info("Restoring backup " . $backup->uuid); my $status - = "Status:
"
-        . $self->app->repo->lr->get_summary_table
-        . "
"; + = "Status:
"
+      . $self->app->repo->lr->get_summary_table
+      . "
"; $self->flash( msg_type => 'success', msg => - "Backup restored successfully. Database recreated, persistence layers in sync. $status" + "Backup restored successfully. Database recreated, persistence layers in sync. $status" ); } else { diff --git a/lib/BibSpace/Controller/Cron.pm b/lib/BibSpace/Controller/Cron.pm index 7187c4d..5867ef2 100644 --- a/lib/BibSpace/Controller/Cron.pm +++ b/lib/BibSpace/Controller/Cron.pm @@ -13,11 +13,9 @@ use v5.16; #because of ~~ use strict; use warnings; - use BibSpace::Functions::FPublications; use BibSpace::Functions::BackupFunctions; - use Mojo::Base 'Mojolicious::Controller'; # use Mojo::UserAgent; @@ -34,7 +32,8 @@ sub index { use JSON -convert_blessed_universally; my $json_text - = JSON->new->allow_blessed(1)->convert_blessed(1)->utf8(1)->pretty(1)->encode( $self->app->preferences ); + = JSON->new->allow_blessed(1)->convert_blessed(1)->utf8(1)->pretty(1) + ->encode($self->app->preferences); say $json_text; $self->stash( @@ -43,7 +42,7 @@ sub index { lr_2 => $self->get_last_cron_run(2), lr_3 => $self->get_last_cron_run(3) ); - $self->render( template => 'display/cron' ); + $self->render(template => 'display/cron'); } ########################################################################################## sub cron { @@ -59,13 +58,16 @@ sub cron { $num_level = 2 if $level_param eq 'week' or $level_param eq '2'; $num_level = 3 if $level_param eq 'month' or $level_param eq '3'; - my $result = $self->cron_level($num_level); - if ( !$result ) { - $self->render( text => "Error 404. Incorrect cron job level: $level_param (numeric: $num_level)", status => 404 ); + if (!$result) { + $self->render( + text => + "Error 404. Incorrect cron job level: $level_param (numeric: $num_level)", + status => 404 + ); } else { - $self->render( text => $result, status => 200 ); + $self->render(text => $result, status => 200); } } @@ -74,30 +76,29 @@ sub cron_level { my $self = shift; my $level = shift; - - if ( !defined $level or $level < 0 or $level > 3 ) { + if (!defined $level or $level < 0 or $level > 3) { return ""; } my $call_freq = 99999; - if ( $level == 0 ) { + if ($level == 0) { $call_freq = $self->config->{cron_day_freq_lock}; } - elsif ( $level == 1 ) { + elsif ($level == 1) { $call_freq = $self->config->{cron_night_freq_lock}; } - elsif ( $level == 2 ) { + elsif ($level == 2) { $call_freq = $self->config->{cron_week_freq_lock}; } - elsif ( $level == 3 ) { + elsif ($level == 3) { $call_freq = $self->config->{cron_month_freq_lock}; } else { # should never happen } - my $message_string = $self->cron_run( $level, $call_freq ); + my $message_string = $self->cron_run($level, $call_freq); # place to debug return $message_string; @@ -117,7 +118,7 @@ sub cron_run { my $last_call = $self->get_last_cron_run($level); # stupid library.... You need to convert units manually - if ( defined $last_call ) { + if (defined $last_call) { $last_call_hours = calc_hours($last_call); } my $left = $call_freq - $last_call_hours; @@ -125,7 +126,7 @@ sub cron_run { my $text_to_render; ############ Cron ACTIONS - if ( $last_call_hours < $call_freq ) { + if ($last_call_hours < $call_freq) { $text_to_render = "Cron level $level called too often. Last call $last_call_hours hours ago. Come back in $left hours\n"; return $text_to_render; @@ -137,25 +138,26 @@ sub cron_run { ############ Cron ACTIONS $self->app->logger->info("Cron level $level started"); - if ( $level == 0 ) { + if ($level == 0) { $self->do_cron_day(); } - elsif ( $level == 1 ) { - Mojo::IOLoop->stream( $self->tx->connection )->timeout(3600); + elsif ($level == 1) { + Mojo::IOLoop->stream($self->tx->connection)->timeout(3600); $self->do_cron_night(); } - elsif ( $level == 2 ) { - Mojo::IOLoop->stream( $self->tx->connection )->timeout(3600); + elsif ($level == 2) { + Mojo::IOLoop->stream($self->tx->connection)->timeout(3600); $self->do_cron_week(); } - elsif ( $level == 3 ) { - Mojo::IOLoop->stream( $self->tx->connection )->timeout(3600); + elsif ($level == 3) { + Mojo::IOLoop->stream($self->tx->connection)->timeout(3600); $self->do_cron_month(); } else { # do nothing } - # this may cause: [error] Unable to open file (json_data/bibspace_preferences.json) for storing : Permission denied at + +# this may cause: [error] Unable to open file (json_data/bibspace_preferences.json) for storing : Permission denied at $self->log_cron_usage($level); $self->app->logger->info("Cron level $level has finished"); @@ -165,7 +167,7 @@ sub cron_run { sub do_cron_day { my $self = shift; - my $backup1 = do_storable_backup( $self->app, "cron" ); + my $backup1 = do_storable_backup($self->app, "cron"); } ########################################################################################## @@ -175,16 +177,15 @@ sub do_cron_night { my @entries = $self->app->repo->entries_all; for my $e (@entries) { - $e->regenerate_html( 0, $self->app->bst, $self->app->bibtexConverter ); + $e->regenerate_html(0, $self->app->bst, $self->app->bibtexConverter); } } ########################################################################################## sub do_cron_week { my $self = shift; - - my $backup1 = do_mysql_backup( $self->app, "cron" ); - my $num_deleted = delete_old_backups( $self->app ); + my $backup1 = do_mysql_backup($self->app, "cron"); + my $num_deleted = delete_old_backups($self->app); } ########################################################################################## @@ -201,12 +202,13 @@ sub log_cron_usage { my $self = shift; my $level = shift; - my $now = DateTime->now->set_time_zone( $self->app->preferences->local_time_zone ); + my $now + = DateTime->now->set_time_zone($self->app->preferences->local_time_zone); my $fomatted_now = DateTime::Format::HTTP->format_datetime($now); say "Storing cron usage level '$level' as '$fomatted_now'."; - $self->app->preferences->cron_set( $level, $fomatted_now ); + $self->app->preferences->cron_set($level, $fomatted_now); } ########################################################################################## sub get_last_cron_run { @@ -214,20 +216,22 @@ sub get_last_cron_run { my $level = shift; # constant :P - my $long_time_ago = DateTime::Duration->new( years => 10 ); + my $long_time_ago = DateTime::Duration->new(years => 10); my $last_call_str = $self->app->preferences->cron_get($level); return $long_time_ago if !$last_call_str; - - my $now = DateTime->now->set_time_zone( $self->app->preferences->local_time_zone ); + my $now + = DateTime->now->set_time_zone($self->app->preferences->local_time_zone); my $last_call; try { $last_call = DateTime::Format::HTTP->parse_datetime($last_call_str); } catch { warn; - $self->app->logger->error("Cannot parse date of last cron usage. Parser got input: '$last_call_str', error: $_ "); + $self->app->logger->error( + "Cannot parse date of last cron usage. Parser got input: '$last_call_str', error: $_ " + ); }; return $long_time_ago if !$last_call; @@ -242,7 +246,6 @@ sub get_last_cron_run { # my $last_call_str = $self->app->preferences->cron_get($level); # return 0 if !$last_call_str; - # my $now = DateTime->now->set_time_zone($self->app->preferences->local_time_zone); # my $last_call; # try{ diff --git a/lib/BibSpace/Controller/Display.pm b/lib/BibSpace/Controller/Display.pm index ad7d7cd..1e14222 100644 --- a/lib/BibSpace/Controller/Display.pm +++ b/lib/BibSpace/Controller/Display.pm @@ -20,21 +20,21 @@ use BibSpace::Util::Statistics; ################################################################################# sub index { my $self = shift; - if ( $self->app->is_demo ) { - $self->session( user => 'demouser' ); - $self->session( user_name => 'demouser' ); + if ($self->app->is_demo) { + $self->session(user => 'demouser'); + $self->session(user_name => 'demouser'); } - $self->render( template => 'display/start' ); + $self->render(template => 'display/start'); } ################################################################################# sub test500 { my $self = shift; - $self->render( text => 'Oops 500.', status => 500 ); + $self->render(text => 'Oops 500.', status => 500); } ################################################################################# sub test404 { my $self = shift; - $self->render( text => 'Oops 404.', status => 404 ); + $self->render(text => 'Oops 404.', status => 404); } ################################################################################# sub get_log_lines { @@ -43,100 +43,113 @@ sub get_log_lines { my $type = shift; my $filter_re = shift; - - my $log_dir = Path::Tiny->new( $dir ); + my $log_dir = Path::Tiny->new($dir); my @file_list = $log_dir->children(qr/\.log$/); my @log_names = map { $_->basename('.log') } @file_list; my $log_2_read; - $log_2_read = $log_dir->child( $type . ".log" ) if defined $type; + $log_2_read = $log_dir->child($type . ".log") if defined $type; $log_2_read = $file_list[0] if !$log_2_read or !$log_2_read->exists; - die "No log file found " if !-e $log_2_read; # throw + die "No log file found " if !-e $log_2_read; # throw - # my @lines = $log_2_read->lines( { count => -1 * $num } ); - # @lines = ( $num >= @lines ) ? reverse @lines : reverse @lines[ -$num .. -1 ]; + # my @lines = $log_2_read->lines( { count => -1 * $num } ); + # @lines = ( $num >= @lines ) ? reverse @lines : reverse @lines[ -$num .. -1 ]; my @lines = $log_2_read->lines(); + # @lines = reverse @lines; chomp(@lines); - if( $filter_re ){ - @lines = grep{ m/$filter_re/ } @lines; + if ($filter_re) { + @lines = grep {m/$filter_re/} @lines; } - return @lines[-$num..-1]; + return @lines[-$num .. -1]; } ################################################################################# sub show_log { - my $self = shift; - my $num = $self->param('num') // 100; - my $type = $self->param('type') // 'general'; # default + my $self = shift; + my $num = $self->param('num') // 100; + my $type = $self->param('type') // 'general'; # default my $filter = $self->param('filter'); - my @lines; - try{ - @lines = get_log_lines( $self->app->get_log_dir, $num, $type, $filter ); + try { + @lines = get_log_lines($self->app->get_log_dir, $num, $type, $filter); } - catch{ + catch { $self->app->logger->error("Cannot find log '$type'. Error: $_."); - $self->stash( msg_type => 'danger', msg => "Cannot find log '$type'." ); + $self->stash(msg_type => 'danger', msg => "Cannot find log '$type'."); }; - my @file_list = Path::Tiny->new( $self->app->get_log_dir )->children(qr/\.log$/); - my $curr_file = Path::Tiny->new( $self->app->get_log_dir )->child('general.log'); + my @file_list + = Path::Tiny->new($self->app->get_log_dir)->children(qr/\.log$/); + my $curr_file + = Path::Tiny->new($self->app->get_log_dir)->child('general.log'); - $self->stash( files => \@file_list, lines => \@lines, curr_file => $curr_file, num => $num); - $self->render( template => 'display/log' ); + $self->stash( + files => \@file_list, + lines => \@lines, + curr_file => $curr_file, + num => $num + ); + $self->render(template => 'display/log'); } ################################################################################# sub show_log_ws { my $self = shift; - my $num = $self->param('num') // 20; - - - $self->on(message => sub { - my ($self, $filter) = @_; - - my @lines = get_log_lines( $self->app->get_log_dir, $num, 'general', $filter ); - $self->send( Mojo::JSON::encode_json( \@lines ) ); - }); - - $self->on(finish => sub { - my ($c, $code, $reason) = @_; - say "show_log_ws WS closed"; - }); + my $num = $self->param('num') // 20; + + $self->on( + message => sub { + my ($self, $filter) = @_; + + my @lines + = get_log_lines($self->app->get_log_dir, $num, 'general', $filter); + $self->send(Mojo::JSON::encode_json(\@lines)); + } + ); + + $self->on( + finish => sub { + my ($c, $code, $reason) = @_; + say "show_log_ws WS closed"; + } + ); } ################################################################################# sub show_stats { my $self = shift; - my $num = $self->param('num') // 20; + my $num = $self->param('num') // 20; my @lines = $self->app->statistics->toLines; - $self->stash( lines => \@lines, num => $num); - $self->render( template => 'display/stats' ); + $self->stash(lines => \@lines, num => $num); + $self->render(template => 'display/stats'); } ################################################################################# sub show_stats_websocket { my $self = shift; - my $num = $self->param('num') // 20; - - - $self->on(message => sub { - my ($self, $filter) = @_; - - my @all_lines = $self->app->statistics->toLines; - my @lines = grep{ /$filter/} @all_lines; - $self->send( Mojo::JSON::encode_json( \@lines ) ); - }); - - $self->on(finish => sub { - my ($c, $code, $reason) = @_; - say "show_stats_websocket WS closed"; - }); + my $num = $self->param('num') // 20; + + $self->on( + message => sub { + my ($self, $filter) = @_; + + my @all_lines = $self->app->statistics->toLines; + my @lines = grep {/$filter/} @all_lines; + $self->send(Mojo::JSON::encode_json(\@lines)); + } + ); + + $self->on( + finish => sub { + my ($c, $code, $reason) = @_; + say "show_stats_websocket WS closed"; + } + ); } ################################################################################# diff --git a/lib/BibSpace/Controller/Helpers.pm b/lib/BibSpace/Controller/Helpers.pm index df81f00..4439fbe 100644 --- a/lib/BibSpace/Controller/Helpers.pm +++ b/lib/BibSpace/Controller/Helpers.pm @@ -4,13 +4,13 @@ use Data::Dumper; use utf8; use Text::BibTeX; # parsing bib files use DateTime; + # use File::Slurp; use v5.16; #because of ~~ use strict; use warnings; - use List::MoreUtils qw(any uniq); use BibSpace::Functions::Core; @@ -20,17 +20,16 @@ use BibSpace::Functions::FDB; use BibSpace::Functions::FPublications; - use base 'Mojolicious::Plugin'; sub register { - my ( $self, $app ) = @_; + my ($self, $app) = @_; - # this must be a helper, - # because smartIDprovider can be exchanged during system lifetime (e.g. restore backup), - # so the reference must always point to the currently valid id provider - # smartIDProvider must be instantiated INSIDE LayeredRepository +# this must be a helper, +# because smartIDprovider can be exchanged during system lifetime (e.g. restore backup), +# so the reference must always point to the currently valid id provider +# smartIDProvider must be instantiated INSIDE LayeredRepository $app->helper( smartIDProvider => sub { my $self = shift; @@ -38,10 +37,10 @@ sub register { } ); - # this must be a helper, - # because entityFactory can be exchanged during system lifetime (e.g. restore backup), - # so the reference must always point to the currently valid id provider - # entityFactory must be instantiated INSIDE LayeredRepository +# this must be a helper, +# because entityFactory can be exchanged during system lifetime (e.g. restore backup), +# so the reference must always point to the currently valid id provider +# entityFactory must be instantiated INSIDE LayeredRepository $app->helper( entityFactory => sub { my $self = shift; @@ -49,39 +48,36 @@ sub register { } ); - $app->helper( is_demo => sub { my $self = shift; return 1 if $self->config->{demo_mode}; - # say "helper->is_demo: run_in_demo_mode: ".$self->app->preferences->run_in_demo_mode; + +# say "helper->is_demo: run_in_demo_mode: ".$self->app->preferences->run_in_demo_mode; return 1 if $self->app->preferences->run_in_demo_mode == 1; return; } ); - $app->helper( - db => sub { - my $self = shift; - my $db_host - = $ENV{BIBSPACE_DB_HOST} || $self->app->config->{db_host}; - my $db_user - = $ENV{BIBSPACE_DB_USER} || $self->app->config->{db_user}; - my $db_database - = $ENV{BIBSPACE_DB_DATABASE} || $self->app->config->{db_database}; - my $db_pass - = $ENV{BIBSPACE_DB_PASS} || $self->app->config->{db_pass}; - return db_connect( $db_host, $db_user, $db_database, $db_pass ); - } - ); + $app->helper( + db => sub { + my $self = shift; + my $db_host = $ENV{BIBSPACE_DB_HOST} || $self->app->config->{db_host}; + my $db_user = $ENV{BIBSPACE_DB_USER} || $self->app->config->{db_user}; + my $db_database + = $ENV{BIBSPACE_DB_DATABASE} || $self->app->config->{db_database}; + my $db_pass = $ENV{BIBSPACE_DB_PASS} || $self->app->config->{db_pass}; + return db_connect($db_host, $db_user, $db_database, $db_pass); + } + ); $app->helper( bst => sub { - my $self = shift; + my $self = shift; my $bst_candidate_file = $self->app->home . '/lib/descartes2.bst'; - if ( defined $self->app->config->{bst_file} ) { + if (defined $self->app->config->{bst_file}) { $bst_candidate_file = $self->app->config->{bst_file}; return File::Spec->rel2abs($bst_candidate_file) if File::Spec->file_name_is_absolute($bst_candidate_file) @@ -96,7 +92,8 @@ sub register { $bst_candidate_file = $self->app->home . '/lib/descartes2.bst'; - return File::Spec->rel2abs($bst_candidate_file) if -e File::Spec->rel2abs($bst_candidate_file); + return File::Spec->rel2abs($bst_candidate_file) + if -e File::Spec->rel2abs($bst_candidate_file); $self->app->logger->error("Cannot find any valid bst file!"); return './bst-not-found.bst'; @@ -106,35 +103,39 @@ sub register { $app->helper( bibtexConverter => sub { my $self = shift; - try{ - my $class = $self->app->preferences->bibitex_html_converter; - Class::Load::load_class($class); - if($class->does('IHtmlBibtexConverter')){ - return $class->new( logger => $self->app->logger ); - } - die "Requested class '$class' does not implement interface 'IHtmlBibtexConverter'"; + try { + my $class = $self->app->preferences->bibitex_html_converter; + Class::Load::load_class($class); + if ($class->does('IHtmlBibtexConverter')) { + return $class->new(logger => $self->app->logger); + } + die + "Requested class '$class' does not implement interface 'IHtmlBibtexConverter'"; } - catch{ - $self->logger->error("Requested unknown type of bibitex_html_converter: '".$self->app->preferences->bibitex_html_converter."'. Error: $_."); + catch { + $self->logger->error( + "Requested unknown type of bibitex_html_converter: '" + . $self->app->preferences->bibitex_html_converter + . "'. Error: $_."); } - finally{ - return BibStyleConverter->new( logger => $self->app->logger ); + finally { + return BibStyleConverter->new(logger => $self->app->logger); }; } ); - $app->helper( - get_referrer => sub { - my $self = shift; - return $self->get_referrer_old; - } + get_referrer => sub { + my $self = shift; + return $self->get_referrer_old; + } ); $app->helper( get_referrer_new => sub { my $self = shift; my $ret = $self->req->headers->referrer; + # $ret //= $self->url_for('start'); return $ret; } @@ -144,38 +145,36 @@ sub register { my $s = shift; my $ret = $s->url_for('start'); $ret = $s->req->headers->referrer - if defined $s->req->headers->referrer - and $s->req->headers->referrer ne ''; + if defined $s->req->headers->referrer + and $s->req->headers->referrer ne ''; return $ret; } ); - $app->helper( - is_manager => sub { - my $self = shift; - return 1 if $self->app->is_demo; - return if !$self->session('user'); - my $me = $self->app->repo->users_find( - sub { $_->login eq $self->session('user') } ); - return if !$me; - return $me->is_manager; - } + is_manager => sub { + my $self = shift; + return 1 if $self->app->is_demo; + return if !$self->session('user'); + my $me = $self->app->repo->users_find( + sub { $_->login eq $self->session('user') }); + return if !$me; + return $me->is_manager; + } ); $app->helper( - is_admin => sub { - my $self = shift; - return 1 if $self->app->is_demo; - return if !$self->session('user'); - my $me = $self->app->repo->users_find( - sub { $_->login eq $self->session('user') } ); - return if !$me; - return $me->is_admin; - } + is_admin => sub { + my $self = shift; + return 1 if $self->app->is_demo; + return if !$self->session('user'); + my $me = $self->app->repo->users_find( + sub { $_->login eq $self->session('user') }); + return if !$me; + return $me->is_admin; + } ); - $app->helper( current_year => sub { return BibSpace::Functions::Core::get_current_year(); @@ -189,19 +188,21 @@ sub register { $app->helper( get_year_of_oldest_entry => sub { - my $self = shift; + my $self = shift; my $author = shift; my @entries = $self->app->repo->entries_all; - if(defined $author){ + if (defined $author) { - my $author_obj = $self->app->repo->authors_find( sub {$_->get_master->uid eq $author} ); - $author_obj ||= $self->app->repo->authors_find( sub {$_->get_master->id eq $author} ); - if ($author_obj){ - @entries = $author_obj->get_entries; + my $author_obj = $self->app->repo->authors_find( + sub { $_->get_master->uid eq $author }); + $author_obj ||= $self->app->repo->authors_find( + sub { $_->get_master->id eq $author }); + if ($author_obj) { + @entries = $author_obj->get_entries; } - + } my @entryYears = map { $_->year } grep { defined $_->year } @entries; @@ -211,37 +212,38 @@ sub register { } ); - $app->helper( num_pubs => sub { - my $self = shift; - my $type = shift; - my $year = shift; + my $self = shift; + my $type = shift; + my $year = shift; my $entries_arr_ref = shift; my @entries; - if($entries_arr_ref){ + if ($entries_arr_ref) { @entries = @$entries_arr_ref; } - else{ - @entries = $self->app->repo->entries_all; + else { + @entries = $self->app->repo->entries_all; } - - if($type){ - @entries = grep {$_->entry_type eq $type} @entries; + + if ($type) { + @entries = grep { $_->entry_type eq $type } @entries; } - if($year){ - @entries = grep { defined $_->year and $_->year == $year and $_->hidden == 0 } @entries; + if ($year) { + @entries + = grep { defined $_->year and $_->year == $year and $_->hidden == 0 } + @entries; } return scalar @entries; - + } ); $app->helper( get_important_tag_types => sub { my $self = shift; - return $self->app->repo->tagTypes_filter(sub{$_->id < 4}); + return $self->app->repo->tagTypes_filter(sub { $_->id < 4 }); } ); @@ -249,7 +251,7 @@ sub register { get_tag_type_obj => sub { my $self = shift; my $type = shift // 1; - return $self->app->repo->tagTypes_find( sub { $_->id == $type } ); + return $self->app->repo->tagTypes_find(sub { $_->id == $type }); } ); @@ -259,9 +261,9 @@ sub register { my $eid = shift; my $type = shift // 1; - my $paper = $self->app->repo->entries_find( sub { $_->id == $eid } ); + my $paper = $self->app->repo->entries_find(sub { $_->id == $eid }); my @tags = $paper->get_tags($type); - @tags = sort {$a->name cmp $b->name} @tags; + @tags = sort { $a->name cmp $b->name } @tags; return @tags; } ); @@ -272,11 +274,11 @@ sub register { my $eid = shift; my $type = shift // 1; - my $paper = $self->app->repo->entries_find( sub { $_->id == $eid } ); - my %has_tags = map {$_ => 1} $paper->get_tags($type); - my @all_tags = $self->app->repo->tags_filter( sub{$_->type == $type} ); + my $paper = $self->app->repo->entries_find(sub { $_->id == $eid }); + my %has_tags = map { $_ => 1 } $paper->get_tags($type); + my @all_tags = $self->app->repo->tags_filter(sub { $_->type == $type }); my @unassigned = grep { not $has_tags{$_} } @all_tags; - @unassigned = sort {$a->name cmp $b->name} @unassigned; + @unassigned = sort { $a->name cmp $b->name } @unassigned; return @unassigned; } ); @@ -294,7 +296,7 @@ sub register { $app->helper( get_visible_authors => sub { my $self = shift; - return $self->app->repo->authors_filter( sub { $_->is_visible } ); + return $self->app->repo->authors_filter(sub { $_->is_visible }); } ); @@ -305,7 +307,6 @@ sub register { } ); - $app->helper( get_num_teams => sub { my $self = shift; @@ -313,17 +314,14 @@ sub register { } ); - $app->helper( num_tags => sub { my $self = shift; my $type = shift // 1; - return scalar $self->app->repo->tags_filter( sub { $_->type == $type } ); + return scalar $self->app->repo->tags_filter(sub { $_->type == $type }); } ); - - $app->helper( num_pubs_for_author_and_tag => sub { my $self = shift; @@ -331,11 +329,11 @@ sub register { my $tag = shift; return - scalar $author->authorships_filter( sub { defined $_ and defined $_->entry and $_->entry->has_tag($tag) } ); + scalar $author->authorships_filter( + sub { defined $_ and defined $_->entry and $_->entry->has_tag($tag) }); } ); - $app->helper( num_pubs_for_tag => sub { my $self = shift; diff --git a/lib/BibSpace/Controller/Login.pm b/lib/BibSpace/Controller/Login.pm index 5577cde..76225c3 100644 --- a/lib/BibSpace/Controller/Login.pm +++ b/lib/BibSpace/Controller/Login.pm @@ -6,559 +6,525 @@ use Mojo::Base 'Mojolicious::Plugin::Config'; use BibSpace::Model::User; use BibSpace::Functions::Core - qw(send_email generate_token salt encrypt_password check_password_policy validate_registration_data); - + qw(send_email generate_token salt encrypt_password check_password_policy validate_registration_data); use Data::Dumper; use Try::Tiny; - #################################################################################### # for _under_ -checking if user is logged in to access other pages sub check_is_logged_in { - my $self = shift; - return 1 if $self->app->is_demo; - - # no session - if( !defined $self->session('user')){ - $self->redirect_to('youneedtologin'); - return; - } + my $self = shift; + return 1 if $self->app->is_demo; - # session exists, but user unknown - my $me = $self->app->repo->users_find(sub { $_->login eq $self->session('user') } ); - if( !defined $me ){ - $self->session( expires => 1 ); - $self->redirect_to('youneedtologin'); - return; - } + # no session + if (!defined $self->session('user')) { + $self->redirect_to('youneedtologin'); + return; + } + + # session exists, but user unknown + my $me + = $self->app->repo->users_find(sub { $_->login eq $self->session('user') }); + if (!defined $me) { + $self->session(expires => 1); + $self->redirect_to('youneedtologin'); + return; + } - return 1; + return 1; } #################################################################################### # for _under_ -checking sub under_check_is_manager { - my $self = shift; - return 1 if $self->app->is_demo; - return $self->under_check_has_rank( User->manager_rank ); + my $self = shift; + return 1 if $self->app->is_demo; + return $self->under_check_has_rank(User->manager_rank); } #################################################################################### # for _under_ -checking sub under_check_is_admin { - my $self = shift; - return 1 if $self->app->is_demo; - return $self->under_check_has_rank( User->admin_rank ); + my $self = shift; + return 1 if $self->app->is_demo; + return $self->under_check_has_rank(User->admin_rank); } #################################################################################### # for _under_ -checking sub under_check_has_rank { - my $self = shift; - my $required_rank = shift; - - return 1 if $self->app->is_demo; + my $self = shift; + my $required_rank = shift; - my $me = $self->app->repo->users_find( - sub { $_->login eq $self->session('user') } ); + return 1 if $self->app->is_demo; - if ( $me and $me->rank >= $required_rank ) { - return 1; - } - - my $your_rank = 'undefined'; - $your_rank = $me->rank if $me; + my $me + = $self->app->repo->users_find(sub { $_->login eq $self->session('user') }); - $self->flash( - msg_type => 'danger', - msg => "You need to have rank '" - . $required_rank - . "' to access this page! " - . "Your rank is: '" - . $your_rank . "'" - . "
You have tried to access: " - . $self->url_for('current')->to_abs - ); - - my $redirect_to = $self->get_referrer; - - if ( $self->get_referrer eq $self->url_for('current')->to_abs ) { - $redirect_to = $self->url_for('/'); - } - $self->redirect_to($redirect_to); - return; + if ($me and $me->rank >= $required_rank) { + return 1; + } + + my $your_rank = 'undefined'; + $your_rank = $me->rank if $me; + + $self->flash( + msg_type => 'danger', + msg => "You need to have rank '" + . $required_rank + . "' to access this page! " + . "Your rank is: '" + . $your_rank . "'" + . "
You have tried to access: " + . $self->url_for('current')->to_abs + ); + + my $redirect_to = $self->get_referrer; + + if ($self->get_referrer eq $self->url_for('current')->to_abs) { + $redirect_to = $self->url_for('/'); + } + $self->redirect_to($redirect_to); + return; } #################################################################################### #################################################################################### #################################################################################### sub manage_users { - my $self = shift; - my $dbh = $self->app->db; + my $self = shift; + my $dbh = $self->app->db; - my @user_objs = $self->app->repo->users_all; - $self->stash( user_objs => \@user_objs ); - $self->render( template => 'login/manage_users' ); + my @user_objs = $self->app->repo->users_all; + $self->stash(user_objs => \@user_objs); + $self->render(template => 'login/manage_users'); } #################################################################################### sub promote_to_rank { - my $self = shift; - my $rank = shift; - - my $profile_id = $self->param('id'); - my $user_obj - = $self->app->repo->users_find( sub { $_->id == $profile_id } ); - - my $me = $self->app->repo->users_find( - sub { $_->login eq $self->session('user') } ); - - if ( $me->is_admin ) { - if ( $me->equals($user_obj) ) { - $self->flash( - msg_type => 'danger', - msg => "You cannot degrade yourself!" - ); - } - else { - $user_obj->rank($rank); - $self->app->repo->users_update($user_obj); - - my $msg - = "User '" . $user_obj->login . "'' has now rank '$rank'."; - $self->app->logger->info($msg); - $self->flash( msg_type => 'success', msg => $msg ); - } + my $self = shift; + my $rank = shift; + + my $profile_id = $self->param('id'); + my $user_obj = $self->app->repo->users_find(sub { $_->id == $profile_id }); + + my $me + = $self->app->repo->users_find(sub { $_->login eq $self->session('user') }); + + if ($me->is_admin) { + if ($me->equals($user_obj)) { + $self->flash(msg_type => 'danger', msg => "You cannot degrade yourself!"); } else { - $self->flash( - msg_type => 'danger', - msg => "Only admins can promote/degrade users!" - ); + $user_obj->rank($rank); + $self->app->repo->users_update($user_obj); + + my $msg = "User '" . $user_obj->login . "'' has now rank '$rank'."; + $self->app->logger->info($msg); + $self->flash(msg_type => 'success', msg => $msg); } - $self->redirect_to('manage_users'); + } + else { + $self->flash( + msg_type => 'danger', + msg => "Only admins can promote/degrade users!" + ); + } + $self->redirect_to('manage_users'); } #################################################################################### sub make_user { - my $self = shift; - return $self->promote_to_rank( User->user_rank ); + my $self = shift; + return $self->promote_to_rank(User->user_rank); } #################################################################################### sub make_manager { - my $self = shift; - return $self->promote_to_rank( User->manager_rank ); + my $self = shift; + return $self->promote_to_rank(User->manager_rank); } #################################################################################### sub make_admin { - my $self = shift; - return $self->promote_to_rank( User->admin_rank ); + my $self = shift; + return $self->promote_to_rank(User->admin_rank); } #################################################################################### sub delete_user { - my $self = shift; - my $profile_id = $self->param('id'); + my $self = shift; + my $profile_id = $self->param('id'); - my $user_obj - = $self->app->repo->users_find( sub { $_->id == $profile_id } ); - my $me = $self->app->repo->users_find( - sub { $_->login eq $self->session('user') } ); - - - if ( $me and !$me->is_admin ) { - $self->flash( msg_type => 'danger', msg => 'You are not admin!' ); - $self->redirect_to('manage_users'); - return; - } - if ( $user_obj and $user_obj->is_admin ) { - $self->flash( - msg_type => 'danger', - msg => 'You cannot delete admin user!' - ); - $self->redirect_to('manage_users'); - return; - } - if ( $user_obj and $user_obj->equals($me) ) { - $self->flash( - msg_type => 'danger', - msg => 'You cannot delete yourself!' - ); - $self->redirect_to('manage_users'); - return; - } - if ($user_obj) { - $self->app->repo->users_delete($user_obj); - my $msg - = "User '$user_obj->{login}' real name: '$user_obj->{real_name}' has been deleted."; - $self->app->logger->info($msg); - $self->flash( msg_type => 'success', msg => $msg ); - } - else { - my $msg - = "Cannot delete user. Reason: cannot find user with ID '$profile_id'."; - $self->app->logger->info($msg); - $self->flash( msg_type => 'danger', msg => $msg ); - } + my $user_obj = $self->app->repo->users_find(sub { $_->id == $profile_id }); + my $me + = $self->app->repo->users_find(sub { $_->login eq $self->session('user') }); + if ($me and !$me->is_admin) { + $self->flash(msg_type => 'danger', msg => 'You are not admin!'); + $self->redirect_to('manage_users'); + return; + } + if ($user_obj and $user_obj->is_admin) { + $self->flash(msg_type => 'danger', msg => 'You cannot delete admin user!'); + $self->redirect_to('manage_users'); + return; + } + if ($user_obj and $user_obj->equals($me)) { + $self->flash(msg_type => 'danger', msg => 'You cannot delete yourself!'); $self->redirect_to('manage_users'); + return; + } + if ($user_obj) { + $self->app->repo->users_delete($user_obj); + my $msg + = "User '$user_obj->{login}' real name: '$user_obj->{real_name}' has been deleted."; + $self->app->logger->info($msg); + $self->flash(msg_type => 'success', msg => $msg); + } + else { + my $msg + = "Cannot delete user. Reason: cannot find user with ID '$profile_id'."; + $self->app->logger->info($msg); + $self->flash(msg_type => 'danger', msg => $msg); + } + + $self->redirect_to('manage_users'); } #################################################################################### sub foreign_profile { - my $self = shift; - my $profile_id = $self->param('id'); - my $user_obj - = $self->app->repo->users_find( sub { $_->id == $profile_id } ); + my $self = shift; + my $profile_id = $self->param('id'); + my $user_obj = $self->app->repo->users_find(sub { $_->id == $profile_id }); - $self->stash( usrobj => $user_obj ); - $self->render( template => 'login/profile' ); + $self->stash(usrobj => $user_obj); + $self->render(template => 'login/profile'); } #################################################################################### sub profile { - my $self = shift; - my $me = $self->app->repo->users_find( - sub { $_->login eq $self->session('user') } ); + my $self = shift; + my $me + = $self->app->repo->users_find(sub { $_->login eq $self->session('user') }); - $self->stash( usrobj => $me ); - $self->render( template => 'login/profile' ); + $self->stash(usrobj => $me); + $self->render(template => 'login/profile'); } #################################################################################### sub index { - my $self = shift; - $self->render( template => 'login/index' ); + my $self = shift; + $self->render(template => 'login/index'); } #################################################################################### sub forgot { - my $self = shift; - $self->app->logger->info("Forgot password form opened"); - $self->render( template => 'login/forgot_request' ); + my $self = shift; + $self->app->logger->info("Forgot password form opened"); + $self->render(template => 'login/forgot_request'); } #################################################################################### sub post_gen_forgot_token { - my $self = shift; + my $self = shift; # this is called when a user fills the form called "Recovery of forgotten password" - my $login = $self->param('user'); - my $email = $self->param('email'); + my $login = $self->param('user'); + my $email = $self->param('email'); + + my $user; + if ($login) { + $self->app->logger->info( + "Request to generate forgot-password-token for login '$login'."); + $user = $self->app->repo->users_find(sub { $_->login eq $login }); + } + if ($email) { + $self->app->logger->info( + "Request to generate forgot-password-token for email '$email'."); + $user = $self->app->repo->users_find(sub { $_->email eq $email }); + } + if (!$user) { + $self->app->logger->warn( + "Cannot find user '$login' nor email '$email' to generate forgot-password-token." + ); + $self->flash( + msg_type => 'warning', + msg => "User '$login' or email '$email' does not exist. Try again." + ); + $self->redirect_to('forgot'); + return; + } + else { + # store token in the user object + $user->forgot_token(generate_token); - my $user; - if ($login) { - $self->app->logger->info( - "Request to generate forgot-password-token for login '$login'."); - $user = $self->app->repo->users_find( sub { $_->login eq $login } ); - } - if ($email) { - $self->app->logger->info( - "Request to generate forgot-password-token for email '$email'."); - $user = $self->app->repo->users_find( sub { $_->email eq $email } ); + my $email_content = $self->render_to_string('email_forgot_password', + token => $user->forgot_token); + try { + my %email_config = ( + mailgun_domain => $self->app->config->{mailgun_domain}, + mailgun_key => $self->app->config->{mailgun_key}, + from => $self->app->config->{mailgun_from}, + to => $user->email, + content => $email_content, + subject => 'BibSpace password reset request' + ); + send_email(\%email_config); } + catch { + $self->app->logger->warn( + "Could not sent Email with Mailgun. This is okay for test, but not for production. Error: $_ ." + ); + }; + $self->app->logger->info("Forgot-password-token '" + . $user->forgot_token + . "' sent to '" + . $user->email + . "'."); - if ( !$user ) { - $self->app->logger->warn( - "Cannot find user '$login' nor email '$email' to generate forgot-password-token."); - $self->flash( - msg_type => 'warning', - msg => "User '$login' or email '$email' does not exist. Try again." - ); - $self->redirect_to('forgot'); - return; - } - else { - # store token in the user object - $user->forgot_token(generate_token); - - my $email_content = $self->render_to_string( 'email_forgot_password', - token => $user->forgot_token ); - try { - my %email_config = ( - mailgun_domain => $self->app->config->{mailgun_domain}, - mailgun_key => $self->app->config->{mailgun_key}, - from => $self->app->config->{mailgun_from}, - to => $user->email, - content => $email_content, - subject => 'BibSpace password reset request' - ); - send_email( \%email_config ); - } - catch { - $self->app->logger->warn( - "Could not sent Email with Mailgun. This is okay for test, but not for production. Error: $_ ." - ); - }; - - $self->app->logger->info( "Forgot-password-token '" - . $user->forgot_token - . "' sent to '" - . $user->email - . "'." ); - - $self->flash( - msg_type => 'info', - msg => - "Email with password reset instructions has been sent. Expect an email from " - . $self->app->config->{mailgun_from} - ); - $self->redirect_to('/'); + $self->flash( + msg_type => 'info', + msg => + "Email with password reset instructions has been sent. Expect an email from " + . $self->app->config->{mailgun_from} + ); + $self->redirect_to('/'); - } + } - $self->redirect_to('forgot'); + $self->redirect_to('forgot'); } #################################################################################### sub token_clicked { - my $self = shift; - my $token = $self->param('token'); + my $self = shift; + my $token = $self->param('token'); - $self->app->logger->info("Reset token clicked '$token'"); - $self->stash( token => $token ); - $self->render( template => 'login/set_new_password' ); + $self->app->logger->info("Reset token clicked '$token'"); + $self->stash(token => $token); + $self->render(template => 'login/set_new_password'); } #################################################################################### sub store_password { - my $self = shift; - my $token = $self->param('token'); - my $pass1 = $self->param('pass1'); - my $pass2 = $self->param('pass2'); - - # search for user that has this token - my $user; - if ($token) { - $user = $self->app->repo->users_find( - sub { defined $_->forgot_token and $_->forgot_token eq $token } ); - } + my $self = shift; + my $token = $self->param('token'); + my $pass1 = $self->param('pass1'); + my $pass2 = $self->param('pass2'); + + # search for user that has this token + my $user; + if ($token) { + $user = $self->app->repo->users_find( + sub { defined $_->forgot_token and $_->forgot_token eq $token }); + } + + if (!$user) { + $self->app->logger->warn( + "Forgot: Reset password token is invalid! Token: '$token'"); + $self->flash( + msg_type => 'danger', + msg => + 'Reset password token is invalid! Make sure you click the newest token that you requested.' + ); + $self->redirect_to('login_form'); + return; + } + if ($pass1 eq $pass2 and check_password_policy($pass1)) { - if ( !$user ) { - $self->app->logger->warn( - "Forgot: Reset password token is invalid! Token: '$token'"); - $self->flash( - msg_type => 'danger', - msg => - 'Reset password token is invalid! Make sure you click the newest token that you requested.' - ); - $self->redirect_to('login_form'); - return; - } - + my $salt = salt(); + my $hash = encrypt_password($pass1, $salt); + $user->pass($pass1); + $user->pass2($salt); + $user->forgot_token(""); + $self->flash( + msg_type => 'success', + msg => + 'Password change successful. All your password reset tokens have been removed. You may login now.' + ); + $self->app->logger->info( + "Forgot: Password change successful for token $token."); + $self->redirect_to('login_form'); + return; + } - if ( $pass1 eq $pass2 and check_password_policy($pass1) ) { - - my $salt = salt(); - my $hash = encrypt_password( $pass1, $salt ); - $user->pass($pass1); - $user->pass2($salt); - $user->forgot_token(""); - $self->flash( - msg_type => 'success', - msg => - 'Password change successful. All your password reset tokens have been removed. You may login now.' - ); - $self->app->logger->info( - "Forgot: Password change successful for token $token."); - $self->redirect_to('login_form'); - return; - } - - my $msg - = 'Passwords are not same or do not obey the password policy. Please try again.'; - $self->flash( msg => $msg, msg_type => 'warning' ); - $self->app->logger->info($msg); - $self->stash( token => $token ); - $self->redirect_to( 'token_clicked', token => $token ); + my $msg + = 'Passwords are not same or do not obey the password policy. Please try again.'; + $self->flash(msg => $msg, msg_type => 'warning'); + $self->app->logger->info($msg); + $self->stash(token => $token); + $self->redirect_to('token_clicked', token => $token); } #################################################################################### sub login { - my $self = shift; - my $input_login = $self->param('user'); - my $input_pass = $self->param('pass'); - - if ( !$input_login or !$input_pass ) { - $self->flash( - msg_type => 'warning', - msg => 'Please provide user-name and password.' - ); - $self->redirect_to( $self->url_for('login_form') ); - return; - } - - $self->app->logger->info("Trying to login as user '$input_login'"); + my $self = shift; + my $input_login = $self->param('user'); + my $input_pass = $self->param('pass'); - # get the user with login - my $user = $self->app->repo->users_find( - sub { $_->login eq $input_login } + if (!$input_login or !$input_pass) { + $self->flash( + msg_type => 'warning', + msg => 'Please provide user-name and password.' ); - - - my $auth_result; - if ( defined $user ){ - $self->app->logger->info("User '$input_login' exists."); - $auth_result = $user->authenticate($input_pass); - } + $self->redirect_to($self->url_for('login_form')); + return; + } - - if ( defined $user and $auth_result and $auth_result == 1){ - $self->session( user => $user->login ); - $self->session( user_name => $user->real_name ); - $self->session(url_history => []); - - $user->record_logging_in; - - $self->app->logger->info("Login as '$input_login' success."); - $self->redirect_to('/'); - return; - } - else { - $self->app->logger->info("User '$input_login' does not exist."); - $self->app->logger->info( - "Wrong user name or password for '$input_login'."); - $self->flash( - msg_type => 'danger', - msg => 'Wrong user name or password' - ); - $self->redirect_to( $self->url_for('login_form') ); - return; - } + $self->app->logger->info("Trying to login as user '$input_login'"); + + # get the user with login + my $user = $self->app->repo->users_find(sub { $_->login eq $input_login }); + + my $auth_result; + if (defined $user) { + $self->app->logger->info("User '$input_login' exists."); + $auth_result = $user->authenticate($input_pass); + } + + if (defined $user and $auth_result and $auth_result == 1) { + $self->session(user => $user->login); + $self->session(user_name => $user->real_name); + $self->session(url_history => []); + + $user->record_logging_in; + + $self->app->logger->info("Login as '$input_login' success."); + $self->redirect_to('/'); + return; + } + else { + $self->app->logger->info("User '$input_login' does not exist."); + $self->app->logger->info("Wrong user name or password for '$input_login'."); + $self->flash(msg_type => 'danger', msg => 'Wrong user name or password'); + $self->redirect_to($self->url_for('login_form')); + return; + } } #################################################################################### sub login_form { - my $self = shift; - $self->app->logger->info("Displaying login form."); - $self->render( template => 'login/index' ); + my $self = shift; + $self->app->logger->info("Displaying login form."); + $self->render(template => 'login/index'); } #################################################################################### sub bad_password { - my $self = shift; + my $self = shift; - $self->app->logger->info("Bad user name or password! (/badpassword)"); + $self->app->logger->info("Bad user name or password! (/badpassword)"); - $self->flash( - msg_type => 'danger', - msg => 'Wrong user name or password' - ); - $self->redirect_to( $self->url_for('login_form') ); + $self->flash(msg_type => 'danger', msg => 'Wrong user name or password'); + $self->redirect_to($self->url_for('login_form')); } #################################################################################### sub not_logged_in { - my $self = shift; + my $self = shift; - $self->app->logger->info( - "Called a page that requires login but user is not logged in. Redirecting to login." - ); + $self->app->logger->info( + "Called a page that requires login but user is not logged in. Redirecting to login." + ); - $self->flash( msg_type => 'danger', msg => 'You need to login first.' ); - $self->redirect_to( $self->url_for('login_form') ); + $self->flash(msg_type => 'danger', msg => 'You need to login first.'); + $self->redirect_to($self->url_for('login_form')); } #################################################################################### sub logout { - my $self = shift; - $self->app->logger->info("User logs out"); + my $self = shift; + $self->app->logger->info("User logs out"); - $self->session( expires => 1 ); - $self->redirect_to( $self->url_for('start') ); + $self->session(expires => 1); + $self->redirect_to($self->url_for('start')); } #################################################################################### sub register_disabled { - my $self = shift; - $self->app->logger->info( - "Login: informing that registration is disabled."); - $self->render( template => 'login/noregister' ); + my $self = shift; + $self->app->logger->info("Login: informing that registration is disabled."); + $self->render(template => 'login/noregister'); } #################################################################################### sub can_register { - my $self = shift; - my $registration_enabled = $self->app->config->{registration_enabled}; - - return 1 if $registration_enabled == 1; - my $me; - if ( $self->session('user') ) { - $me = $self->app->repo->users_find( - sub { $_->login eq $self->session('user') } - ); - } - return 1 if $me and $me->is_admin; - return; + my $self = shift; + my $registration_enabled = $self->app->config->{registration_enabled}; + + return 1 if $registration_enabled == 1; + my $me; + if ($self->session('user')) { + $me + = $self->app->repo->users_find(sub { $_->login eq $self->session('user') } + ); + } + return 1 if $me and $me->is_admin; + return; } #################################################################################### sub register { - my $self = shift; - - - if ( $self->can_register ) { - $self->stash( - name => 'James Bond', - email => 'test@example.com', - login => 'j.bond007', - password1 => '', - password2 => '' - ); - $self->render( template => 'login/register' ); - return; - } - else { - $self->redirect_to('/noregister'); - } + my $self = shift; + + if ($self->can_register) { + $self->stash( + name => 'James Bond', + email => 'test@example.com', + login => 'j.bond007', + password1 => '', + password2 => '' + ); + $self->render(template => 'login/register'); + return; + } + else { + $self->redirect_to('/noregister'); + } } #################################################################################### sub post_do_register { - my $self = shift; - my $config = $self->app->config; - my $login = $self->param('login'); - my $name = $self->param('name'); - my $email = $self->param('email'); - my $password1 = $self->param('password1'); - my $password2 = $self->param('password2'); - - - if ( !$self->can_register ) { - $self->redirect_to('/noregister'); - return; - } - - - $self->app->logger->info( - "Received registration data. Login: '$login', email: '$email'."); + my $self = shift; + my $config = $self->app->config; + my $login = $self->param('login'); + my $name = $self->param('name'); + my $email = $self->param('email'); + my $password1 = $self->param('password1'); + my $password2 = $self->param('password2'); + + if (!$self->can_register) { + $self->redirect_to('/noregister'); + return; + } + + $self->app->logger->info( + "Received registration data. Login: '$login', email: '$email'."); + + try { + # this throws on failure + validate_registration_data($login, $email, $password1, $password2); + my $existing_user + = $self->app->repo->users_find(sub { $_->login eq $login }); + die "This login is already taken.\n" if $existing_user; + + my $salt = salt(); + my $hash = encrypt_password($password1, $salt); + my $new_user = $self->app->entityFactory->new_User( + login => $login, + email => $email, + real_name => $name, + pass => $hash, + pass2 => $salt + ); + $self->app->repo->users_save($new_user); - try { - # this throws on failure - validate_registration_data( $login, $email, $password1, $password2 ); - my $existing_user - = $self->app->repo->users_find( sub { $_->login eq $login } ); - die "This login is already taken.\n" if $existing_user; - - my $salt = salt(); - my $hash = encrypt_password( $password1, $salt ); - my $new_user = $self->app->entityFactory->new_User( - login => $login, - email => $email, - real_name => $name, - pass => $hash, - pass2 => $salt - ); - $self->app->repo->users_save($new_user); - - $self->flash( - msg_type => 'success', - msg => - "User created successfully! You may now login using login: $login." - ); - $self->redirect_to('/'); - } - catch { - my $failure_reason = $_; - $self->app->logger->warn($failure_reason); - $self->flash( msg_type => 'danger', msg => $failure_reason ); - $self->stash( - name => $name, - email => $email, - login => $login, - password1 => $password1, - password2 => $password2 - ); - $self->redirect_to('register'); - }; + $self->flash( + msg_type => 'success', + msg => "User created successfully! You may now login using login: $login." + ); + $self->redirect_to('/'); + } + catch { + my $failure_reason = $_; + $self->app->logger->warn($failure_reason); + $self->flash(msg_type => 'danger', msg => $failure_reason); + $self->stash( + name => $name, + email => $email, + login => $login, + password1 => $password1, + password2 => $password2 + ); + $self->redirect_to('register'); + }; } #################################################################################### 1; diff --git a/lib/BibSpace/Controller/Persistence.pm b/lib/BibSpace/Controller/Persistence.pm index 18f5fee..2b9c223 100644 --- a/lib/BibSpace/Controller/Persistence.pm +++ b/lib/BibSpace/Controller/Persistence.pm @@ -18,18 +18,17 @@ use BibSpace::Functions::FDB; use Mojo::Base 'Mojolicious::Controller'; - ################################################################################# sub persistence_status { my $self = shift; my $status - = "Status:
"
-      . $self->app->repo->lr->get_summary_table
-      . "
"; - $self->stash( msg_type => 'success', msg => $status ); - $self->flash( msg_type => 'success', msg => $status ); - $self->redirect_to( $self->get_referrer ); + = "Status:
"
+    . $self->app->repo->lr->get_summary_table
+    . "
"; + $self->stash(msg_type => 'success', msg => $status); + $self->flash(msg_type => 'success', msg => $status); + $self->redirect_to($self->get_referrer); } ################################################################################# @@ -37,37 +36,35 @@ sub persistence_status_ajax { my $self = shift; my $status - = "Status:
"
-      . $self->app->repo->lr->get_summary_table
-      . "
"; - $self->render( text => $status ); + = "Status:
"
+    . $self->app->repo->lr->get_summary_table
+    . "
"; + $self->render(text => $status); } ################################################################################# sub load_fixture { my $self = shift; - my $fixture_file - = $self->app->home->rel_file('fixture/bibspace_fixture.dat'); - $self->app->logger->info( - "Loading fixture from: " . $fixture_file->to_string ); + my $fixture_file = $self->app->home->rel_file('fixture/bibspace_fixture.dat'); + $self->app->logger->info("Loading fixture from: " . $fixture_file->to_string); my $fixture = Backup->new( dir => '' . $fixture_file->dirname, filename => '' . $fixture_file->basename ); - restore_storable_backup( $fixture, $self->app ); + restore_storable_backup($fixture, $self->app); my $status - = "Status:
"
-      . $self->app->repo->lr->get_summary_table
-      . "
"; + = "Status:
"
+    . $self->app->repo->lr->get_summary_table
+    . "
"; $self->flash( msg_type => 'success', msg => "Fixture loaded into memory and mysql. $status" ); - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } ################################################################################# sub save_fixture { @@ -75,69 +72,59 @@ sub save_fixture { $self->app->logger->warn("PERSISTENCE CONTROLLER does: save_fixture"); - my $fixture_file - = $self->app->home->rel_file('fixture/bibspace_fixture.dat'); + my $fixture_file = $self->app->home->rel_file('fixture/bibspace_fixture.dat'); - my $backup = Backup->create( 'dummy', "storable" ); - $backup->dir( '' . $fixture_file->dirname ); - $backup->filename( '' . $fixture_file->basename ); + my $backup = Backup->create('dummy', "storable"); + $backup->dir('' . $fixture_file->dirname); + $backup->filename('' . $fixture_file->basename); my $layer = $self->app->repo->lr->get_read_layer; my $path = "" . $backup->get_path; - $Storable::forgive_me - = "do store regexp please, we will not use them anyway"; + $Storable::forgive_me = "do store regexp please, we will not use them anyway"; # if you see any exceptions being thrown here, this might be due to REGEXP caused by DateTime pattern. # this should not happen currently however - I think it is fixed now. Storable::store $layer, $path; my $status - = "Status:
"
-      . $self->app->repo->lr->get_summary_table
-      . "
"; + = "Status:
"
+    . $self->app->repo->lr->get_summary_table
+    . "
"; $self->flash( msg_type => 'success', msg => "Fixture stored to '" . $backup->get_path . "'. $status" ); - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } ################################################################################# sub copy_mysql_to_smart { my $self = shift; - $self->app->logger->warn( - "PERSISTENCE CONTROLLER does: copy_mysql_to_smart"); + $self->app->logger->warn("PERSISTENCE CONTROLLER does: copy_mysql_to_smart"); - $self->app->repo->lr->copy_data( { from => 'mysql', to => 'smart' } ); + $self->app->repo->lr->copy_data({from => 'mysql', to => 'smart'}); $self->app->link_data; my $status - = "Status:
"
-      . $self->app->repo->lr->get_summary_table
-      . "
"; - $self->flash( - msg_type => 'success', - msg => "Copied mysql => smart. $status" - ); - $self->redirect_to( $self->get_referrer ); + = "Status:
"
+    . $self->app->repo->lr->get_summary_table
+    . "
"; + $self->flash(msg_type => 'success', msg => "Copied mysql => smart. $status"); + $self->redirect_to($self->get_referrer); } ################################################################################# sub copy_smart_to_mysql { my $self = shift; - - $self->app->repo->lr->copy_data( { from => 'smart', to => 'mysql' } ); + $self->app->repo->lr->copy_data({from => 'smart', to => 'mysql'}); my $status - = "Status:
"
-      . $self->app->repo->lr->get_summary_table
-      . "
"; - $self->flash( - msg_type => 'success', - msg => "Copied smart => mysql. $status" - ); - $self->redirect_to( $self->get_referrer ); + = "Status:
"
+    . $self->app->repo->lr->get_summary_table
+    . "
"; + $self->flash(msg_type => 'success', msg => "Copied smart => mysql. $status"); + $self->redirect_to($self->get_referrer); } ################################################################################# @@ -147,7 +134,7 @@ sub insert_random_data { my $str_len = 60; - for ( 1 .. $num ) { + for (1 .. $num) { my $obj = $self->app->entityFactory->new_User( login => random_string($str_len), email => random_string($str_len) . '@example.com', @@ -158,19 +145,20 @@ sub insert_random_data { ); $self->app->repo->users_save($obj); - $obj = $self->app->entityFactory->new_Author( - uid => random_string($str_len), ); + $obj + = $self->app->entityFactory->new_Author(uid => random_string($str_len),); $self->app->repo->authors_save($obj); - $obj = $self->app->entityFactory->new_Entry( - bib => random_string($str_len), ); + $obj + = $self->app->entityFactory->new_Entry(bib => random_string($str_len),); $self->app->repo->entries_save($obj); - $obj = $self->app->entityFactory->new_TagType( - name => random_string($str_len), ); + $obj + = $self->app->entityFactory->new_TagType(name => random_string($str_len), + ); $self->app->repo->tagTypes_save($obj); - my $tt = ( $self->app->repo->tagTypes_all )[0]; + my $tt = ($self->app->repo->tagTypes_all)[0]; $obj = $self->app->entityFactory->new_Tag( name => random_string($str_len), @@ -178,21 +166,17 @@ sub insert_random_data { ); $self->app->repo->tags_save($obj); - - $obj = $self->app->entityFactory->new_Team( - name => random_string($str_len), ); + $obj + = $self->app->entityFactory->new_Team(name => random_string($str_len),); $self->app->repo->teams_save($obj); } my $status - = "Status:
"
-      . $self->app->repo->lr->get_summary_table
-      . "
"; - $self->flash( - msg_type => 'success', - msg => "Copied smart => mysql. $status" - ); - $self->redirect_to( $self->get_referrer ); + = "Status:
"
+    . $self->app->repo->lr->get_summary_table
+    . "
"; + $self->flash(msg_type => 'success', msg => "Copied smart => mysql. $status"); + $self->redirect_to($self->get_referrer); } ################################################################################# sub reset_smart { @@ -212,15 +196,14 @@ sub reset_smart { $self->app->preferences->run_in_demo_mode(1); say "setting preferences->run_in_demo_mode to: '" - . $self->app->preferences->run_in_demo_mode . "'"; - + . $self->app->preferences->run_in_demo_mode . "'"; my $status - = "Status:
"
-      . $self->app->repo->lr->get_summary_table
-      . "
"; - $self->flash( msg_type => 'success', msg => $status ); - $self->redirect_to( $self->get_referrer ); + = "Status:
"
+    . $self->app->repo->lr->get_summary_table
+    . "
"; + $self->flash(msg_type => 'success', msg => $status); + $self->redirect_to($self->get_referrer); } ################################################################################# sub reset_mysql { @@ -232,23 +215,23 @@ sub reset_mysql { if ($layer) { $layer->reset_data; my $status - = "Status:
"
-        . $self->app->repo->lr->get_summary_table
-        . "
"; - $self->flash( msg_type => 'success', msg => $status ); + = "Status:
"
+      . $self->app->repo->lr->get_summary_table
+      . "
"; + $self->flash(msg_type => 'success', msg => $status); } else { my $status - = "Status:
"
-        . $self->app->repo->lr->get_summary_table
-        . "
"; + = "Status:
"
+      . $self->app->repo->lr->get_summary_table
+      . "
"; $self->flash( msg_type => 'danger', msg => "Reset failed - backend handle undefined. " . $status ); } - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } ################################################################################# sub reset_all { @@ -260,20 +243,18 @@ sub reset_all { foreach (@layers) { $_->reset_data } $self->app->repo->lr->reset_uid_providers; - # no pub_admin user would lock the whole system # if you insert it here, it may will cause clash of IDs # $self->app->insert_admin; # instead, do not insert admin and set system in demo mode $self->app->preferences->run_in_demo_mode(1); - my $status - = "Status:
"
-      . $self->app->repo->lr->get_summary_table
-      . "
"; - $self->flash( msg_type => 'success', msg => $status ); - $self->redirect_to( $self->get_referrer ); + = "Status:
"
+    . $self->app->repo->lr->get_summary_table
+    . "
"; + $self->flash(msg_type => 'success', msg => $status); + $self->redirect_to($self->get_referrer); } ################################################################################# sub system_status { @@ -284,14 +265,12 @@ sub system_status { my $backups_dir = $self->app->config->{backups_dir}; my $upload_dir = $self->app->get_upload_dir; - my $backup_dir_absolute = $self->config->{backups_dir}; $backup_dir_absolute - =~ s!/*$!/!; # makes sure that there is exactly one / at the end + =~ s!/*$!/!; # makes sure that there is exactly one / at the end my $errored = 0; - ################### $msg .= "
" . "Connecting to DB: "; try { @@ -341,11 +320,11 @@ sub system_status { $msg .= "
" . "End."; if ($errored) { - $self->render( text => $msg, status => 500 ); + $self->render(text => $msg, status => 500); return; } else { - $self->render( text => $msg, status => 200 ); + $self->render(text => $msg, status => 200); } } ################################################################################# diff --git a/lib/BibSpace/Controller/Preferences.pm b/lib/BibSpace/Controller/Preferences.pm index 35f738a..8704a03 100644 --- a/lib/BibSpace/Controller/Preferences.pm +++ b/lib/BibSpace/Controller/Preferences.pm @@ -4,12 +4,12 @@ use strict; use warnings; use utf8; use v5.16; #because of ~~ + # use File::Slurp; use Try::Tiny; use Data::Dumper; - use Mojo::Base 'Mojolicious::Controller'; use Storable; use BibSpace::Functions::Core; @@ -19,49 +19,55 @@ use Class::MOP; use Moose::Util qw/does_role/; - use BibSpace::Converter::IHtmlBibtexConverter; ################################################################################# sub index { - my $self = shift; + my $self = shift; - # http://search.cpan.org/~ether/Moose-2.2004/lib/Moose/Util.pm#does_role($class_or_obj,_$role_or_obj) - my @converterClasses = grep { does_role($_ , 'IHtmlBibtexConverter') } Class::MOP::get_all_metaclasses; - @converterClasses = grep { $_ ne 'IHtmlBibtexConverter' } @converterClasses; - +# http://search.cpan.org/~ether/Moose-2.2004/lib/Moose/Util.pm#does_role($class_or_obj,_$role_or_obj) + my @converterClasses = grep { does_role($_, 'IHtmlBibtexConverter') } + Class::MOP::get_all_metaclasses; + @converterClasses = grep { $_ ne 'IHtmlBibtexConverter' } @converterClasses; - $self->stash( preferences => $self->app->preferences, converters => \@converterClasses ); - $self->render( template => 'display/preferences' ); + $self->stash( + preferences => $self->app->preferences, + converters => \@converterClasses + ); + $self->render(template => 'display/preferences'); } ################################################################################# sub save { - my $self = shift; - my $bibitex_html_converter = $self->param('bibitex_html_converter'); - my $local_time_zone = $self->param('local_time_zone'); - my $output_time_format = $self->param('output_time_format'); - my $run_in_demo_mode = $self->param('run_in_demo_mode'); + my $self = shift; + my $bibitex_html_converter = $self->param('bibitex_html_converter'); + my $local_time_zone = $self->param('local_time_zone'); + my $output_time_format = $self->param('output_time_format'); + my $run_in_demo_mode = $self->param('run_in_demo_mode'); + + if ($run_in_demo_mode and $run_in_demo_mode eq 'on') { + $run_in_demo_mode = 1; + } + else { + $run_in_demo_mode = 0; + } - if($run_in_demo_mode and $run_in_demo_mode eq 'on'){ - $run_in_demo_mode = 1; - } - else{ - $run_in_demo_mode = 0; - } + my $msg = "Preferences saved!"; + my $msg_type = "success"; - my $msg = "Preferences saved!"; - my $msg_type = "success"; + # TODO: validate inputs + $self->app->preferences->run_in_demo_mode($run_in_demo_mode); + $self->app->preferences->bibitex_html_converter($bibitex_html_converter); + $self->app->preferences->local_time_zone($local_time_zone); + $self->app->preferences->output_time_format($output_time_format); - # TODO: validate inputs - $self->app->preferences->run_in_demo_mode($run_in_demo_mode); - $self->app->preferences->bibitex_html_converter($bibitex_html_converter); - $self->app->preferences->local_time_zone($local_time_zone); - $self->app->preferences->output_time_format($output_time_format); - + $self->stash( + preferences => $self->app->preferences, + msg_type => $msg_type, + msg => $msg + ); - $self->stash( preferences => $self->app->preferences, msg_type=>$msg_type, msg =>$msg ); - # $self->render( template => 'display/preferences' ); - $self->redirect_to( $self->get_referrer ); + # $self->render( template => 'display/preferences' ); + $self->redirect_to($self->get_referrer); } ################################################################################# 1; diff --git a/lib/BibSpace/Controller/Publications.pm b/lib/BibSpace/Controller/Publications.pm index 1b60534..642a6b0 100644 --- a/lib/BibSpace/Controller/Publications.pm +++ b/lib/BibSpace/Controller/Publications.pm @@ -21,7 +21,6 @@ use Encode; use BibSpace::Functions::Core; use BibSpace::Functions::FPublications; - use Mojo::Base 'Mojolicious::Controller'; use Mojo::Base 'Mojolicious::Plugin::Config'; use Mojo::UserAgent; @@ -56,12 +55,12 @@ our %mons = ( sub all { my $self = shift; - my @all = Fget_publications_main_hashed_args( $self, { year => undef } ); - my @filtered = Fget_publications_main_hashed_args( $self, {}, \@all ); + my @all = Fget_publications_main_hashed_args($self, {year => undef}); + my @filtered = Fget_publications_main_hashed_args($self, {}, \@all); - $self->stash( entries => \@filtered, all_entries => \@all ); - my $html = $self->render_to_string( template => 'publications/all' ); - $self->render( data => $html ); + $self->stash(entries => \@filtered, all_entries => \@all); + my $html = $self->render_to_string(template => 'publications/all'); + $self->render(data => $html); } #################################################################################### sub all_recently_added { @@ -70,18 +69,17 @@ sub all_recently_added { $self->app->logger->info("Displaying recently added entries."); - my @all = Fget_publications_main_hashed_args( $self, { year => undef } ); + my @all = Fget_publications_main_hashed_args($self, {year => undef}); my @added_entries = sort { $b->creation_time cmp $a->creation_time } @all; - @added_entries = @added_entries[ 0 .. $num - 1 ]; + @added_entries = @added_entries[0 .. $num - 1]; - my @filtered - = Fget_publications_main_hashed_args( $self, {}, \@added_entries ); + my @filtered = Fget_publications_main_hashed_args($self, {}, \@added_entries); # special sorting here @filtered = sort { $b->creation_time cmp $a->creation_time } @filtered; - $self->stash( entries => \@filtered, all_entries => \@added_entries ); - $self->render( template => 'publications/all' ); + $self->stash(entries => \@filtered, all_entries => \@added_entries); + $self->render(template => 'publications/all'); } #################################################################################### @@ -91,21 +89,18 @@ sub all_recently_modified { $self->app->logger->info("Displaying recently modified entries."); - - my @all = Fget_publications_main_hashed_args( $self, { year => undef } ); - my @modified_entries - = sort { $b->modified_time cmp $a->modified_time } @all; - @modified_entries = @modified_entries[ 0 .. $num - 1 ]; + my @all = Fget_publications_main_hashed_args($self, {year => undef}); + my @modified_entries = sort { $b->modified_time cmp $a->modified_time } @all; + @modified_entries = @modified_entries[0 .. $num - 1]; my @filtered - = Fget_publications_main_hashed_args( $self, {}, \@modified_entries ); + = Fget_publications_main_hashed_args($self, {}, \@modified_entries); # special sorting here @filtered = sort { $b->modified_time cmp $a->modified_time } @filtered; - - $self->stash( entries => \@filtered, all_entries => \@modified_entries ); - $self->render( template => 'publications/all' ); + $self->stash(entries => \@filtered, all_entries => \@modified_entries); + $self->render(template => 'publications/all'); } #################################################################################### @@ -114,55 +109,51 @@ sub all_without_tag { my $tagtype = $self->param('tagtype') // 1; # this will filter entries based on query - my @all = Fget_publications_main_hashed_args( $self, { year => undef } ); + my @all = Fget_publications_main_hashed_args($self, {year => undef}); my @untagged_entries = grep { scalar $_->get_tags($tagtype) == 0 } @all; my @filtered - = Fget_publications_main_hashed_args( $self, {}, \@untagged_entries ); - + = Fget_publications_main_hashed_args($self, {}, \@untagged_entries); my $msg - = "This list contains papers that have no tags of type '$tagtype'. Use this list to tag the untagged papers! "; - $self->stash( msg_type => 'info', msg => $msg ); - $self->stash( entries => \@filtered, all_entries => \@untagged_entries ); - $self->render( template => 'publications/all' ); + = "This list contains papers that have no tags of type '$tagtype'. Use this list to tag the untagged papers! "; + $self->stash(msg_type => 'info', msg => $msg); + $self->stash(entries => \@filtered, all_entries => \@untagged_entries); + $self->render(template => 'publications/all'); } #################################################################################### sub all_orphaned { my $self = shift; - my @all = Fget_publications_main_hashed_args( $self, { year => undef } ); + my @all = Fget_publications_main_hashed_args($self, {year => undef}); - my @entries = grep { scalar( $_->get_authors ) == 0 } @all; + my @entries = grep { scalar($_->get_authors) == 0 } @all; - my @filtered = Fget_publications_main_hashed_args( $self, {}, \@entries ); + my @filtered = Fget_publications_main_hashed_args($self, {}, \@entries); my $msg - = "This list contains papers, that are currently not assigned to any of authors."; + = "This list contains papers, that are currently not assigned to any of authors."; $msg - .= ' Click to delete '; + .= ' Click to delete '; - $self->stash( msg_type => 'info', msg => $msg ); - $self->stash( entries => \@filtered, all_entries => \@entries ); - $self->render( template => 'publications/all' ); + $self->stash(msg_type => 'info', msg => $msg); + $self->stash(entries => \@filtered, all_entries => \@entries); + $self->render(template => 'publications/all'); } #################################################################################### sub show_unrelated_to_team { my $self = shift; my $team_id = $self->param('teamid'); - $self->app->logger->info( - "Displaying entries unrelated to team '$team_id'."); - + $self->app->logger->info("Displaying entries unrelated to team '$team_id'."); my $team_name = ""; - my $team = $self->app->repo->teams_find( sub { $_->id == $team_id } ); + my $team = $self->app->repo->teams_find(sub { $_->id == $team_id }); $team_name = $team->name if defined $team; - - my @all = Fget_publications_main_hashed_args( $self, { year => undef } ); + my @all = Fget_publications_main_hashed_args($self, {year => undef}); my @teamEntres = $team->get_entries; my %inTeam = map { $_ => 1 } @teamEntres; @@ -171,19 +162,19 @@ sub show_unrelated_to_team { # hash destroys order! @entriesUnrelated = sort_publications(@entriesUnrelated); my @filtered - = Fget_publications_main_hashed_args( $self, {}, \@entriesUnrelated ); + = Fget_publications_main_hashed_args($self, {}, \@entriesUnrelated); my $msg = "This list contains papers, that are:
  • Not assigned to the team " - . $team_name . "
  • + . $team_name . "
  • Not assigned to any author (former or actual) of the team " - . $team_name . "
  • + . $team_name . "
"; - $self->stash( msg_type => 'info', msg => $msg ); - $self->stash( entries => \@filtered, all_entries => \@entriesUnrelated ); - $self->render( template => 'publications/all' ); + $self->stash(msg_type => 'info', msg => $msg); + $self->stash(entries => \@filtered, all_entries => \@entriesUnrelated); + $self->render(template => 'publications/all'); } #################################################################################### sub all_with_missing_month { @@ -191,35 +182,30 @@ sub all_with_missing_month { $self->app->logger->info("Displaying entries without month"); - - my @all = Fget_publications_main_hashed_args( $self, { year => undef } ); + my @all = Fget_publications_main_hashed_args($self, {year => undef}); my @entries - = grep { !defined $_->month or $_->month < 1 or $_->month > 12 } @all; + = grep { !defined $_->month or $_->month < 1 or $_->month > 12 } @all; - my @filtered = Fget_publications_main_hashed_args( $self, {}, \@entries ); + my @filtered = Fget_publications_main_hashed_args($self, {}, \@entries); my $msg = "This list contains entries with missing BibTeX field 'month'. "; $msg .= "Add this data to get the proper chronological sorting."; - $self->stash( msg_type => 'info', msg => $msg ); - $self->stash( entries => \@filtered, all_entries => \@entries ); - $self->render( template => 'publications/all' ); + $self->stash(msg_type => 'info', msg => $msg); + $self->stash(entries => \@filtered, all_entries => \@entries); + $self->render(template => 'publications/all'); } #################################################################################### sub all_candidates_to_delete { my $self = shift; - $self->app->logger->info( - "Displaying entries that are candidates_to_delete"); - + $self->app->logger->info("Displaying entries that are candidates_to_delete"); - my @all = Fget_publications_main_hashed_args( $self, { year => undef } ); + my @all = Fget_publications_main_hashed_args($self, {year => undef}); my @entries = grep { scalar $_->get_tags == 0 } @all; # no tags - @entries - = grep { scalar $_->get_teams == 0 } @entries; # no relation to teams - @entries = grep { scalar $_->get_exceptions == 0 } @entries; # no exceptions - my @filtered = Fget_publications_main_hashed_args( $self, {}, \@entries ); - + @entries = grep { scalar $_->get_teams == 0 } @entries; # no relation to teams + @entries = grep { scalar $_->get_exceptions == 0 } @entries; # no exceptions + my @filtered = Fget_publications_main_hashed_args($self, {}, \@entries); my $msg = "

This list contains papers, that are:

    @@ -230,9 +216,9 @@ sub all_candidates_to_delete {

Such entries may wanted to be removed form the system or serve as a help with configuration.

"; - $self->stash( msg_type => 'info', msg => $msg ); - $self->stash( entries => \@filtered, all_entries => \@entries ); - $self->render( template => 'publications/all' ); + $self->stash(msg_type => 'info', msg => $msg); + $self->stash(entries => \@filtered, all_entries => \@entries); + $self->render(template => 'publications/all'); } #################################################################################### #################################################################################### @@ -240,7 +226,7 @@ sub all_candidates_to_delete { sub all_bibtex { my $self = shift; - my @objs = Fget_publications_main_hashed_args( $self, { hidden => 0 } ); + my @objs = Fget_publications_main_hashed_args($self, {hidden => 0}); my $big_str = "
\n";
   foreach my $obj (@objs) {
@@ -248,7 +234,7 @@ sub all_bibtex {
     $big_str .= "\n";
   }
   $big_str .= "\n
"; - $self->render( text => $big_str ); + $self->render(text => $big_str); } #################################################################################### @@ -256,11 +242,11 @@ sub all_read { my $self = shift; # this function does filtering ! - my @objs = Fget_publications_main_hashed_args( $self, { hidden => 0 } ); + my @objs = Fget_publications_main_hashed_args($self, {hidden => 0}); - $self->stash( entries => \@objs ); - my $html = $self->render_to_string( template => 'publications/all_read' ); - $self->render( data => $html ); + $self->stash(entries => \@objs); + my $html = $self->render_to_string(template => 'publications/all_read'); + $self->render(data => $html); } #################################################################################### @@ -269,17 +255,17 @@ sub single { my $self = shift; my $id = $self->param('id'); - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); + my $entry = $self->app->repo->entries_find(sub { $_->id == $id }); my @objs; - if ( defined $entry ) { + if (defined $entry) { push @objs, $entry; } else { - $self->stash( msg_type => 'danger', msg => "Entry $id does not exist." ); + $self->stash(msg_type => 'danger', msg => "Entry $id does not exist."); } - $self->stash( entries => \@objs ); - $self->render( template => 'publications/all' ); + $self->stash(entries => \@objs); + $self->render(template => 'publications/all'); } #################################################################################### @@ -290,13 +276,13 @@ sub single_read { my @objs = (); - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); + my $entry = $self->app->repo->entries_find(sub { $_->id == $id }); - if ( defined $entry and $entry->is_hidden == 0 ) { + if (defined $entry and $entry->is_hidden == 0) { push @objs, $entry; } - $self->stash( entries => \@objs ); - $self->render( template => 'publications/all_read' ); + $self->stash(entries => \@objs); + $self->render(template => 'publications/all_read'); } #################################################################################### sub fixMonths { @@ -315,7 +301,7 @@ sub fixMonths { msg => 'Fixing entries month field finished.', msg_type => 'info' ); - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } #################################################################################### sub toggle_hide { @@ -324,18 +310,17 @@ sub toggle_hide { $self->app->logger->info("Toggle hide entry '$id'."); - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); + my $entry = $self->app->repo->entries_find(sub { $_->id == $id }); - if ( defined $entry ) { + if (defined $entry) { $entry->toggle_hide; $self->app->repo->entries_update($entry); } else { - $self->flash( msg => "There is no entry with id $id" ); + $self->flash(msg => "There is no entry with id $id"); } - - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } #################################################################################### sub make_paper { @@ -344,18 +329,17 @@ sub make_paper { $self->app->logger->info("Make entry '$id' 'paper'."); - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); + my $entry = $self->app->repo->entries_find(sub { $_->id == $id }); - if ( defined $entry ) { + if (defined $entry) { $entry->make_paper(); $self->app->repo->entries_update($entry); } else { - $self->flash( msg => "There is no entry with id $id" ); + $self->flash(msg => "There is no entry with id $id"); } - - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } #################################################################################### sub make_talk { @@ -364,25 +348,24 @@ sub make_talk { $self->app->logger->info("Make entry '$id' 'talk'."); - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); + my $entry = $self->app->repo->entries_find(sub { $_->id == $id }); - if ( defined $entry ) { + if (defined $entry) { $entry->make_talk(); $self->app->repo->entries_update($entry); } else { - $self->flash( msg => "There is no entry with id $id" ); + $self->flash(msg => "There is no entry with id $id"); } - - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } #################################################################################### sub delete_orphaned { my $self = shift; - my @entries = $self->app->repo->entries_filter( - sub { scalar( $_->get_authors ) == 0 } ); + my @entries + = $self->app->repo->entries_filter(sub { scalar($_->get_authors) == 0 }); foreach my $entry (@entries) { my @au = $entry->authorships_all; @@ -396,12 +379,11 @@ sub delete_orphaned { my $num_deleted = $self->app->repo->entries_delete(@entries); my $msg = "$num_deleted entries have been removed"; - $self->flash( msg => $msg, msg_type => 'info' ); + $self->flash(msg => $msg, msg_type => 'info'); $self->redirect_to('all_orphaned'); } - #################################################################################### sub fix_file_urls { my $self = shift; @@ -412,7 +394,7 @@ sub fix_file_urls { my @all_entries; if ($id) { - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); + my $entry = $self->app->repo->entries_find(sub { $_->id == $id }); push @all_entries, $entry if $entry; } else { @@ -428,10 +410,9 @@ sub fix_file_urls { ++$num_checks; my $str; $str .= "Entry " . $entry->id . ": "; - $entry->discover_attachments( $self->app->get_upload_dir ); + $entry->discover_attachments($self->app->get_upload_dir); my @discovered_types = $entry->attachments_keys; - $str .= "has types: ("; foreach (@discovered_types) { $str .= " $_, "; @@ -447,10 +428,10 @@ sub fix_file_urls { id => $entry->id )->to_abs; - if ( $file and $file->exists ) { - $entry->remove_bibtex_fields( ['pdf'] ); + if ($file and $file->exists) { + $entry->remove_bibtex_fields(['pdf']); $str .= "\n\t"; - $entry->add_bibtex_field( "pdf", "$file_url" ); + $entry->add_bibtex_field("pdf", "$file_url"); $fixed = 1; $str .= "Added Bibtex filed PDF " . $file_url; } @@ -462,10 +443,10 @@ sub fix_file_urls { id => $entry->id )->to_abs; - if ( $file and $file->exists ) { - $entry->remove_bibtex_fields( ['slides'] ); + if ($file and $file->exists) { + $entry->remove_bibtex_fields(['slides']); $str .= "\n\t"; - $entry->add_bibtex_field( "slides", "$file_url" ); + $entry->add_bibtex_field("slides", "$file_url"); $fixed = 1; $str .= "Added Bibtex filed SLIDES " . $file_url; } @@ -474,8 +455,7 @@ sub fix_file_urls { if ($fixed) { $big_str .= $str; ++$num_fixes; - $entry->regenerate_html( 0, $self->app->bst, - $self->app->bibtexConverter ); + $entry->regenerate_html(0, $self->app->bst, $self->app->bibtexConverter); } } @@ -484,9 +464,9 @@ sub fix_file_urls { $self->flash( msg_type => 'info', msg => - "Checked $num_checks and regenerated $num_fixes entries. You may need to run regenerate HTML force now. Detailed fix results have been saved to log." + "Checked $num_checks and regenerated $num_fixes entries. You may need to run regenerate HTML force now. Detailed fix results have been saved to log." ); - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } #################################################################################### sub remove_attachment { @@ -497,25 +477,23 @@ sub remove_attachment { $self->app->logger->info( "Requested to remove attachment of type '$filetype'."); - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); - my ( $msg, $msg_type ); + my $entry = $self->app->repo->entries_find(sub { $_->id == $id }); + my ($msg, $msg_type); - $entry->discover_attachments( $self->app->get_upload_dir ); + $entry->discover_attachments($self->app->get_upload_dir); - - if ( $entry->attachments_has($filetype) ) { + if ($entry->attachments_has($filetype)) { $self->app->logger->debug("Entry has attachment of type '$filetype'."); - if ( $filetype eq 'paper' ) { - $entry->remove_bibtex_fields( ['pdf'] ); + if ($filetype eq 'paper') { + $entry->remove_bibtex_fields(['pdf']); } - elsif ( $filetype eq 'slides' ) { - $entry->remove_bibtex_fields( ['slides'] ); + elsif ($filetype eq 'slides') { + $entry->remove_bibtex_fields(['slides']); } $entry->delete_attachment($filetype); - $entry->regenerate_html( 1, $self->app->bst, - $self->app->bibtexConverter ); + $entry->regenerate_html(1, $self->app->bst, $self->app->bibtexConverter); $self->app->repo->entries_save($entry); $msg = "The attachment has been removed for entry '$id'."; @@ -526,13 +504,13 @@ sub remove_attachment { $self->app->logger->debug("Entry has NO attachment of type '$filetype'."); $msg - = "File not found. Cannot remove attachment. Filetype '$filetype', entry '$id'."; + = "File not found. Cannot remove attachment. Filetype '$filetype', entry '$id'."; $msg_type = 'danger'; $self->app->logger->error($msg); } - $self->flash( msg_type => $msg_type, msg => $msg ); - $self->redirect_to( $self->get_referrer ); + $self->flash(msg_type => $msg_type, msg => $msg); + $self->redirect_to($self->get_referrer); } #################################################################################### sub discover_attachments { @@ -540,22 +518,21 @@ sub discover_attachments { my $id = $self->param('id'); my $do = $self->param('do'); - - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); + my $entry = $self->app->repo->entries_find(sub { $_->id == $id }); $self->app->logger->info("Discovery of attachments for entry ID '$id'."); my $msg; my $msg_type = 'info'; - if ( $entry and $do and $do == 1 ) { - $entry->discover_attachments( $self->app->get_upload_dir ); + if ($entry and $do and $do == 1) { + $entry->discover_attachments($self->app->get_upload_dir); $msg .= "Discovery was run for dir '" . $self->app->get_upload_dir . "'."; } elsif ($entry) { $msg .= "Just displaying information. "; $msg - .= "Attachments debug:
"
-        . $entry->get_attachments_debug_string
-        . "
"; + .= "Attachments debug:
"
+      . $entry->get_attachments_debug_string
+      . "
"; } else { $msg = "Cannot discover, entry '$id' not found."; @@ -563,9 +540,8 @@ sub discover_attachments { $self->app->logger->error($msg); } - - $self->flash( msg_type => $msg_type, msg => $msg ); - $self->redirect_to( $self->get_referrer ); + $self->flash(msg_type => $msg_type, msg => $msg); + $self->redirect_to($self->get_referrer); } #################################################################################### sub download { @@ -573,46 +549,45 @@ sub download { my $id = $self->param('id'); # entry ID my $filetype = $self->param('filetype'); - - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); + my $entry = $self->app->repo->entries_find(sub { $_->id == $id }); my $file; if ($entry) { - $entry->discover_attachments( $self->app->get_upload_dir ); + $entry->discover_attachments($self->app->get_upload_dir); $file = $entry->get_attachment($filetype); } else { $self->app->logger->error("Cannot download - entry '$id' not found."); - $self->render( status => 404, text => "File not found." ); + $self->render(status => 404, text => "File not found."); return; } - if ( $file and -e $file ) { - $self->render_file( 'filepath' => $file ); + if ($file and -e $file) { + $self->render_file('filepath' => $file); return; } $self->app->logger->error( "File not found. Requested download for entry '$id', filetype '$filetype'." ); - $self->render( text => "File not found.", status => 404 ); + $self->render(text => "File not found.", status => 404); } #################################################################################### sub add_pdf { my $self = shift; my $id = $self->param('id'); - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); + my $entry = $self->app->repo->entries_find(sub { $_->id == $id }); - if ( !defined $entry ) { - $self->flash( msg_type => 'danger', msg => "Entry '$id' not found." ); - $self->redirect_to( $self->get_referrer ); + if (!defined $entry) { + $self->flash(msg_type => 'danger', msg => "Entry '$id' not found."); + $self->redirect_to($self->get_referrer); return; } $entry->populate_from_bib(); - $entry->generate_html( $self->app->bst, $self->app->bibtexConverter ); + $entry->generate_html($self->app->bst, $self->app->bibtexConverter); - $self->stash( mentry => $entry ); - $self->render( template => 'publications/pdf_upload' ); + $self->stash(mentry => $entry); + $self->render(template => 'publications/pdf_upload'); } #################################################################################### sub add_pdf_post { @@ -621,12 +596,12 @@ sub add_pdf_post { my $filetype = $self->param('filetype'); my $uploaded_file = $self->param('uploaded_file'); - my $uploads_directory = Path::Tiny->new( $self->app->get_upload_dir ); + my $uploads_directory = Path::Tiny->new($self->app->get_upload_dir); $self->app->logger->info("Saving attachment for entry '$id'"); # Check file size - if ( $self->req->is_limit_exceeded ) { + if ($self->req->is_limit_exceeded) { my $curr_limit_B = $ENV{MOJO_MAX_MESSAGE_SIZE}; $curr_limit_B ||= 16777216; my $curr_limit_MB = $curr_limit_B / 1024 / 1024; @@ -637,83 +612,76 @@ sub add_pdf_post { msg => "The File is too big and cannot be saved!", msg_type => "danger" ); - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); return; } - - if ( !$uploaded_file ) { - $self->flash( msg => "File upload unsuccessful!", msg_type => "danger" ); + if (!$uploaded_file) { + $self->flash(msg => "File upload unsuccessful!", msg_type => "danger"); $self->app->logger->info( "Saving attachment for paper id '$id' FAILED. Unknown reason"); - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); return; } my $size = $uploaded_file->size; - my $sizeKB = int( $size / 1024 ); - if ( $size == 0 ) { + my $sizeKB = int($size / 1024); + if ($size == 0) { $self->flash( msg => "No file was selected or file has 0 bytes! Not saving!", msg_type => "danger" ); $self->app->logger->info( "Saving attachment for paper id '$id' FAILED. File size is 0."); - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); return; } - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); + my $entry = $self->app->repo->entries_find(sub { $_->id == $id }); - if ( !defined $entry ) { - $self->flash( - msg_type => 'danger', - msg => "Entry '$id' does not exist." - ); - $self->redirect_to( $self->get_referrer ); + if (!defined $entry) { + $self->flash(msg_type => 'danger', msg => "Entry '$id' does not exist."); + $self->redirect_to($self->get_referrer); return; } - my $name = $uploaded_file->filename; - my @dot_arr = split( /\./, $name ); + my @dot_arr = split(/\./, $name); my $extension = $dot_arr[-1]; - my $file_url; my $destination; - if ( $filetype eq 'paper' ) { + if ($filetype eq 'paper') { $entry->delete_attachment('paper'); - $destination - = $uploads_directory->path( "papers", "paper-$id.$extension" ); + $destination = $uploads_directory->path("papers", "paper-$id.$extension"); $uploaded_file->move_to($destination); $self->app->logger->debug( "Attachments file has been moved to: $destination."); - $entry->add_attachment( 'paper', $destination ); + $entry->add_attachment('paper', $destination); $file_url = $self->url_for( 'download_publication_pdf', filetype => "paper", id => $entry->id )->to_abs; - $entry->add_bibtex_field( 'pdf', "$file_url" ); + $entry->add_bibtex_field('pdf', "$file_url"); } - elsif ( $filetype eq 'slides' ) { + elsif ($filetype eq 'slides') { $entry->delete_attachment('slides'); $destination - = $uploads_directory->path( "slides", "slides-paper-$id.$extension" ); + = $uploads_directory->path("slides", "slides-paper-$id.$extension"); $uploaded_file->move_to($destination); $self->app->logger->debug( "Attachments file has been moved to: $destination."); - $entry->add_attachment( 'slides', $destination ); + $entry->add_attachment('slides', $destination); $file_url = $self->url_for( 'download_publication', filetype => "slides", id => $entry->id )->to_abs; - $entry->add_bibtex_field( 'slides', "$file_url" ); + $entry->add_bibtex_field('slides', "$file_url"); } else { # ignore - we support only pdf and slides so far @@ -723,15 +691,15 @@ sub add_pdf_post { "Saving attachment for entry '$id' under: '$destination'."); my $msg - = "Successfully uploaded the $sizeKB KB file as $filetype. + = "Successfully uploaded the $sizeKB KB file as $filetype. The file was renamed to: " . $destination->basename . ""; + . $file_url . "\">" . $destination->basename . ""; - $entry->regenerate_html( 1, $self->app->bst, $self->app->bibtexConverter ); + $entry->regenerate_html(1, $self->app->bst, $self->app->bibtexConverter); $self->app->repo->entries_save($entry); - $self->flash( msg_type => 'success', msg => $msg ); - $self->redirect_to( $self->get_referrer ); + $self->flash(msg_type => 'success', msg => $msg); + $self->redirect_to($self->get_referrer); } #################################################################################### @@ -740,14 +708,14 @@ sub mark_author_to_regenerate { my $author_id = $self->param('author_id'); my $converter = $self->app->bibtexConverter; - my $author = $self->app->repo->authors_find( sub { $_->id == $author_id } ); + my $author = $self->app->repo->authors_find(sub { $_->id == $author_id }); my @entries; if ($author) { - $self->app->logger->info( "Marking entries of author '" - . $author->uid - . "' for HTML regeneration." ); + $self->app->logger->info("Marking entries of author '" + . $author->uid + . "' for HTML regeneration."); @entries = $author->get_entries; foreach my $entry (@entries) { @@ -757,15 +725,13 @@ sub mark_author_to_regenerate { } my $msg - = "" - . scalar(@entries) - . " entries have been MARKED for regeneration. "; + = "" . scalar(@entries) . " entries have been MARKED for regeneration. "; $msg .= "Now you may run 'regenerate all' or 'regenerate in chunks'. "; $msg .= "Regenration in chunks is useful for large set of entries. "; $self->app->logger->info($msg); - $self->flash( msg_type => 'info', msg => $msg ); - $self->redirect_to( $self->get_referrer ); + $self->flash(msg_type => 'info', msg => $msg); + $self->redirect_to($self->get_referrer); } #################################################################################### sub regenerate_html_for_all { @@ -777,16 +743,16 @@ sub regenerate_html_for_all { $self->app->logger->info("regenerate_html_for_all is running"); my @entries - = $self->app->repo->entries_filter( sub { $_->need_html_regen == 1 } ); + = $self->app->repo->entries_filter(sub { $_->need_html_regen == 1 }); my $num_regen - = Fregenerate_html_for_array( $self->app, 0, $converter, \@entries ); + = Fregenerate_html_for_array($self->app, 0, $converter, \@entries); my $left_todo = scalar(@entries) - $num_regen; my $msg = "$num_regen entries have been regenerated. "; $msg .= "$left_todo furter entries still require regeneration."; $self->app->logger->info($msg); - $self->flash( msg_type => 'info', msg => $msg ); - $self->redirect_to( $self->get_referrer ); + $self->flash(msg_type => 'info', msg => $msg); + $self->redirect_to($self->get_referrer); } #################################################################################### sub regenerate_html_in_chunk { @@ -801,23 +767,23 @@ sub regenerate_html_in_chunk { "regenerate_html_in_chunk is running, chunk size $chunk_size "); my @entries - = $self->app->repo->entries_filter( sub { $_->need_html_regen == 1 } ); + = $self->app->repo->entries_filter(sub { $_->need_html_regen == 1 }); my $last_entry_index = $chunk_size - 1; $last_entry_index = scalar(@entries) - 1 if scalar(@entries) < $chunk_size; - my @portion_of_entries = @entries[ 0 .. $last_entry_index ]; + my @portion_of_entries = @entries[0 .. $last_entry_index]; @portion_of_entries = grep { defined $_ } @portion_of_entries; - my $num_regen = Fregenerate_html_for_array( $self->app, 1, $converter, - \@portion_of_entries ); + my $num_regen = Fregenerate_html_for_array($self->app, 1, $converter, + \@portion_of_entries); my $left_todo = scalar(@entries) - $num_regen; my $msg = "$num_regen entries have been regenerated. "; $msg .= "$left_todo furter entries still require regeneration."; $self->app->logger->info($msg); - $self->flash( msg_type => 'info', msg => $msg ); - $self->redirect_to( $self->get_referrer() ); + $self->flash(msg_type => 'info', msg => $msg); + $self->redirect_to($self->get_referrer()); } #################################################################################### sub mark_all_to_regenerate { @@ -835,14 +801,12 @@ sub mark_all_to_regenerate { $self->app->repo->entries_save(@entries); my $msg - = "" - . scalar(@entries) - . " entries have been MARKED for regeneration. "; + = "" . scalar(@entries) . " entries have been MARKED for regeneration. "; $msg .= "Now you may run 'regenerate all' or 'regenerate in chunks'. "; $msg .= "Regenration in chunks is useful for large set of entries. "; $self->app->logger->info($msg); - $self->flash( msg_type => 'info', msg => $msg ); - $self->redirect_to( $self->get_referrer() ); + $self->flash(msg_type => 'info', msg => $msg); + $self->redirect_to($self->get_referrer()); } @@ -852,32 +816,28 @@ sub regenerate_html { my $converter = $self->app->bibtexConverter; my $id = $self->param('id'); + my $entry = $self->app->repo->entries_find(sub { $_->id == $id }); - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); - - if ( !defined $entry ) { - $self->flash( - msg => "There is no entry with id $id", - msg_type => 'danger' - ); - $self->redirect_to( $self->get_referrer ); + if (!defined $entry) { + $self->flash(msg => "There is no entry with id $id", msg_type => 'danger'); + $self->redirect_to($self->get_referrer); return; } my @entries = ($entry); my $num_regen - = Fregenerate_html_for_array( $self->app, 1, $converter, \@entries ); + = Fregenerate_html_for_array($self->app, 1, $converter, \@entries); my $msg; - if ( $num_regen == 1 ) { + if ($num_regen == 1) { $msg = "$num_regen entry has been regenerated."; } else { $msg = "$num_regen entries have been regenerated."; } $self->app->logger->info($msg); - $self->flash( msg_type => 'info', msg => $msg ); + $self->flash(msg_type => 'info', msg => $msg); - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } #################################################################################### @@ -885,16 +845,13 @@ sub delete_sure { my $self = shift; my $id = $self->param('id'); - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); + my $entry = $self->app->repo->entries_find(sub { $_->id == $id }); - if ( !defined $entry ) { + if (!defined $entry) { $self->app->logger->warn( "Entry '$id' does not exist and thus can't be deleted."); - $self->flash( - mgs_type => 'danger', - msg => "There is no entry with id $id" - ); - $self->redirect_to( $self->get_referrer ); + $self->flash(mgs_type => 'danger', msg => "There is no entry with id $id"); + $self->redirect_to($self->get_referrer); return; } @@ -908,7 +865,7 @@ sub delete_sure { $self->app->repo->entries_delete($entry); $self->app->logger->info("Entry '$id' has been deleted."); - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } #################################################################################### sub show_authors_of_entry { @@ -916,21 +873,19 @@ sub show_authors_of_entry { my $id = $self->param('id'); $self->app->logger->info("Showing authors of entry id $id"); + my $entry = $self->app->repo->entries_find(sub { $_->{id} == $id }); - my $entry = $self->app->repo->entries_find( sub { $_->{id} == $id } ); - - if ( !defined $entry ) { - $self->flash( msg => "There is no entry with id $id" ); - $self->redirect_to( $self->get_referrer ); + if (!defined $entry) { + $self->flash(msg => "There is no entry with id $id"); + $self->redirect_to($self->get_referrer); return; } - my @authors = map { $_->author } $entry->authorships_all; my @teams = $entry->get_teams; - $self->stash( entry => $entry, authors => \@authors, teams => \@teams ); - $self->render( template => 'publications/show_authors' ); + $self->stash(entry => $entry, authors => \@authors, teams => \@teams); + $self->render(template => 'publications/show_authors'); } #################################################################################### #################################################################################### @@ -941,22 +896,19 @@ sub manage_tags { $self->app->logger->info("Manage tags of entry id $id"); + my $entry = $self->app->repo->entries_find(sub { $_->{id} == $id }); - my $entry = $self->app->repo->entries_find( sub { $_->{id} == $id } ); - - if ( !defined $entry ) { - $self->flash( msg => "There is no entry with id $id" ); - $self->redirect_to( $self->get_referrer ); + if (!defined $entry) { + $self->flash(msg => "There is no entry with id $id"); + $self->redirect_to($self->get_referrer); return; } - my @tags = $entry->get_tags; my @tag_types = $self->app->repo->tagTypes_all; - - $self->stash( entry => $entry, tags => \@tags, tag_types => \@tag_types ); - $self->render( template => 'publications/manage_tags' ); + $self->stash(entry => $entry, tags => \@tags, tag_types => \@tag_types); + $self->render(template => 'publications/manage_tags'); } #################################################################################### @@ -965,10 +917,10 @@ sub remove_tag { my $entry_id = $self->param('eid'); my $tag_id = $self->param('tid'); - my $entry = $self->app->repo->entries_find( sub { $_->id == $entry_id } ); - my $tag = $self->app->repo->tags_find( sub { $_->id == $tag_id } ); + my $entry = $self->app->repo->entries_find(sub { $_->id == $entry_id }); + my $tag = $self->app->repo->tags_find(sub { $_->id == $tag_id }); - if ( defined $entry and defined $tag ) { + if (defined $entry and defined $tag) { my $search_label = Labeling->new( entry => $entry, @@ -978,8 +930,7 @@ sub remove_tag { ); my $label - = $self->app->repo->labelings_find( sub { $_->equals($search_label) } - ); + = $self->app->repo->labelings_find(sub { $_->equals($search_label) }); if ($label) { @@ -988,23 +939,21 @@ sub remove_tag { $tag->remove_labeling($label); $self->app->repo->labelings_delete($label); - $self->app->logger->info( - "Removed tag " . $tag->name . " from entry ID " . $entry->id . ". " ); + "Removed tag " . $tag->name . " from entry ID " . $entry->id . ". "); } else { # this paper does not have this tag - do nothing - $self->app->logger->warn( "Cannot remove tag " - . $tag->name - . " from entry ID " - . $entry->id - . " - reason: labeling not found. " ); + $self->app->logger->warn("Cannot remove tag " + . $tag->name + . " from entry ID " + . $entry->id + . " - reason: labeling not found. "); } } - - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } #################################################################################### @@ -1013,10 +962,10 @@ sub add_tag { my $entry_id = $self->param('eid'); my $tag_id = $self->param('tid'); - my $entry = $self->app->repo->entries_find( sub { $_->id == $entry_id } ); - my $tag = $self->app->repo->tags_find( sub { $_->id == $tag_id } ); + my $entry = $self->app->repo->entries_find(sub { $_->id == $entry_id }); + my $tag = $self->app->repo->tags_find(sub { $_->id == $tag_id }); - if ( defined $entry and defined $tag ) { + if (defined $entry and defined $tag) { my $label = Labeling->new( entry => $entry, tag => $tag, @@ -1036,7 +985,7 @@ sub add_tag { ); } - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } #################################################################################### #################################################################################### @@ -1045,16 +994,14 @@ sub manage_exceptions { my $self = shift; my $id = $self->param('id'); + my $entry = $self->app->repo->entries_find(sub { $_->{id} == $id }); - my $entry = $self->app->repo->entries_find( sub { $_->{id} == $id } ); - - if ( !defined $entry ) { - $self->flash( msg => "There is no entry with id $id" ); - $self->redirect_to( $self->get_referrer ); + if (!defined $entry) { + $self->flash(msg => "There is no entry with id $id"); + $self->redirect_to($self->get_referrer); return; } - my @exceptions = $entry->exceptions_all; my @all_teams = $self->app->repo->teams_all; my @teams = $entry->get_teams; @@ -1062,8 +1009,7 @@ sub manage_exceptions { # cannot use objects as keysdue to stringification! my %exceptions_hash = map { $_->team->id => 1 } @exceptions; - my @unassigned_teams = grep { not $exceptions_hash{ $_->id } } @all_teams; - + my @unassigned_teams = grep { not $exceptions_hash{$_->id} } @all_teams; $self->stash( entry => $entry, @@ -1073,7 +1019,7 @@ sub manage_exceptions { authors => \@authors, unassigned_teams => \@unassigned_teams ); - $self->render( template => 'publications/manage_exceptions' ); + $self->render(template => 'publications/manage_exceptions'); } #################################################################################### sub add_exception { @@ -1081,12 +1027,11 @@ sub add_exception { my $entry_id = $self->param('eid'); my $team_id = $self->param('tid'); - my $msg; - my $entry = $self->app->repo->entries_find( sub { $_->id == $entry_id } ); - my $team = $self->app->repo->teams_find( sub { $_->id == $team_id } ); + my $entry = $self->app->repo->entries_find(sub { $_->id == $entry_id }); + my $team = $self->app->repo->teams_find(sub { $_->id == $team_id }); - if ( defined $entry and defined $team ) { + if (defined $entry and defined $team) { my $exception = Exception->new( entry => $entry, @@ -1100,22 +1045,22 @@ sub add_exception { $self->app->repo->exceptions_save($exception); $msg - = "Exception added! Entry ID " - . $entry->id - . " will be now listed under team '" - . $team->name . "'."; + = "Exception added! Entry ID " + . $entry->id + . " will be now listed under team '" + . $team->name . "'."; } else { $msg - = "Cannot find entry or team to create exception. Searched team ID: " - . $team_id - . " entry ID: " - . $entry_id . "."; + = "Cannot find entry or team to create exception. Searched team ID: " + . $team_id + . " entry ID: " + . $entry_id . "."; } - $self->flash( msg => $msg ); + $self->flash(msg => $msg); $self->app->logger->info($msg); - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } #################################################################################### @@ -1125,13 +1070,12 @@ sub remove_exception { my $entry_id = $self->param('eid'); my $team_id = $self->param('tid'); - - my $entry = $self->app->repo->entries_find( sub { $_->id == $entry_id } ); - my $team = $self->app->repo->teams_find( sub { $_->id == $team_id } ); + my $entry = $self->app->repo->entries_find(sub { $_->id == $entry_id }); + my $team = $self->app->repo->teams_find(sub { $_->id == $team_id }); my $msg; - if ( defined $entry and defined $team ) { + if (defined $entry and defined $team) { my $ex = Exception->new( team_id => $team_id, @@ -1140,40 +1084,39 @@ sub remove_exception { entry => $entry ); - my $exception - = $self->app->repo->exceptions_find( sub { $_->equals($ex) } ); + my $exception = $self->app->repo->exceptions_find(sub { $_->equals($ex) }); - if ( defined $exception ) { + if (defined $exception) { $entry->remove_exception($exception); $team->remove_exception($exception); $self->app->repo->exceptions_delete($exception); $msg - = "Removed exception team '" - . $team->name - . "' from entry ID " - . $entry->id . ". "; + = "Removed exception team '" + . $team->name + . "' from entry ID " + . $entry->id . ". "; } else { $msg - = "Cannot find exception to remove. Searched team '" - . $team->name - . "' entry ID: " - . $entry->id . "."; + = "Cannot find exception to remove. Searched team '" + . $team->name + . "' entry ID: " + . $entry->id . "."; } } else { $msg - = "Cannot find exception to remove. Searched team ID: " - . $team_id - . " entry ID: " - . $entry_id . "."; + = "Cannot find exception to remove. Searched team ID: " + . $team_id + . " entry ID: " + . $entry_id . "."; } - $self->flash( msg => $msg ); + $self->flash(msg => $msg); $self->app->logger->info($msg); - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } #################################################################################### @@ -1191,43 +1134,42 @@ sub get_adding_editing_message_for_error_code { # 2 The proposed key is OK. # 3 Proposed key exists already - HTML message - if ( $exit_code eq 'ERR_BIBTEX' ) { + if ($exit_code eq 'ERR_BIBTEX') { return "You have bibtex errors! No changes were written to the database."; } - elsif ( $exit_code eq 'PREVIEW' ) { + elsif ($exit_code eq 'PREVIEW') { return 'Displaying preview. No changes were written to the database.'; } - elsif ( $exit_code eq 'ADD_OK' ) { + elsif ($exit_code eq 'ADD_OK') { return 'Entry added successfully. Switched to editing mode.'; } - elsif ( $exit_code eq 'EDIT_OK' ) { + elsif ($exit_code eq 'EDIT_OK') { return 'Entry updated successfully.'; } - elsif ( $exit_code eq 'KEY_OK' ) { + elsif ($exit_code eq 'KEY_OK') { return - 'The proposed key is OK. You may continue with your edits. No changes were written to the database.'; + 'The proposed key is OK. You may continue with your edits. No changes were written to the database.'; } - elsif ( $exit_code eq 'KEY_TAKEN' ) { + elsif ($exit_code eq 'KEY_TAKEN') { return - 'The proposed key exists already in DB under ID . + 'The proposed key exists already in DB under ID .
Show me the existing entry ID ' - . $existing_id - . ' in a new window + . $self->url_for('edit_publication', id => $existing_id) + . '" target="_blank">Show me the existing entry ID ' + . $existing_id + . ' in a new window
Entry has not been saved. Please pick another BibTeX key. No changes were written to the database.'; } - elsif ( defined $exit_code and $exit_code ne '' ) { + elsif (defined $exit_code and $exit_code ne '') { return "Unknown exit code: $exit_code"; } } - #################################################################################### sub publications_add_get { my $self = shift; @@ -1241,17 +1183,17 @@ sub publications_add_get { publisher = {Printer-at-home publishing}, title = {{Selected aspects of some methods}}, year = {' . get_current_year() . '}, - month = {' . $mons{ get_current_month() } . '}, + month = {' . $mons{get_current_month()} . '}, day = {1--31}, }'; - my $e_dummy = $self->app->entityFactory->new_Entry( bib => $bib ); + my $e_dummy = $self->app->entityFactory->new_Entry(bib => $bib); $e_dummy->populate_from_bib(); - $e_dummy->generate_html( $self->app->bst, $self->app->bibtexConverter ); + $e_dummy->generate_html($self->app->bst, $self->app->bibtexConverter); - $self->stash( entry => $e_dummy, msg => $msg ); - $self->render( template => 'publications/add_entry' ); + $self->stash(entry => $e_dummy, msg => $msg); + $self->render(template => 'publications/add_entry'); } #################################################################################### sub publications_add_post { @@ -1283,97 +1225,88 @@ sub publications_add_post { # 2 => KEY_OK # 3 => KEY_TAKEN - - my $entry = $self->app->entityFactory->new_Entry( bib => $new_bib ); + my $entry = $self->app->entityFactory->new_Entry(bib => $new_bib); # any action - if ( !$entry->has_valid_bibtex ) { + if (!$entry->has_valid_bibtex) { $status_code_str = 'ERR_BIBTEX'; - my $msg - = get_adding_editing_message_for_error_code( $self, $status_code_str, - $existing_id ); + my $msg = get_adding_editing_message_for_error_code($self, $status_code_str, + $existing_id); my $msg_type = 'danger'; $self->app->logger->info( "Adding publication. Action: > $action <. Status code: $status_code_str." ); - $self->stash( entry => $entry, msg => $msg, msg_type => $msg_type ); - $self->render( template => 'publications/add_entry' ); + $self->stash(entry => $entry, msg => $msg, msg_type => $msg_type); + $self->render(template => 'publications/add_entry'); return; } - $entry->generate_html( $self->app->bst, $self->app->bibtexConverter ); - my $bibtex_warnings = FprintBibtexWarnings( $entry->warnings ); + $entry->generate_html($self->app->bst, $self->app->bibtexConverter); + my $bibtex_warnings = FprintBibtexWarnings($entry->warnings); # any action my $existing_entry = $self->app->repo->entries_find( - sub { $_->bibtex_key eq $entry->bibtex_key } ); + sub { $_->bibtex_key eq $entry->bibtex_key }); if ($existing_entry) { $status_code_str = 'KEY_TAKEN'; my $msg_type = 'danger'; $existing_id = $existing_entry->id; - my $msg - = get_adding_editing_message_for_error_code( $self, $status_code_str, - $existing_id ); + my $msg = get_adding_editing_message_for_error_code($self, $status_code_str, + $existing_id); $self->app->logger->info( "Adding publication. Action: > $action <. Status code: $status_code_str." ); - $self->stash( entry => $entry, msg => $msg, msg_type => $msg_type ); - $self->render( template => 'publications/add_entry' ); + $self->stash(entry => $entry, msg => $msg, msg_type => $msg_type); + $self->render(template => 'publications/add_entry'); return; } - - if ( $action eq 'preview' or $action eq 'check_key' ) { + if ($action eq 'preview' or $action eq 'check_key') { my $status_code_str = 'PREVIEW'; my $msg_type = 'info'; $msg_type = 'warning' if $bibtex_warnings; - my $msg - = get_adding_editing_message_for_error_code( $self, $status_code_str, - $existing_id ); + my $msg = get_adding_editing_message_for_error_code($self, $status_code_str, + $existing_id); $msg .= $bibtex_warnings; $self->app->logger->info( "Adding publication. Action: > $action <. Status code: $status_code_str." ); - $self->stash( entry => $entry, msg => $msg, msg_type => $msg_type ); - $self->render( template => 'publications/add_entry' ); + $self->stash(entry => $entry, msg => $msg, msg_type => $msg_type); + $self->render(template => 'publications/add_entry'); return; } - - if ( $action eq 'save' ) { + if ($action eq 'save') { $status_code_str = 'ADD_OK'; $entry->fix_month(); - $entry->generate_html( $self->app->bst, $self->app->bibtexConverter ); + $entry->generate_html($self->app->bst, $self->app->bibtexConverter); $self->app->repo->entries_save($entry); $added_under_id = $entry->id; ## !!! the entry must be added before executing Freassign_authors_to_entries_given_by_array ## why? beacuse authorship will be unable to map existing entry to the author - Freassign_authors_to_entries_given_by_array( $self->app, 1, [$entry] ); - + Freassign_authors_to_entries_given_by_array($self->app, 1, [$entry]); my $msg_type = 'success'; $msg_type = 'warning' if $bibtex_warnings; - my $msg - = get_adding_editing_message_for_error_code( $self, $status_code_str, - $existing_id ); + my $msg = get_adding_editing_message_for_error_code($self, $status_code_str, + $existing_id); $msg .= $bibtex_warnings; $self->app->logger->info( "Adding publication. Action: > $action <. Status code: $status_code_str." ); - $self->flash( msg => $msg, msg_type => $msg_type ); + $self->flash(msg => $msg, msg_type => $msg_type); $self->redirect_to( - $self->url_for( 'edit_publication', id => $added_under_id ) ); + $self->url_for('edit_publication', id => $added_under_id)); return; } - } #################################################################################### sub publications_edit_get { @@ -1382,18 +1315,18 @@ sub publications_edit_get { $self->app->logger->info("Editing publication entry id $id"); - my $entry = $self->app->repo->entries_find( sub { $_->id == $id } ); + my $entry = $self->app->repo->entries_find(sub { $_->id == $id }); - if ( !defined $entry ) { - $self->flash( msg => "There is no entry with id $id" ); - $self->redirect_to( $self->get_referrer ); + if (!defined $entry) { + $self->flash(msg => "There is no entry with id $id"); + $self->redirect_to($self->get_referrer); return; } $entry->populate_from_bib(); - $entry->generate_html( $self->app->bst, $self->app->bibtexConverter ); + $entry->generate_html($self->app->bst, $self->app->bibtexConverter); - $self->stash( entry => $entry ); - $self->render( template => 'publications/edit_entry' ); + $self->stash(entry => $entry); + $self->render(template => 'publications/edit_entry'); } #################################################################################### sub publications_edit_post { @@ -1405,22 +1338,20 @@ sub publications_edit_post { my $param_check_key = $self->param('check_key'); my $action = 'save'; # user clicks save - $action = 'preview' if $self->param('preview'); # user clicks preview - $action = 'check_key' if $self->param('check_key'); # user clicks check key + $action = 'preview' if $self->param('preview'); # user clicks preview + $action = 'check_key' if $self->param('check_key'); # user clicks check key - $self->app->logger->info( - "Editing publication id $id. Action: > $action <."); + $self->app->logger->info("Editing publication id $id. Action: > $action <."); $new_bib =~ s/^\s+|\s+$//g; $new_bib =~ s/^\t//g; - - my ( $mentry, $status_code_str, $existing_id, $added_under_id ) - = Fhandle_add_edit_publication( $self->app, $new_bib, $id, $action, - $self->app->bst ); + my ($mentry, $status_code_str, $existing_id, $added_under_id) + = Fhandle_add_edit_publication($self->app, $new_bib, $id, $action, + $self->app->bst); my $adding_msg - = get_adding_editing_message_for_error_code( $self, $status_code_str, - $existing_id ); + = get_adding_editing_message_for_error_code($self, $status_code_str, + $existing_id); $self->app->logger->info( "Editing publication id $id. Action: > $action <. Status code: $status_code_str." @@ -1434,17 +1365,17 @@ sub publications_edit_post { # 2 => KEY_OK # 3 => KEY_TAKEN - my $bibtex_warnings = FprintBibtexWarnings( $mentry->warnings ); + my $bibtex_warnings = FprintBibtexWarnings($mentry->warnings); my $msg = $adding_msg . $bibtex_warnings; my $msg_type = 'success'; $msg_type = 'warning' if $bibtex_warnings =~ m/Warning/; $msg_type = 'danger' - if $status_code_str eq 'ERR_BIBTEX' - or $status_code_str eq 'KEY_TAKEN' - or $bibtex_warnings =~ m/Error/; + if $status_code_str eq 'ERR_BIBTEX' + or $status_code_str eq 'KEY_TAKEN' + or $bibtex_warnings =~ m/Error/; - $self->stash( entry => $mentry, msg => $msg, msg_type => $msg_type ); - $self->render( template => 'publications/edit_entry' ); + $self->stash(entry => $mentry, msg => $msg, msg_type => $msg_type); + $self->render(template => 'publications/edit_entry'); } #################################################################################### sub clean_ugly_bibtex { @@ -1452,24 +1383,24 @@ sub clean_ugly_bibtex { # TODO: put this into config or preferences! my @fields_to_clean - = qw(bdsk-url-1 bdsk-url-2 bdsk-url-3 date-added date-modified owner tags); + = qw(bdsk-url-1 bdsk-url-2 bdsk-url-3 date-added date-modified owner tags); $self->app->logger->info("Cleaning ugly Bibtex fields for all entries"); my @entries = $self->app->repo->entries_all; my $num_removed = 0; foreach my $entry (@entries) { - $num_removed = $num_removed - + $entry->clean_ugly_bibtex_fields( \@fields_to_clean ); + $num_removed + = $num_removed + $entry->clean_ugly_bibtex_fields(\@fields_to_clean); } $self->flash( msg_type => 'info', msg => - "All entries have now their Bibtex cleaned. I have removed $num_removed fields." + "All entries have now their Bibtex cleaned. I have removed $num_removed fields." ); - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } #################################################################################### 1; diff --git a/lib/BibSpace/Controller/PublicationsExperimental.pm b/lib/BibSpace/Controller/PublicationsExperimental.pm index c4ee017..98d2fc7 100644 --- a/lib/BibSpace/Controller/PublicationsExperimental.pm +++ b/lib/BibSpace/Controller/PublicationsExperimental.pm @@ -19,69 +19,64 @@ use Encode; use BibSpace::Functions::Core; use BibSpace::Functions::FPublications; - use Mojo::Base 'Mojolicious::Controller'; use Mojo::Base 'Mojolicious::Plugin::Config'; use Mojo::UserAgent; use Mojo::Log; our %mons = ( - 1 => 'January', - 2 => 'February', - 3 => 'March', - 4 => 'April', - 5 => 'May', - 6 => 'June', - 7 => 'July', - 8 => 'August', - 9 => 'September', - 10 => 'October', - 11 => 'November', - 12 => 'December' + 1 => 'January', + 2 => 'February', + 3 => 'March', + 4 => 'April', + 5 => 'May', + 6 => 'June', + 7 => 'July', + 8 => 'August', + 9 => 'September', + 10 => 'October', + 11 => 'November', + 12 => 'December' ); #################################################################################### ## ADD form sub publications_add_many_get { - my $self = shift; + my $self = shift; - my $bib1 - = '@article{key-ENTRY1-' - . get_current_year() . ', + my $bib1 = '@article{key-ENTRY1-' . get_current_year() . ', author = {Johny Example}, title = {{Selected aspects of some methods ' . random_string(8) . '}}, journal = {Journal of this and that}, publisher = {Printer-at-home publishing}, year = {' . get_current_year() . '}, - month = {' . $mons{ 12 } . '}, + month = {' . $mons{12} . '}, day = {1--31}, }'; - my $bib2 - = '@article{key-ENTRY2-' - . get_current_year() . ', + my $bib2 = '@article{key-ENTRY2-' . get_current_year() . ', author = {Johny Example}, title = {{Selected aspects of some methods ' . random_string(8) . '}}, journal = {Journal of other things}, publisher = {Copy-machine publishing house}, year = {' . get_current_year() . '}, - month = {' . $mons{ 12 } . '}, + month = {' . $mons{12} . '}, day = {1--31}, }'; - my $bib = $bib1 . "\n\n" . $bib2; + my $bib = $bib1 . "\n\n" . $bib2; - my $msg - = "Adding multiple publications at once is experimental!
Adding mode You operate on an unsaved entry!"; + my $msg + = "Adding multiple publications at once is experimental!
Adding mode You operate on an unsaved entry!"; - $self->stash( - bib => $bib, - key => '', - existing_id => '', - exit_code => '', - preview => '' - ); - $self->stash( msg_type=>'warning', msg => $msg); - $self->render( template => 'publications/add_multiple_entries' ); + $self->stash( + bib => $bib, + key => '', + existing_id => '', + exit_code => '', + preview => '' + ); + $self->stash(msg_type => 'warning', msg => $msg); + $self->render(template => 'publications/add_multiple_entries'); } ############################################################################################################ @@ -90,182 +85,172 @@ sub publications_add_many_get { sub publications_add_many_post { - my $self = shift; - my $id = $self->param('id') // undef; - my $new_bib = $self->param('new_bib'); - my $preview_param = $self->param('preview') // undef; - my $save_param = $self->param('save') // undef; + my $self = shift; + my $id = $self->param('id') // undef; + my $new_bib = $self->param('new_bib'); + my $preview_param = $self->param('preview') // undef; + my $save_param = $self->param('save') // undef; - # my $check_key = || undef; - my $preview = 0; - my $msg = "Adding mode You operate on an unsaved entry!
"; + # my $check_key = || undef; + my $preview = 0; + my $msg = "Adding mode You operate on an unsaved entry!
"; - $self->app->logger->info("Adding multiple publications"); + $self->app->logger->info("Adding multiple publications"); - $self->app->logger->debug("Adding multiple publications with bib $new_bib"); + $self->app->logger->debug("Adding multiple publications with bib $new_bib"); - my $debug_str = ""; + my $debug_str = ""; - my $html_preview = ""; - my $code = -2; + my $html_preview = ""; + my $code = -2; - my @bibtex_codes = split_bibtex_entries($new_bib); + my @bibtex_codes = split_bibtex_entries($new_bib); + # status_code_strings + # -2 => PREVIEW + # -1 => ERR_BIBTEX + # 0 => ADD_OK + # 1 => EDIT_OK + # 2 => KEY_OK + # 3 => KEY_TAKEN + my $num_errors = 0; + for my $bibtex_code (@bibtex_codes) { - # status_code_strings - # -2 => PREVIEW - # -1 => ERR_BIBTEX - # 0 => ADD_OK - # 1 => EDIT_OK - # 2 => KEY_OK - # 3 => KEY_TAKEN - my $num_errors = 0; - for my $bibtex_code (@bibtex_codes) { + my ($mentry, $status_code_str, $existing_id, $added_under_id) + = Fhandle_add_edit_publication($self->app, $bibtex_code, -1, 'preview'); - my ( $mentry, $status_code_str, $existing_id, $added_under_id ) - = Fhandle_add_edit_publication( $self->app, $bibtex_code, -1, - 'preview' ); - - if ( $status_code_str eq 'ERR_BIBTEX' ) { - $debug_str - .= "BIBTEX error in
 $bibtex_code 
"; - $num_errors++; - } - elsif ( $status_code_str eq 'KEY_TAKEN' ) { # => bibtex OK, key OK - $debug_str - .= "KEY_TAKEN error in
 $bibtex_code 
"; - $num_errors++; - } - else{ - $debug_str - .= "$status_code_str for
 $bibtex_code 
"; - } + if ($status_code_str eq 'ERR_BIBTEX') { + $debug_str .= "BIBTEX error in
 $bibtex_code 
"; + $num_errors++; } - - if ( $num_errors > 0 ) { - $msg = "$num_errors have errors. Please correct entries before continuing. No changes were written to database.
$debug_str"; - $self->stash( - bib => $new_bib, - existing_id => 0, - key => '', - msg_type => 'danger', - msg => $msg, - exit_code => $code, - preview => $html_preview - ); - $self->render( template => 'publications/add_multiple_entries' ); - return; + elsif ($status_code_str eq 'KEY_TAKEN') { # => bibtex OK, key OK + $debug_str .= "KEY_TAKEN error in
 $bibtex_code 
"; + $num_errors++; } - if ( defined $preview_param ) { - $msg = "Check ready.
".$debug_str; - $self->stash( - bib => $new_bib, - existing_id => 0, - key => '', - msg_type => 'info', - msg => $msg, - exit_code => $code, - preview => $html_preview - ); - $self->render( template => 'publications/add_multiple_entries' ); - return; + else { + $debug_str .= "$status_code_str for
 $bibtex_code 
"; } + } - # here all Bibtex entries are OK - - my @key_arr = (); - - for my $bibtex_code (@bibtex_codes) { - - # $debug_str.="
Found code!"; - my $entry = $self->app->entityFactory->new_Entry( bib=>$bibtex_code ); - $entry->populate_from_bib; - $debug_str .= "
Found key: ".$entry->{bibtex_key}; + if ($num_errors > 0) { + $msg + = "$num_errors have errors. Please correct entries before continuing. No changes were written to database.
$debug_str"; + $self->stash( + bib => $new_bib, + existing_id => 0, + key => '', + msg_type => 'danger', + msg => $msg, + exit_code => $code, + preview => $html_preview + ); + $self->render(template => 'publications/add_multiple_entries'); + return; + } + if (defined $preview_param) { + $msg = "Check ready.
" . $debug_str; + $self->stash( + bib => $new_bib, + existing_id => 0, + key => '', + msg_type => 'info', + msg => $msg, + exit_code => $code, + preview => $html_preview + ); + $self->render(template => 'publications/add_multiple_entries'); + return; + } - push @key_arr, $entry->{bibtex_key}; - } + # here all Bibtex entries are OK - my @mentries = (); + my @key_arr = (); - my %seen; - my $are_unique = 0; + for my $bibtex_code (@bibtex_codes) { - # if size of arr is equal to size of uniq arr - $are_unique = 1 if uniq(@key_arr) == @key_arr; + # $debug_str.="
Found code!"; + my $entry = $self->app->entityFactory->new_Entry(bib => $bibtex_code); + $entry->populate_from_bib; + $debug_str .= "
Found key: " . $entry->{bibtex_key}; - # count how many times a given key appears - foreach my $value (@key_arr) { - $seen{$value}++; - } + push @key_arr, $entry->{bibtex_key}; + } - $debug_str .= "
Checking if input keys are unique: "; - $debug_str .= "Yes!" if $are_unique; - $debug_str .= "No! " unless $are_unique; - - if ( $are_unique == 0 ) { # if the array is not empty - $debug_str .= "
" - . "Some bibtex keys in the input are not unique. Please correct the input."; - foreach my $key ( keys %seen ) { - $debug_str - .= "
" - . "Bibtex key: $key exists " - . $seen{$key} - . " times!" - if $seen{$key} > 1; - } - $msg = $debug_str - . "Please correct entries before continuing. No changes were written to database."; - $self->stash( - bib => $new_bib, - existing_id => 0, - key => '', - msg_type => 'danger', - msg => $msg, - exit_code => $code, - preview => $html_preview - ); - $self->render( template => 'publications/add_multiple_entries' ); - return; - } + my @mentries = (); - my $msg_type = 'warning'; + my %seen; + my $are_unique = 0; - if( defined $save_param ){ - $debug_str .= "
Entries ready to add! Starting."; + # if size of arr is equal to size of uniq arr + $are_unique = 1 if uniq(@key_arr) == @key_arr; - $msg_type = 'success'; + # count how many times a given key appears + foreach my $value (@key_arr) { + $seen{$value}++; + } - for my $bibtex_code (@bibtex_codes) { - my ( $mentry, $status_code_str, $existing_id, $added_under_id ) - = Fhandle_add_edit_publication( $self->app, $bibtex_code, -1, 'save', - $self->app->bst ); + $debug_str .= "
Checking if input keys are unique: "; + $debug_str .= "Yes!" if $are_unique; + $debug_str .= "No! " unless $are_unique; - if ( $status_code_str eq 'ADD_OK' ) { - $debug_str .= "
" - . "Added key entry as id $added_under_id successfully!"; - } - else { # => bibtex OK, key OK - $debug_str .= "
" - . "Something went wrong. Status: $status_code_str
"; - $msg_type = 'danger'; - } - } + if ($are_unique == 0) { # if the array is not empty + $debug_str .= "
" + . "Some bibtex keys in the input are not unique. Please correct the input."; + foreach my $key (keys %seen) { + $debug_str + .= "
" . "Bibtex key: $key exists " . $seen{$key} . " times!" + if $seen{$key} > 1; } + $msg = $debug_str + . "Please correct entries before continuing. No changes were written to database."; + $self->stash( + bib => $new_bib, + existing_id => 0, + key => '', + msg_type => 'danger', + msg => $msg, + exit_code => $code, + preview => $html_preview + ); + $self->render(template => 'publications/add_multiple_entries'); + return; + } + my $msg_type = 'warning'; + if (defined $save_param) { + $debug_str .= "
Entries ready to add! Starting."; - $self->stash( - bib => $new_bib, - existing_id => 0, - msg_type => $msg_type, - key => '', - msg => $msg . $debug_str, - exit_code => $code, - preview => $html_preview - ); - $self->render( template => 'publications/add_multiple_entries' ); + $msg_type = 'success'; + + for my $bibtex_code (@bibtex_codes) { + my ($mentry, $status_code_str, $existing_id, $added_under_id) + = Fhandle_add_edit_publication($self->app, $bibtex_code, -1, 'save', + $self->app->bst); + + if ($status_code_str eq 'ADD_OK') { + $debug_str + .= "
" . "Added key entry as id $added_under_id successfully!"; + } + else { # => bibtex OK, key OK + $debug_str + .= "
" . "Something went wrong. Status: $status_code_str
"; + $msg_type = 'danger'; + } + } + } + + $self->stash( + bib => $new_bib, + existing_id => 0, + msg_type => $msg_type, + key => '', + msg => $msg . $debug_str, + exit_code => $code, + preview => $html_preview + ); + $self->render(template => 'publications/add_multiple_entries'); } #################################################################################### - 1; diff --git a/lib/BibSpace/Controller/PublicationsLanding.pm b/lib/BibSpace/Controller/PublicationsLanding.pm index 224eaab..060051d 100644 --- a/lib/BibSpace/Controller/PublicationsLanding.pm +++ b/lib/BibSpace/Controller/PublicationsLanding.pm @@ -12,7 +12,6 @@ use v5.16; #because of ~~ use strict; use warnings; - use TeX::Encode; use Encode; @@ -25,21 +24,20 @@ use Mojo::UserAgent; use Mojo::Log; our %mons = ( - 1 => 'January', - 2 => 'February', - 3 => 'March', - 4 => 'April', - 5 => 'May', - 6 => 'June', - 7 => 'July', - 8 => 'August', - 9 => 'September', - 10 => 'October', - 11 => 'November', - 12 => 'December' + 1 => 'January', + 2 => 'February', + 3 => 'March', + 4 => 'April', + 5 => 'May', + 6 => 'June', + 7 => 'July', + 8 => 'August', + 9 => 'September', + 10 => 'October', + 11 => 'November', + 12 => 'December' ); - =item NAMING CONVENTION Peer-Reviewed Journal and Magazine Articles <--- this is section (the text is section description) @@ -56,523 +54,592 @@ Peer-Reviewed International Conference, Workshop Papers, and Book Chapters <--- [2] Piotr Rygielski, Viliam Simko, Felix Sittner, Doris Aschenbrenner, Samuel Kounev, and Klaus Schil... =cut -our $text_delimiter_l = ''; -our $text_delimiter_r = ''; +our $text_delimiter_l = ''; +our $text_delimiter_r = ''; our $anchor_delimiter_l = ' '; our $anchor_delimiter_r = ' '; -our $selected_text_delimiter_l = ''; -our $selected_text_delimiter_r = ''; +our $selected_text_delimiter_l = ''; +our $selected_text_delimiter_r = ''; our $selected_anchor_delimiter_l = '['; our $selected_anchor_delimiter_r = ']'; ############################################################################################################ ## Controller function sub landing_types { - my $self = shift; - my $bibtex_type = $self->param('bibtex_type') // undef; - my $entry_type = $self->param('entry_type') // undef; - - my @all_types - = $self->app->repo->types_filter( sub { $_->onLanding == 1 } ); - - # key: our bibtex type - # value: description of our bibtex type - my %hash_our_type_to_description - = map { $_->our_type => $_->description } @all_types; - - my @entries_to_show; - my @section_names = keys %hash_our_type_to_description; - - ########## - ## Step 1: define which sections to show on the landing list and get the entire papers set for this filtering query - ########## - if ($bibtex_type) { - # user wants to filter on bibtex_type => user wants to show only papers - # we assume that talks do not have bibtex_type - they are special - @section_names = ($bibtex_type); + my $self = shift; + my $bibtex_type = $self->param('bibtex_type') // undef; + my $entry_type = $self->param('entry_type') // undef; + + my @all_types = $self->app->repo->types_filter(sub { $_->onLanding == 1 }); + + # key: our bibtex type + # value: description of our bibtex type + my %hash_our_type_to_description + = map { $_->our_type => $_->description } @all_types; + + my @entries_to_show; + my @section_names = keys %hash_our_type_to_description; + + ########## + ## Step 1: define which sections to show on the landing list and get the entire papers set for this filtering query + ########## + if ($bibtex_type) { + + # user wants to filter on bibtex_type => user wants to show only papers + # we assume that talks do not have bibtex_type - they are special + @section_names = ($bibtex_type); + } + + if ($entry_type and $entry_type eq 'talk') { + + # user wants to show only talks + + # this needs to be added manually as talks are special + $hash_our_type_to_description{'talk'} = "Talks"; + @section_names = ('talk'); + @entries_to_show = $self->get_talks_for_landing; + } + elsif ($entry_type and $entry_type eq 'paper') { + + # user wants to show only papers + @entries_to_show = $self->get_papers_for_landing; + } + else { + # user wants to show everything = talks and papers + + # this needs to be added manually as talks are special + $hash_our_type_to_description{'talk'} = "Talks"; + push @section_names, 'talk'; + @entries_to_show = $self->get_entries_for_landing; + } + + ########## + ## Step 2: set default section descriptions if needed + ########## + ## issue default description if there is no custom description in the system + foreach my $section_name (sort reverse @section_names) { + if (!exists($hash_our_type_to_description{$section_name})) { + $hash_our_type_to_description{$section_name} + = get_generic_type_description($section_name); } + } - if ( $entry_type and $entry_type eq 'talk' ) { + # key: our bibtex type + # value: ref to array of entry objects + my %hash_our_type_to_entries; - # user wants to show only talks + ########## + ## Step 3: assign papers to given sections + ########## - # this needs to be added manually as talks are special - $hash_our_type_to_description{'talk'} = "Talks"; - @section_names = ('talk'); - @entries_to_show = $self->get_talks_for_landing; - } - elsif ( $entry_type and $entry_type eq 'paper' ) { + my @sections_having_entries; + foreach my $section_name (sort reverse @section_names) { - # user wants to show only papers - @entries_to_show = $self->get_papers_for_landing; + # TODO: refactor into: get_entries_for_section + my @entries_in_section; + if ($section_name eq 'talk') { + @entries_in_section = grep { $_->is_talk } @entries_to_show; } else { - # user wants to show everything = talks and papers - - # this needs to be added manually as talks are special - $hash_our_type_to_description{'talk'} = "Talks"; - push @section_names, 'talk'; - @entries_to_show = $self->get_entries_for_landing; - } - - - - - ########## - ## Step 2: set default section descriptions if needed - ########## - ## issue default description if there is no custom description in the system - foreach my $section_name ( sort reverse @section_names ) { - if ( !exists( $hash_our_type_to_description{$section_name} ) ) { - $hash_our_type_to_description{$section_name} - = get_generic_type_description($section_name); - } + @entries_in_section = grep { + $_->is_paper + and $_->matches_our_type($section_name, $self->app->repo) + } @entries_to_show; } + $hash_our_type_to_entries{$section_name} = \@entries_in_section; - # key: our bibtex type - # value: ref to array of entry objects - my %hash_our_type_to_entries; - - ########## - ## Step 3: assign papers to given sections - ########## - - my @sections_having_entries; - foreach my $section_name ( sort reverse @section_names ) { - - # TODO: refactor into: get_entries_for_section - my @entries_in_section; - if ( $section_name eq 'talk' ) { - @entries_in_section = grep { $_->is_talk } @entries_to_show; - } - else { - @entries_in_section - = grep { $_->is_paper and $_->matches_our_type($section_name, $self->app->repo) } - @entries_to_show; - } - - $hash_our_type_to_entries{$section_name} = \@entries_in_section; - - if ( scalar(@entries_in_section) > 0 ) { - push @sections_having_entries, $section_name; - } + if (scalar(@entries_in_section) > 0) { + push @sections_having_entries, $section_name; } - - - ## hash_our_type_to_entries: our bibtex type string -> ref_arr_entry_objects - ## hash_our_type_to_description: our bibtex type string -> our bibtex type description string - ## sections_having_entries: array of section names that have more than 0 entries - return $self->display_landing( - \%hash_our_type_to_entries, \%hash_our_type_to_description, - \@sections_having_entries, $self->get_switchlink_html('years'), - $self->get_filtering_navbar_html() - ); + } + + ## hash_our_type_to_entries: our bibtex type string -> ref_arr_entry_objects + ## hash_our_type_to_description: our bibtex type string -> our bibtex type description string + ## sections_having_entries: array of section names that have more than 0 entries + return $self->display_landing( + \%hash_our_type_to_entries, \%hash_our_type_to_description, + \@sections_having_entries, $self->get_switchlink_html('years'), + $self->get_filtering_navbar_html() + ); } ############################################################################################################ ## Controller function sub landing_years { - my $self = shift; - my $year = $self->param('year') // undef; - my $author = $self->param('author') // undef; + my $self = shift; + my $year = $self->param('year') // undef; + my $author = $self->param('author') // undef; - # shows talks + papers by default - # my $entry_type = $self->param('entry_type') // undef; + # shows talks + papers by default + # my $entry_type = $self->param('entry_type') // undef; + my $min_year = $self->get_year_of_oldest_entry($author) + // $self->current_year; + my $max_year = $self->current_year; - my $min_year = $self->get_year_of_oldest_entry($author) // $self->current_year; - my $max_year = $self->current_year; + # 8 is a month in which we show publications from the next year + if ($self->current_month > 8) { # TODO export to config. + $max_year++; + } - # 8 is a month in which we show publications from the next year - if ( $self->current_month > 8 ) { # TODO export to config. - $max_year++; - } + if ($year) { + $min_year = $year; + $max_year = $year; + } - if ($year) { - $min_year = $year; - $max_year = $year; - } - - my %hash_year_to_description - = map { $_ => $_ } ( $min_year .. $max_year ); - my %hash_year_to_entries; + my %hash_year_to_description = map { $_ => $_ } ($min_year .. $max_year); + my %hash_year_to_entries; - ## fetch all entries outside of the loop - my @all_entries = $self->get_entries_for_landing; + ## fetch all entries outside of the loop + my @all_entries = $self->get_entries_for_landing; - foreach my $year ( $min_year .. $max_year ) { + foreach my $year ($min_year .. $max_year) { - my @entries_to_show = grep { $_->year == $year } @all_entries; - $hash_year_to_entries{$year} = \@entries_to_show; + my @entries_to_show = grep { $_->year == $year } @all_entries; + $hash_year_to_entries{$year} = \@entries_to_show; - if ( scalar(@entries_to_show) == 0 ) { - delete $hash_year_to_description{$year}; - delete $hash_year_to_entries{$year}; + if (scalar(@entries_to_show) == 0) { + delete $hash_year_to_description{$year}; + delete $hash_year_to_entries{$year}; - } } + } - my @sections_sorted = reverse sort keys %hash_year_to_entries; + my @sections_sorted = reverse sort keys %hash_year_to_entries; - # displaying years - you may switch to types - my $switchlink = $self->get_switchlink_html("types"); - my $navbar_html = $self->get_filtering_navbar_html(); + # displaying years - you may switch to types + my $switchlink = $self->get_switchlink_html("types"); + my $navbar_html = $self->get_filtering_navbar_html(); - return $self->display_landing( \%hash_year_to_entries, - \%hash_year_to_description, \@sections_sorted, $switchlink, - $navbar_html ); + return $self->display_landing(\%hash_year_to_entries, + \%hash_year_to_description, \@sections_sorted, $switchlink, $navbar_html); } ############################################################################################################ sub display_landing { - my $self = shift; - my $hash_our_type_to_entries = shift; - my $hash_our_type_to_description = shift; - my $ordered_section_names_ref = shift; - my $switchlink = shift; - my $navbar_html = shift; - - my $navbar = $self->param('navbar') // 0; - my $show_title = $self->param('title') // 0; - my $show_switch = $self->param('switchlink') // 1; - my $query_permalink = $self->param('permalink'); - my $query_tag_name = $self->param('tag'); - - # reset switchlink if show_switch different to 1 - $switchlink = "" if $show_switch != 1; - $navbar_html = "" if $navbar != 1; - - - my $display_tag_name; - if ( defined $query_permalink ) { - - my $tag_obj_with_permalink = $self->app->repo->tags_find( - sub { - defined $_->permalink and $_->permalink eq $query_permalink; - } - ); - if ( defined $tag_obj_with_permalink ) { - $display_tag_name = $tag_obj_with_permalink->name; - } - else { - $display_tag_name = $query_permalink; - } - } - elsif ( defined $query_tag_name ) { - $display_tag_name = $query_tag_name; + my $self = shift; + my $hash_our_type_to_entries = shift; + my $hash_our_type_to_description = shift; + my $ordered_section_names_ref = shift; + my $switchlink = shift; + my $navbar_html = shift; + + my $navbar = $self->param('navbar') // 0; + my $show_title = $self->param('title') // 0; + my $show_switch = $self->param('switchlink') // 1; + my $query_permalink = $self->param('permalink'); + my $query_tag_name = $self->param('tag'); + + # reset switchlink if show_switch different to 1 + $switchlink = "" if $show_switch != 1; + $navbar_html = "" if $navbar != 1; + + my $display_tag_name; + if (defined $query_permalink) { + + my $tag_obj_with_permalink = $self->app->repo->tags_find( + sub { + defined $_->permalink and $_->permalink eq $query_permalink; + } + ); + if (defined $tag_obj_with_permalink) { + $display_tag_name = $tag_obj_with_permalink->name; } - - if ( defined $display_tag_name - and defined $show_title - and $show_title == 1 ) - { - $display_tag_name =~ s/_+/_/g; - $display_tag_name =~ s/_/\ /g; + else { + $display_tag_name = $query_permalink; } - - - my $title = "Publications and Talks "; - $title = " Publications " - if $self->param('entry_type') - and $self->param('entry_type') eq 'paper'; - $title = " Talks " - if $self->param('entry_type') - and $self->param('entry_type') eq 'talk'; - - - $title .= " of team '" . $self->param('team') . "'" - if $self->param('team'); - $title .= " of author '" . $self->param('author') . "'" - if $self->param('author'); - $title .= " labeled as '" . $display_tag_name . "'" if $display_tag_name; - $title .= " of type '" . $self->param('bibtex_type') . "'" - if $self->param('bibtex_type'); - $title .= " published in year '" . $self->param('year') . "'" - if $self->param('year'); - - # my $url = $self->req->url; - # say "scheme ".$url->scheme; - # say "userinfo ".$url->userinfo; - # say "host ".$url->host; - # say "port ".$url->port; - # say "path ".$url->path; - # say "query ".$url->query; - # say "fragment ".$url->fragment; - - # keys = years - # my @objs = @{ $hash_values{$year} }; - # foreach my $obj (@objs){ - $self->stash( - hash_our_type_to_entries => $hash_our_type_to_entries, - hash_our_type_to_description => $hash_our_type_to_description, - # this defines order of sections - ordered_section_names => $ordered_section_names_ref, - navbar => $navbar_html, - show_title => $show_title, - title => $title, - switch_link => $switchlink - ); - $self->res->headers->header( 'Access-Control-Allow-Origin' => '*' ); - - - my $html - = $self->render_to_string( template => 'publications/landing_obj' ); - $self->render( data => $html ); - - # $self->render( template => 'publications/landing_obj' ); + } + elsif (defined $query_tag_name) { + $display_tag_name = $query_tag_name; + } + + if (defined $display_tag_name and defined $show_title and $show_title == 1) { + $display_tag_name =~ s/_+/_/g; + $display_tag_name =~ s/_/\ /g; + } + + my $title = "Publications and Talks "; + $title = " Publications " + if $self->param('entry_type') + and $self->param('entry_type') eq 'paper'; + $title = " Talks " + if $self->param('entry_type') + and $self->param('entry_type') eq 'talk'; + + $title .= " of team '" . $self->param('team') . "'" if $self->param('team'); + $title .= " of author '" . $self->param('author') . "'" + if $self->param('author'); + $title .= " labeled as '" . $display_tag_name . "'" if $display_tag_name; + $title .= " of type '" . $self->param('bibtex_type') . "'" + if $self->param('bibtex_type'); + $title .= " published in year '" . $self->param('year') . "'" + if $self->param('year'); + + # my $url = $self->req->url; + # say "scheme ".$url->scheme; + # say "userinfo ".$url->userinfo; + # say "host ".$url->host; + # say "port ".$url->port; + # say "path ".$url->path; + # say "query ".$url->query; + # say "fragment ".$url->fragment; + + # keys = years + # my @objs = @{ $hash_values{$year} }; + # foreach my $obj (@objs){ + $self->stash( + hash_our_type_to_entries => $hash_our_type_to_entries, + hash_our_type_to_description => $hash_our_type_to_description, + + # this defines order of sections + ordered_section_names => $ordered_section_names_ref, + navbar => $navbar_html, + show_title => $show_title, + title => $title, + switch_link => $switchlink + ); + $self->res->headers->header('Access-Control-Allow-Origin' => '*'); + + my $html = $self->render_to_string(template => 'publications/landing_obj'); + $self->render(data => $html); + + # $self->render( template => 'publications/landing_obj' ); } ############################################################################################################ ####################################### HELPER functions for this controller ############################### ############################################################################################################ - sub get_switchlink_html { - my $self = shift; - my $keyword = shift; - - my $str; - $str .= '
'; - $str .= 'Grouping: '; - - - if ( $keyword eq 'years' ) { - $str .= $selected_anchor_delimiter_l.''.$selected_text_delimiter_l.'Types'.$selected_text_delimiter_r.''.$selected_anchor_delimiter_r; - $str .= $anchor_delimiter_l.''.$text_delimiter_l.'Years'.$text_delimiter_r.''.$anchor_delimiter_r; - } - elsif ( $keyword eq 'types' ) { - $str .= $anchor_delimiter_l.''.$text_delimiter_l.'Types'.$text_delimiter_r.''.$anchor_delimiter_r; - $str .= $selected_anchor_delimiter_l.''.$selected_text_delimiter_l.'Years'.$selected_text_delimiter_r.''.$selected_anchor_delimiter_r; - } - $str .= '
'; - return $str; + my $self = shift; + my $keyword = shift; + + my $str; + $str .= '
'; + $str .= 'Grouping: '; + + if ($keyword eq 'years') { + $str + .= $selected_anchor_delimiter_l + . '' + . $selected_text_delimiter_l . 'Types' + . $selected_text_delimiter_r . '' + . $selected_anchor_delimiter_r; + $str + .= $anchor_delimiter_l + . '' + . $text_delimiter_l . 'Years' + . $text_delimiter_r . '' + . $anchor_delimiter_r; + } + elsif ($keyword eq 'types') { + $str + .= $anchor_delimiter_l + . '' + . $text_delimiter_l . 'Types' + . $text_delimiter_r . '' + . $anchor_delimiter_r; + $str + .= $selected_anchor_delimiter_l + . '' + . $selected_text_delimiter_l . 'Years' + . $selected_text_delimiter_r . '' + . $selected_anchor_delimiter_r; + } + $str .= '
'; + return $str; } ############################################################################################################ ############################################################################################################ ############################################################################################################ -sub get_filtering_navbar_html { - my $self = shift; - - my $str; - ############### KIND - $str .= $self->get_navbar_kinds_html; - ############### TYPES - $str .= $self->get_navbar_types_html; - ############### YEARS - $str .= $self->get_navbar_years_html; - - $str .= '
'; - my $url = $self->url_with->query( [bibtex_type => undef, entry_type => undef, year => undef] ); - $str .= $anchor_delimiter_l.''.$text_delimiter_l.'clear all selections'.$text_delimiter_l.''.$anchor_delimiter_r; - $str .= '
'; - return $str; +sub get_filtering_navbar_html { + my $self = shift; + + my $str; + ############### KIND + $str .= $self->get_navbar_kinds_html; + ############### TYPES + $str .= $self->get_navbar_types_html; + ############### YEARS + $str .= $self->get_navbar_years_html; + + $str .= '
'; + my $url = $self->url_with->query( + [bibtex_type => undef, entry_type => undef, year => undef]); + $str + .= $anchor_delimiter_l + . '' + . $text_delimiter_l + . 'clear all selections' + . $text_delimiter_l . '' + . $anchor_delimiter_r; + $str .= '
'; + return $str; } ############################################################################################################ sub get_navbar_kinds_html { - my $self = shift; - - my $curr_bibtex_type = $self->req->param('bibtex_type') // undef; - my $curr_entry_type = $self->req->param('entry_type') // undef; - my $curr_year = $self->req->param('year') // undef; - - ############### KIND - my $str; - $str .= '
'; - $str .= 'Kind: '; - - - foreach my $key (qw(Paper Talk)) { - - my $url; - if($key eq 'Talk'){ - $url = $self->url_with->query( [entry_type => lc($key), bibtex_type => 'misc'] ); - } - else{ - $url = $self->url_with->query( [entry_type => lc($key)] ); - } - - my $text = $text_delimiter_l . $key . $text_delimiter_r; - - my $num = $self->num_pubs_filtering( - $curr_bibtex_type, - $key, - $curr_year ); - - if ( defined $curr_entry_type and lc($key) eq $curr_entry_type ) { - my $text = $selected_text_delimiter_l . $key . $selected_text_delimiter_r; - $str .= $selected_anchor_delimiter_l.''.$text.''.$selected_anchor_delimiter_r; - } - else { - my $text = $text_delimiter_l . $key . $text_delimiter_r; - $str .= $anchor_delimiter_l.''.$text.''.$anchor_delimiter_r; - } - } - $str .= '
'; - return $str; -} -############################################################################################################ -sub get_navbar_types_html { - my $self = shift; + my $self = shift; - my $curr_bibtex_type = $self->req->param('bibtex_type') // undef; - my $curr_entry_type = $self->req->param('entry_type') // undef; - my $curr_year = $self->req->param('year') // undef; + my $curr_bibtex_type = $self->req->param('bibtex_type') // undef; + my $curr_entry_type = $self->req->param('entry_type') // undef; + my $curr_year = $self->req->param('year') // undef; - my @landingTypes - = $self->app->repo->types_filter( sub { $_->onLanding == 1 } ); + ############### KIND + my $str; + $str .= '
'; + $str .= 'Kind: '; - my %bibtex_type_to_label - = map { $_->our_type => $_->description } @landingTypes; - foreach my $k ( keys %bibtex_type_to_label ) { - if ( !$bibtex_type_to_label{$k} ) { - $bibtex_type_to_label{$k} = get_generic_type_description($k); - } + foreach my $key (qw(Paper Talk)) { + + my $url; + if ($key eq 'Talk') { + $url = $self->url_with->query( + [entry_type => lc($key), bibtex_type => 'misc']); + } + else { + $url = $self->url_with->query([entry_type => lc($key)]); } - ############### TYPE - my $str; - $str .= '
'; - $str .= 'Type: '; + my $text = $text_delimiter_l . $key . $text_delimiter_r; + my $num = $self->num_pubs_filtering($curr_bibtex_type, $key, $curr_year); - foreach my $type ( sort { $a->our_type cmp $b->our_type } @landingTypes ) - { - my $key = $type->our_type; - my $num = $self->num_pubs_filtering( $key, $curr_entry_type, $curr_year ); - my $url = $self->url_with->query( [bibtex_type => $key] ); - - my $text = $text_delimiter_l . $bibtex_type_to_label{$key} . $text_delimiter_r; - - if ( defined $curr_bibtex_type and $key eq $curr_bibtex_type ) { - my $text = $selected_text_delimiter_l . $bibtex_type_to_label{$key} . $selected_text_delimiter_r; - if ($num) { - $str .= $selected_anchor_delimiter_l.''.$text.''.$selected_anchor_delimiter_r; - - } - else{ - $str .= $selected_anchor_delimiter_l.''.$text.''.$selected_anchor_delimiter_r; - } - } - else { - my $text = $text_delimiter_l . $bibtex_type_to_label{$key} . $text_delimiter_r; - if ($num) { - $str .= $anchor_delimiter_l.''.$text.''.$anchor_delimiter_r; - } - else { - $str .= $anchor_delimiter_l.''.$text.''.$anchor_delimiter_r; - } - } + if (defined $curr_entry_type and lc($key) eq $curr_entry_type) { + my $text = $selected_text_delimiter_l . $key . $selected_text_delimiter_r; + $str + .= $selected_anchor_delimiter_l + . '' + . $text . '' + . $selected_anchor_delimiter_r; + } + else { + my $text = $text_delimiter_l . $key . $text_delimiter_r; + $str + .= $anchor_delimiter_l + . '' + . $text . '' + . $anchor_delimiter_r; } - $str .= '
'; - return $str; + } + $str .= '
'; + return $str; } ############################################################################################################ -sub get_navbar_years_html { - my $self = shift; +sub get_navbar_types_html { + my $self = shift; - my $curr_bibtex_type = $self->param('bibtex_type') // undef; - my $curr_entry_type = $self->param('entry_type') // undef; - my $curr_year = $self->param('year') // undef; - my $author = $self->param('author') // undef; + my $curr_bibtex_type = $self->req->param('bibtex_type') // undef; + my $curr_entry_type = $self->req->param('entry_type') // undef; + my $curr_year = $self->req->param('year') // undef; - my $min_year = $self->get_year_of_oldest_entry($author) // $self->current_year; - my $max_year = $self->current_year; + my @landingTypes = $self->app->repo->types_filter(sub { $_->onLanding == 1 }); - # 8 is a month in which we show publications from the next year - if ( $self->current_month > 8 ) { # TODO export to config. - $max_year++; + my %bibtex_type_to_label + = map { $_->our_type => $_->description } @landingTypes; + foreach my $k (keys %bibtex_type_to_label) { + if (!$bibtex_type_to_label{$k}) { + $bibtex_type_to_label{$k} = get_generic_type_description($k); } - my @all_years = ( $min_year .. $max_year ); - @all_years = reverse @all_years; - - ############### YEARS - my $str; - $str .= '
'; - $str .= 'Year: '; - - - foreach my $key ( reverse sort @all_years ) { - my $num = $self->num_pubs_filtering( - $curr_bibtex_type, - $curr_entry_type, - $key ); - - my $url = $self->url_with->query( [year => $key] ); - - if ( defined $curr_year and $key eq $curr_year ) { - my $text = $selected_text_delimiter_l . $key . $selected_text_delimiter_r; - if ($num) { - $str .= $selected_anchor_delimiter_l.''.$text.''.$selected_anchor_delimiter_r; - } - else{ - $str .= $selected_anchor_delimiter_l.''.$text.''.$selected_anchor_delimiter_r; - } - } - else { - my $text = $text_delimiter_l . $key . $text_delimiter_r; - if ($num) { - $str .= $anchor_delimiter_l.''.$text.''.$anchor_delimiter_r; - } - else { - $str .= $anchor_delimiter_l.''.$text.''.$anchor_delimiter_r; - } - } + } + + ############### TYPE + my $str; + $str .= '
'; + $str .= 'Type: '; + + foreach my $type (sort { $a->our_type cmp $b->our_type } @landingTypes) { + my $key = $type->our_type; + my $num = $self->num_pubs_filtering($key, $curr_entry_type, $curr_year); + my $url = $self->url_with->query([bibtex_type => $key]); + + my $text + = $text_delimiter_l . $bibtex_type_to_label{$key} . $text_delimiter_r; + + if (defined $curr_bibtex_type and $key eq $curr_bibtex_type) { + my $text + = $selected_text_delimiter_l + . $bibtex_type_to_label{$key} + . $selected_text_delimiter_r; + if ($num) { + $str + .= $selected_anchor_delimiter_l + . '' + . $text . '' + . $selected_anchor_delimiter_r; + + } + else { + $str + .= $selected_anchor_delimiter_l + . '' + . $text . '' + . $selected_anchor_delimiter_r; + } } - $str .= '
'; - return $str; + else { + my $text + = $text_delimiter_l . $bibtex_type_to_label{$key} . $text_delimiter_r; + if ($num) { + $str + .= $anchor_delimiter_l + . '' + . $text . '' + . $anchor_delimiter_r; + } + else { + $str + .= $anchor_delimiter_l + . '' + . $text . '' + . $anchor_delimiter_r; + } + } + } + $str .= '
'; + return $str; +} +############################################################################################################ +sub get_navbar_years_html { + my $self = shift; + + my $curr_bibtex_type = $self->param('bibtex_type') // undef; + my $curr_entry_type = $self->param('entry_type') // undef; + my $curr_year = $self->param('year') // undef; + my $author = $self->param('author') // undef; + + my $min_year = $self->get_year_of_oldest_entry($author) + // $self->current_year; + my $max_year = $self->current_year; + + # 8 is a month in which we show publications from the next year + if ($self->current_month > 8) { # TODO export to config. + $max_year++; + } + my @all_years = ($min_year .. $max_year); + @all_years = reverse @all_years; + + ############### YEARS + my $str; + $str .= '
'; + $str .= 'Year: '; + + foreach my $key (reverse sort @all_years) { + my $num + = $self->num_pubs_filtering($curr_bibtex_type, $curr_entry_type, $key); + + my $url = $self->url_with->query([year => $key]); + + if (defined $curr_year and $key eq $curr_year) { + my $text = $selected_text_delimiter_l . $key . $selected_text_delimiter_r; + if ($num) { + $str + .= $selected_anchor_delimiter_l + . '' + . $text . '' + . $selected_anchor_delimiter_r; + } + else { + $str + .= $selected_anchor_delimiter_l + . '' + . $text . '' + . $selected_anchor_delimiter_r; + } + } + else { + my $text = $text_delimiter_l . $key . $text_delimiter_r; + if ($num) { + $str + .= $anchor_delimiter_l + . '' + . $text . '' + . $anchor_delimiter_r; + } + else { + $str + .= $anchor_delimiter_l + . '' + . $text . '' + . $anchor_delimiter_r; + } + } + } + $str .= '
'; + return $str; } ############################################################################################################ sub num_pubs_filtering { - my $self = shift; - my $curr_bibtex_type = shift; - my $curr_entry_type = shift; - my $curr_year = shift; - - - return scalar Fget_publications_main_hashed_args( - $self, - { bibtex_type => $curr_bibtex_type, - entry_type => $curr_entry_type, - year => $curr_year, - visible => 1, - hidden => 0 - } - ); + my $self = shift; + my $curr_bibtex_type = shift; + my $curr_entry_type = shift; + my $curr_year = shift; + + return scalar Fget_publications_main_hashed_args( + $self, + { + bibtex_type => $curr_bibtex_type, + entry_type => $curr_entry_type, + year => $curr_year, + visible => 1, + hidden => 0 + } + ); } ############################################################################################################ ############################################################################################################ ############################################################################################################ sub get_papers_for_landing { - my $self = shift; - return Fget_publications_main_hashed_args( - $self, - { entry_type => 'paper', - visible => 0, - hidden => 0 - # the rest of parameters will be taken from $self - } - ); + my $self = shift; + return Fget_publications_main_hashed_args( + $self, + { + entry_type => 'paper', + visible => 0, + hidden => 0 + + # the rest of parameters will be taken from $self + } + ); } ############################################################################################################ sub get_talks_for_landing { - my $self = shift; - return Fget_publications_main_hashed_args( - $self, - { entry_type => 'talk', - visible => 0, - hidden => 0 - # the rest of parameters will be taken from $self - } - ); + my $self = shift; + return Fget_publications_main_hashed_args( + $self, + { + entry_type => 'talk', + visible => 0, + hidden => 0 + + # the rest of parameters will be taken from $self + } + ); } ############################################################################################################ sub get_entries_for_landing { - my $self = shift; - return Fget_publications_main_hashed_args( - $self, - { visible => 0, - hidden => 0, - # the rest of parameters will be taken from $self - } - ); + my $self = shift; + return Fget_publications_main_hashed_args( + $self, + { + visible => 0, + hidden => 0, + + # the rest of parameters will be taken from $self + } + ); } ############################################################################################################ 1; diff --git a/lib/BibSpace/Controller/PublicationsSeo.pm b/lib/BibSpace/Controller/PublicationsSeo.pm index cb5abcb..5e83ba5 100644 --- a/lib/BibSpace/Controller/PublicationsSeo.pm +++ b/lib/BibSpace/Controller/PublicationsSeo.pm @@ -1,7 +1,5 @@ package BibSpace::Controller::PublicationsSeo; - - use strict; use warnings; use utf8; @@ -12,9 +10,9 @@ use v5.16; #because of ~~ use TeX::Encode; use Encode; -use BibSpace::Functions::Core; -use BibSpace::Functions::FPublications; -use BibSpace::Controller::Publications; +use BibSpace::Functions::Core; +use BibSpace::Functions::FPublications; +use BibSpace::Controller::Publications; use Mojo::Base 'Mojolicious::Controller'; use Mojo::Base 'Mojolicious::Plugin::Config'; @@ -23,253 +21,248 @@ use Mojo::Log; #################################################################################### sub metalist { - my $self = shift; - + my $self = shift; - my @pubs = Fget_publications_main_hashed_args( $self, { entry_type => 'paper', hidden => 0 } ); + my @pubs = Fget_publications_main_hashed_args($self, + {entry_type => 'paper', hidden => 0}); - $self->stash( entries => \@pubs ); - $self->render( template => 'publicationsSEO/metalist' ); + $self->stash(entries => \@pubs); + $self->render(template => 'publicationsSEO/metalist'); } #################################################################################### sub meta { - my $self = shift; - my $id = $self->param('id'); - - my $mentry = $self->app->repo->entries_find(sub{ $_->id==$id } ); - - if ( !defined $mentry or $mentry->is_hidden ) { - $self->render( - text => 'Cannot find entry ID \'' . $id.'\'.', - status => 404 - ); - return; - } - - - # PARSING BIBTEX - - #this should happen earlier! - $mentry->bib( fix_bibtex_national_characters( $mentry->bib ) ); - $mentry->populate_from_bib(); - - my $bibtex_entry_str = $mentry->bib; - my $bibtex_entry = new Text::BibTeX::Entry(); - $bibtex_entry->parse_s($bibtex_entry_str); - unless ( $bibtex_entry->parse_ok ) { - $self->render( - text => - 'Error 503: Cannot parse BibTeX code for this entry! Entry id: ' - . $id, - status => 503 - ); # TODO: check proper error code - return; - } - - # EXTRACTING IMPORTANT FIELDS - - # TITLE - my $title = $mentry->get_title; - - my $citation_title = $title; - - # AUTHORS - my @names; - my @citation_authors; - if ( $bibtex_entry->exists('author') ) { - my @authors = $bibtex_entry->split('author'); - my (@n) = $bibtex_entry->names('author'); - @names = @n; - } - elsif ( $bibtex_entry->exists('editor') ) { - my @authors = $bibtex_entry->split('editor'); - my (@n) = $bibtex_entry->names('editor'); - @names = @n; - } - for my $name (@names) { - - my $name_clean = ""; - $name_clean = decode( 'latex', $name_clean ); - my $firstname = join( ' ', grep {defined $_} $name->part('first') ) if defined $name->part('first'); - my $von = join( ' ', $name->part('von') ) - if defined $name->part('von'); - my $lastname = join( ' ', $name->part('last') ); - my $jr = join( ' ', $name->part('jr') ) if defined $name->part('jr'); + my $self = shift; + my $id = $self->param('id'); - $name_clean = $firstname; - $name_clean .= " " . $von if defined $von; - $name_clean .= " " . $lastname if defined $lastname; - $name_clean .= " " . $jr if defined $jr; + my $mentry = $self->app->repo->entries_find(sub { $_->id == $id }); - $name_clean =~ s/\ +/ /g; - $name_clean =~ s/\ $//g; - - push @citation_authors, $name_clean; - } - - # PUBLICATION DATE - my $year = $bibtex_entry->get('year'); - - my $month = 0; - $month = $bibtex_entry->get('month') if $bibtex_entry->exists('month'); - - my $days = 0; - $days = $bibtex_entry->get('day') if $bibtex_entry->exists('day'); - my @day = (); - @day = split( "--", $days ) if defined $days; - my $first_day = 0; - $first_day = $day[0] if defined $days; - - my $citation_publication_date = $year; + if (!defined $mentry or $mentry->is_hidden) { + $self->render( + text => 'Cannot find entry ID \'' . $id . '\'.', + status => 404 + ); + return; + } + + # PARSING BIBTEX + + #this should happen earlier! + $mentry->bib(fix_bibtex_national_characters($mentry->bib)); + $mentry->populate_from_bib(); + + my $bibtex_entry_str = $mentry->bib; + my $bibtex_entry = new Text::BibTeX::Entry(); + $bibtex_entry->parse_s($bibtex_entry_str); + unless ($bibtex_entry->parse_ok) { + $self->render( + text => 'Error 503: Cannot parse BibTeX code for this entry! Entry id: ' + . $id, + status => 503 + ); # TODO: check proper error code + return; + } + + # EXTRACTING IMPORTANT FIELDS + + # TITLE + my $title = $mentry->get_title; + + my $citation_title = $title; + + # AUTHORS + my @names; + my @citation_authors; + if ($bibtex_entry->exists('author')) { + my @authors = $bibtex_entry->split('author'); + my (@n) = $bibtex_entry->names('author'); + @names = @n; + } + elsif ($bibtex_entry->exists('editor')) { + my @authors = $bibtex_entry->split('editor'); + my (@n) = $bibtex_entry->names('editor'); + @names = @n; + } + for my $name (@names) { + + my $name_clean = ""; + $name_clean = decode('latex', $name_clean); + my $firstname = join(' ', grep { defined $_ } $name->part('first')) + if defined $name->part('first'); + my $von = join(' ', $name->part('von')) if defined $name->part('von'); + my $lastname = join(' ', $name->part('last')); + my $jr = join(' ', $name->part('jr')) if defined $name->part('jr'); + + $name_clean = $firstname; + $name_clean .= " " . $von if defined $von; + $name_clean .= " " . $lastname if defined $lastname; + $name_clean .= " " . $jr if defined $jr; + + $name_clean =~ s/\ +/ /g; + $name_clean =~ s/\ $//g; + + push @citation_authors, $name_clean; + } + + # PUBLICATION DATE + my $year = $bibtex_entry->get('year'); + + my $month = 0; + $month = $bibtex_entry->get('month') if $bibtex_entry->exists('month'); + + my $days = 0; + $days = $bibtex_entry->get('day') if $bibtex_entry->exists('day'); + my @day = (); + @day = split("--", $days) if defined $days; + my $first_day = 0; + $first_day = $day[0] if defined $days; + + my $citation_publication_date = $year; # $citation_publication_date .= "/".$month if defined $month; # $citation_publication_date .= "/".$first_day if defined $first_day and defined $days;; - # ABSTRACT - my $abstract = $bibtex_entry->get('abstract'); - $abstract ||= "This paper has no abstract. The title is: " . $citation_title; - - $abstract = decode( 'latex', $abstract ); - $abstract =~ s/^\{//g; - $abstract =~ s/\}$//g; - - # TYPE - my $type = $bibtex_entry->type; - - my $citation_journal_title; # OK - my $citation_conference_title; #ok - my $citation_issn; # IGNORE - my $citation_isbn; # IGNORE - my $citation_volume; #ok - my $citation_issue; # ok - my $citation_firstpage; #ok - my $citation_lastpage; #ok - - if ( $type eq "article" ) { - if ( $bibtex_entry->exists('journal') ) { - $citation_journal_title = $bibtex_entry->get('journal'); - $citation_journal_title - = decode( 'latex', $citation_journal_title ); - } - if ( $bibtex_entry->exists('volume') ) { - $citation_volume = $bibtex_entry->get('volume'); - } - if ( $bibtex_entry->exists('number') ) { - $citation_issue = $bibtex_entry->get('number'); - } + # ABSTRACT + my $abstract = $bibtex_entry->get('abstract'); + $abstract ||= "This paper has no abstract. The title is: " . $citation_title; + + $abstract = decode('latex', $abstract); + $abstract =~ s/^\{//g; + $abstract =~ s/\}$//g; + + # TYPE + my $type = $bibtex_entry->type; + + my $citation_journal_title; # OK + my $citation_conference_title; #ok + my $citation_issn; # IGNORE + my $citation_isbn; # IGNORE + my $citation_volume; #ok + my $citation_issue; # ok + my $citation_firstpage; #ok + my $citation_lastpage; #ok + + if ($type eq "article") { + if ($bibtex_entry->exists('journal')) { + $citation_journal_title = $bibtex_entry->get('journal'); + $citation_journal_title = decode('latex', $citation_journal_title); } - - if ( $bibtex_entry->exists('pages') ) { - my $pages = $bibtex_entry->get('pages'); - - my @pages_arr; - if ( $pages =~ /--/ ) { - @pages_arr = split( "--", $pages ); - } - elsif ( $pages =~ /-/ ) { - @pages_arr = split( "--", $pages ); - } - else { - push @pages_arr, $pages; - push @pages_arr, $pages; - } - $citation_firstpage = $pages_arr[0] if defined $pages_arr[0]; - $citation_lastpage = $pages_arr[1] if defined $pages_arr[1]; + if ($bibtex_entry->exists('volume')) { + $citation_volume = $bibtex_entry->get('volume'); } - - if ( $bibtex_entry->exists('booktitle') ) { - $citation_conference_title = $bibtex_entry->get('booktitle'); - $citation_conference_title - = decode( 'latex', $citation_conference_title ); + if ($bibtex_entry->exists('number')) { + $citation_issue = $bibtex_entry->get('number'); } + } - # TECH REPORTS AND THESES + if ($bibtex_entry->exists('pages')) { + my $pages = $bibtex_entry->get('pages'); - my $citation_dissertation_institution; - my $citation_technical_report_institution; - my $citation_technical_report_number; - - if ( $type eq "mastersthesis" or $type eq "phdthesis" ) { - $citation_dissertation_institution = $bibtex_entry->get('school') - if $bibtex_entry->exists('school'); - $citation_dissertation_institution - = decode( 'latex', $citation_dissertation_institution ); + my @pages_arr; + if ($pages =~ /--/) { + @pages_arr = split("--", $pages); } - - if ( $type eq "techreport" ) { - $citation_technical_report_institution = $bibtex_entry->get('institution') - if $bibtex_entry->exists('institution'); - $citation_technical_report_institution - = decode( 'latex', $citation_technical_report_institution ); - $citation_technical_report_number = $bibtex_entry->get('number') - if $bibtex_entry->exists('number'); - $citation_technical_report_number = $bibtex_entry->get('type') - if $bibtex_entry->exists('type'); + elsif ($pages =~ /-/) { + @pages_arr = split("--", $pages); } - - # PDF URL - my $citation_pdf_url = undef; - - if ( $bibtex_entry->exists('pdf') ) { - my $local_file_paper = $mentry->get_attachment('paper'); - if ( $local_file_paper and -e $local_file_paper ) { - $citation_pdf_url = $self->url_for( - 'download_publication_pdf', - filetype => 'paper', - id => $id - ); - } - else { - $citation_pdf_url = $bibtex_entry->get('pdf'); - } + else { + push @pages_arr, $pages; + push @pages_arr, $pages; } - elsif ( $bibtex_entry->exists('slides') ) { - my $local_file_slides = $mentry->get_attachment('slides'); - if ( $local_file_slides and -e $local_file_slides ) { - $citation_pdf_url = $self->url_for( - 'download_publication_pdf', - filetype => 'slides', - id => $id - ); - } - else { - $citation_pdf_url = $bibtex_entry->get('slides'); - } + $citation_firstpage = $pages_arr[0] if defined $pages_arr[0]; + $citation_lastpage = $pages_arr[1] if defined $pages_arr[1]; + } + + if ($bibtex_entry->exists('booktitle')) { + $citation_conference_title = $bibtex_entry->get('booktitle'); + $citation_conference_title = decode('latex', $citation_conference_title); + } + + # TECH REPORTS AND THESES + + my $citation_dissertation_institution; + my $citation_technical_report_institution; + my $citation_technical_report_number; + + if ($type eq "mastersthesis" or $type eq "phdthesis") { + $citation_dissertation_institution = $bibtex_entry->get('school') + if $bibtex_entry->exists('school'); + $citation_dissertation_institution + = decode('latex', $citation_dissertation_institution); + } + + if ($type eq "techreport") { + $citation_technical_report_institution = $bibtex_entry->get('institution') + if $bibtex_entry->exists('institution'); + $citation_technical_report_institution + = decode('latex', $citation_technical_report_institution); + $citation_technical_report_number = $bibtex_entry->get('number') + if $bibtex_entry->exists('number'); + $citation_technical_report_number = $bibtex_entry->get('type') + if $bibtex_entry->exists('type'); + } + + # PDF URL + my $citation_pdf_url = undef; + + if ($bibtex_entry->exists('pdf')) { + my $local_file_paper = $mentry->get_attachment('paper'); + if ($local_file_paper and -e $local_file_paper) { + $citation_pdf_url = $self->url_for( + 'download_publication_pdf', + filetype => 'paper', + id => $id + ); } - elsif ( $bibtex_entry->exists('url') ) { - $citation_pdf_url = $bibtex_entry->get('url'); + else { + $citation_pdf_url = $bibtex_entry->get('pdf'); + } + } + elsif ($bibtex_entry->exists('slides')) { + my $local_file_slides = $mentry->get_attachment('slides'); + if ($local_file_slides and -e $local_file_slides) { + $citation_pdf_url = $self->url_for( + 'download_publication_pdf', + filetype => 'slides', + id => $id + ); } else { - # this entry has no pdf/slides/url. $citation_pdf_url remains undef + $citation_pdf_url = $bibtex_entry->get('slides'); } - - # READY SET OF VARIABLES HOLDING METADATA. Some may be undef - my $ca_ref = \@citation_authors; - - $self->stash( - citation_title => $citation_title, - citation_authors => $ca_ref, - abstract => $abstract, - citation_publication_date => $citation_publication_date, - citation_journal_title => $citation_journal_title, - citation_conference_title => $citation_conference_title, - citation_issn => $citation_issn, - citation_isbn => $citation_isbn, - citation_volume => $citation_volume, - citation_issue => $citation_issue, - citation_firstpage => $citation_firstpage, - citation_lastpage => $citation_lastpage, - citation_dissertation_institution => - $citation_dissertation_institution, - citation_technical_report_institution => - $citation_technical_report_institution, - citation_technical_report_number => $citation_technical_report_number, - citation_pdf_url => $citation_pdf_url - ); - - $self->render( template => 'publicationsSEO/meta' ); + } + elsif ($bibtex_entry->exists('url')) { + $citation_pdf_url = $bibtex_entry->get('url'); + } + else { + # this entry has no pdf/slides/url. $citation_pdf_url remains undef + } + + # READY SET OF VARIABLES HOLDING METADATA. Some may be undef + my $ca_ref = \@citation_authors; + + $self->stash( + citation_title => $citation_title, + citation_authors => $ca_ref, + abstract => $abstract, + citation_publication_date => $citation_publication_date, + citation_journal_title => $citation_journal_title, + citation_conference_title => $citation_conference_title, + citation_issn => $citation_issn, + citation_isbn => $citation_isbn, + citation_volume => $citation_volume, + citation_issue => $citation_issue, + citation_firstpage => $citation_firstpage, + citation_lastpage => $citation_lastpage, + citation_dissertation_institution => $citation_dissertation_institution, + citation_technical_report_institution => + $citation_technical_report_institution, + citation_technical_report_number => $citation_technical_report_number, + citation_pdf_url => $citation_pdf_url + ); + + $self->render(template => 'publicationsSEO/meta'); } #################################################################################### diff --git a/lib/BibSpace/Controller/Tags.pm b/lib/BibSpace/Controller/Tags.pm index f2cfdb8..4b34bcb 100644 --- a/lib/BibSpace/Controller/Tags.pm +++ b/lib/BibSpace/Controller/Tags.pm @@ -6,6 +6,7 @@ use Data::Dumper; use utf8; use Text::BibTeX; # parsing bib files use DateTime; + # use File::Slurp; use v5.16; #because of ~~ @@ -29,18 +30,18 @@ sub index { my $letter = $self->param('letter'); my $type = $self->param('type') // 1; - my @all_tags = $self->app->repo->tags_filter( sub { $_->type == $type } ); + my @all_tags = $self->app->repo->tags_filter(sub { $_->type == $type }); my @tags = @all_tags; - if ( defined $letter ) { - @tags = grep { ( substr( $_->name, 0, 1 ) cmp $letter ) == 0 } @all_tags; + if (defined $letter) { + @tags = grep { (substr($_->name, 0, 1) cmp $letter) == 0 } @all_tags; } - my @letters_arr = map { substr( $_->name, 0, 1 ) } @all_tags; + my @letters_arr = map { substr($_->name, 0, 1) } @all_tags; @letters_arr = uniq @letters_arr; @letters_arr = sort @letters_arr; @tags = sort { $a->name cmp $b->name } @tags; - $self->stash( otags => \@tags, type => $type, letters_arr => \@letters_arr ); - $self->render( template => 'tags/tags' ); + $self->stash(otags => \@tags, type => $type, letters_arr => \@letters_arr); + $self->render(template => 'tags/tags'); } #################################################################################### @@ -49,7 +50,7 @@ sub add { my $dbh = $self->app->db; my $type = $self->param('type') // 1; - $self->render( template => 'tags/add', type => $type ); + $self->render(template => 'tags/add', type => $type); } #################################################################################### @@ -58,21 +59,23 @@ sub add_post { my $tag_type = $self->param('type') // 1; my $tags_to_add = $self->param('new_tag'); - my @tags; my @tag_names; - if ( defined $tags_to_add ) { - my @pre_tag_names = split( ';', $tags_to_add ); + if (defined $tags_to_add) { + my @pre_tag_names = split(';', $tags_to_add); foreach my $tag (@pre_tag_names) { $tag = clean_tag_name($tag); - if ( defined $tag and $tag ne '' and length($tag) > 0 ) { + if (defined $tag and $tag ne '' and length($tag) > 0) { push @tag_names, $tag if defined $tag; } } foreach my $tag_name (@tag_names) { - my $new_tag = $self->app->entityFactory->new_Tag( name => $tag_name, type => $tag_type ); - + my $new_tag = $self->app->entityFactory->new_Tag( + name => $tag_name, + type => $tag_type + ); + $self->app->repo->tags_save($new_tag); $self->app->logger->info("Added new tag '$tag_name' type '$tag_type'."); push @tags, $new_tag; @@ -80,16 +83,16 @@ sub add_post { } } - - $self->flash( msg => "The following tags (of type $tag_type) were added successfully: " . " " - . join( ", ", map { $_->name } @tags ) + $self->flash( + msg => "The following tags (of type $tag_type) were added successfully: " + . " " + . join(", ", map { $_->name } @tags) . " ," . " ids: " - . join( ", ", map { $_->id } @tags ) - . "" ) + . join(", ", map { $_->id } @tags) . "") if scalar @tags > 0; - $self->redirect_to( $self->url_for( 'all_tags', type => $tag_type ) ); + $self->redirect_to($self->url_for('all_tags', type => $tag_type)); # $self->render(template => 'tags/add'); } @@ -105,7 +108,7 @@ sub edit { my $new_type = $self->param('new_type') || undef; my $saved = 0; - my $tag = $self->app->repo->tags_find( sub { $_->id == $id } ); + my $tag = $self->app->repo->tags_find(sub { $_->id == $id }); $tag->name($new_name) if defined $new_name; $tag->permalink($new_permalink) if defined $new_permalink; @@ -113,10 +116,10 @@ sub edit { $self->app->repo->tags_update($tag); - $self->flash( msg_type => 'success', msg => 'Changes saved.' ); + $self->flash(msg_type => 'success', msg => 'Changes saved.'); - $self->stash( tagobj => $tag ); - $self->render( template => 'tags/edit' ); + $self->stash(tagobj => $tag); + $self->render(template => 'tags/edit'); } @@ -127,56 +130,60 @@ sub get_authors_for_tag_and_team { my $tag_id = $self->param('tag_id'); my $team_id = $self->param('team_id'); - my $tag = $self->app->repo->tags_find( sub { $_->id == $tag_id } ); - if( !$tag ){ - $tag = $self->app->repo->tags_find( sub { $_->name eq $tag_id } ); + my $tag = $self->app->repo->tags_find(sub { $_->id == $tag_id }); + if (!$tag) { + $tag = $self->app->repo->tags_find(sub { $_->name eq $tag_id }); } - my $team = $self->app->repo->teams_find( sub { $_->id == $team_id } ); - if( !$team ){ - $team = $self->app->repo->teams_find( sub { $_->name eq $team_id } ); + my $team = $self->app->repo->teams_find(sub { $_->id == $team_id }); + if (!$team) { + $team = $self->app->repo->teams_find(sub { $_->name eq $team_id }); } - if ( !defined $tag ) { - $self->render( text => "Tag $tag_id does not exist", status => 404 ); + if (!defined $tag) { + $self->render(text => "Tag $tag_id does not exist", status => 404); return; } - if ( defined $team_id and !defined $team ) { - $self->render( text => "Team $team_id does not exist", status => 404 ); + if (defined $team_id and !defined $team) { + $self->render(text => "Team $team_id does not exist", status => 404); return; } my @authors = $tag->get_authors($dbh); - @authors = grep {$_->has_team($team)} @authors; + @authors = grep { $_->has_team($team) } @authors; - $self->stash( tag => $tag, authors => \@authors ); - $self->render( template => 'tags/authors_having_tag_read' ); + $self->stash(tag => $tag, authors => \@authors); + $self->render(template => 'tags/authors_having_tag_read'); } #################################################################################### sub get_tags_for_author_read { my $self = shift; my $author_id = $self->param('author_id'); - my $author = $self->app->repo->authors_find( sub { $_->id == $author_id } ); - if( !$author ){ - $author = $self->app->repo->authors_find( sub { $_->get_master->uid eq $author_id } ); + my $author = $self->app->repo->authors_find(sub { $_->id == $author_id }); + if (!$author) { + $author + = $self->app->repo->authors_find(sub { $_->get_master->uid eq $author_id } + ); } - if( !$author ){ - $author = $self->app->repo->authors_find( sub { $_->uid eq $author_id } ); + if (!$author) { + $author = $self->app->repo->authors_find(sub { $_->uid eq $author_id }); } - if ( !$author ) { - $self->render( text => "Cannot find author $author_id.", status => 404 ); + if (!$author) { + $self->render(text => "Cannot find author $author_id.", status => 404); return; } my @author_tags = $author->get_tags; - if ( !@author_tags ) { - $self->render( text => "Author $author_id has no tagged papers.", status => 200 ); + if (!@author_tags) { + $self->render( + text => "Author $author_id has no tagged papers.", + status => 200 + ); return; } - ### here list of objects should be created my @tagc_cloud_arr; @@ -184,14 +191,18 @@ sub get_tags_for_author_read { foreach my $tag (@author_tags) { - my $tag_name = $tag->{name}; $tag_name =~ s/_/\ /g; - my @objs = Fget_publications_main_hashed_args( $self, { hidden => 0, author => $author->{id}, tag => $tag->{id} } ); + my @objs = Fget_publications_main_hashed_args($self, + {hidden => 0, author => $author->{id}, tag => $tag->{id}}); my $count = scalar @objs; - my $url - = $self->url_for('lyp')->query( author => $author->{master}, tag => $tag_name, title => '1', navbar => '1' ); + my $url = $self->url_for('lyp')->query( + author => $author->{master}, + tag => $tag_name, + title => '1', + navbar => '1' + ); my $tag_cloud_obj = TagCloud->new(); $tag_cloud_obj->{url} = $url; @@ -201,84 +212,98 @@ sub get_tags_for_author_read { push @tagc_cloud_arr, $tag_cloud_obj; } - @sorted_tagc_cloud_arr = reverse sort { $a->{count} <=> $b->{count} } @tagc_cloud_arr; + @sorted_tagc_cloud_arr + = reverse sort { $a->{count} <=> $b->{count} } @tagc_cloud_arr; ### old code - $self->stash( tags => \@author_tags, author => $author, tcarr => \@sorted_tagc_cloud_arr ); - $self->render( template => 'tags/author_tags_read' ); + $self->stash( + tags => \@author_tags, + author => $author, + tcarr => \@sorted_tagc_cloud_arr + ); + $self->render(template => 'tags/author_tags_read'); } #################################################################################### # we mean here tags of type 1 sub get_tags_for_team_read { - my $self = shift; - my $team_id = $self->param('team_id'); - my $tag_type = 1; + my $self = shift; + my $team_id = $self->param('team_id'); + my $tag_type = 1; - my $team = $self->app->repo->teams_find( sub { $_->id == $team_id } ); - $team ||= $self->app->repo->teams_find( sub { $_->name eq $team_id } ); + my $team = $self->app->repo->teams_find(sub { $_->id == $team_id }); + $team ||= $self->app->repo->teams_find(sub { $_->name eq $team_id }); - - if ( !$team ) { - $self->render( text => "404. Team '$team_id' does not exist.", status => 404 ); + if (!$team) { + $self->render( + text => "404. Team '$team_id' does not exist.", + status => 404 + ); return; } my @team_entries = grep { $_->has_team($team) } $self->app->repo->entries_all; my %team_tags_hash; foreach my $paper (@team_entries) { + # merge two hashes - %team_tags_hash = (%team_tags_hash, map { "".$_->name => $_} $paper->get_tags($tag_type) ); + %team_tags_hash = (%team_tags_hash, + map { "" . $_->name => $_ } $paper->get_tags($tag_type)); } my @team_tags = values %team_tags_hash; - - if ( !@team_tags ) { - $self->render( text => "Team '$team_id' has no tagged papers.", status => 200 ); + if (!@team_tags) { + $self->render( + text => "Team '$team_id' has no tagged papers.", + status => 200 + ); return; } - my @tagc_cloud_arr; my @sorted_tagc_cloud_arr; foreach my $tag (@team_tags) { my $tag_name = $tag->name; $tag_name =~ s/_/\ /g; - + # FIXME: not all papers belong to team # FIXME: take exceptions into account - my @papers = grep { $_->has_team($team) } $tag->get_entries; - + my @papers = grep { $_->has_team($team) } $tag->get_entries; - my $url = "".$self->url_for('lyp')->query( team => $team->name, tag => $tag->name, title => '1', navbar => '1' ); + my $url = "" + . $self->url_for('lyp')->query( + team => $team->name, + tag => $tag->name, + title => '1', + navbar => '1' + ); my $tag_cloud_obj = TagCloud->new( url => $url, - count => "".scalar(@papers), + count => "" . scalar(@papers), name => $tag_name, ); push @tagc_cloud_arr, $tag_cloud_obj; } - @sorted_tagc_cloud_arr = reverse sort { - $a->count <=> $b->count - or $b->name cmp $a->name - } @tagc_cloud_arr; + @sorted_tagc_cloud_arr + = reverse sort { $a->count <=> $b->count or $b->name cmp $a->name } + @tagc_cloud_arr; - $self->stash( tcarr => \@sorted_tagc_cloud_arr ); - $self->render( template => 'tags/author_tags_read' ); + $self->stash(tcarr => \@sorted_tagc_cloud_arr); + $self->render(template => 'tags/author_tags_read'); } #################################################################################### sub get_authors_for_tag { - my $self = shift; - my $tag_id = $self->param('id'); + my $self = shift; + my $tag_id = $self->param('id'); - my $tag = $self->app->repo->tags_find( sub { $_->id == $tag_id } ); - if ( !defined $tag ) { - $self->render( text => "404. Tag '$tag_id' does not exist.", status => 404 ); + my $tag = $self->app->repo->tags_find(sub { $_->id == $tag_id }); + if (!defined $tag) { + $self->render(text => "404. Tag '$tag_id' does not exist.", status => 404); return; } @@ -288,13 +313,16 @@ sub get_authors_for_tag { my @subset_authors = map { $_->author } $paper->authorships_all; push @subset_authors, @authors; } - if ( !@authors ) { - $self->render( text => "There are no authors having papers with tag $tag_id.", status => 200 ); + if (!@authors) { + $self->render( + text => "There are no authors having papers with tag $tag_id.", + status => 200 + ); return; } - $self->stash( tag => $tag, authors => \@authors ); - $self->render( template => 'tags/authors_having_tag' ); + $self->stash(tag => $tag, authors => \@authors); + $self->render(template => 'tags/authors_having_tag'); } #################################################################################### @@ -302,31 +330,34 @@ sub delete { my $self = shift; my $id = $self->param('id'); - my $tag = $self->app->repo->tags_find( sub { $_->id == $id } ); + my $tag = $self->app->repo->tags_find(sub { $_->id == $id }); if ($tag) { my $name = $tag->name; ## TODO: refactor these blocks nicely! ## Deleting labelings my @labelings = $tag->labelings_all; + # for each entry, remove labeling in this team - foreach my $labeling ( @labelings ){ - $labeling->entry->remove_labeling($labeling); + foreach my $labeling (@labelings) { + $labeling->entry->remove_labeling($labeling); } $self->app->repo->labelings_delete(@labelings); + # remove all labelings for this team $tag->labelings_clear; + # finally delete tag $self->app->repo->tags_delete($tag); - $self->flash( msg_type => 'success', msg => "Tag $name has been deleted." ); + $self->flash(msg_type => 'success', msg => "Tag $name has been deleted."); $self->app->logger->info("Tag $name has been deleted."); } else { - $self->flash( msg_type => 'danger', msg => "Tag $id can not be deleted." ); + $self->flash(msg_type => 'danger', msg => "Tag $id can not be deleted."); } - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } #################################################################################### 1; diff --git a/lib/BibSpace/Controller/Tagtypes.pm b/lib/BibSpace/Controller/Tagtypes.pm index b76f5a1..4f2c3ac 100644 --- a/lib/BibSpace/Controller/Tagtypes.pm +++ b/lib/BibSpace/Controller/Tagtypes.pm @@ -4,13 +4,13 @@ use Data::Dumper; use utf8; use Text::BibTeX; # parsing bib files use DateTime; + # use File::Slurp; use v5.16; #because of ~~ use strict; use warnings; - use BibSpace::Functions::Core; use BibSpace::Model::TagType; @@ -20,105 +20,103 @@ use Mojo::Log; #################################################################################### sub index { - my $self = shift; - my @tag_types = $self->app->repo->tagTypes_all; - $self->render( template => 'tagtypes/tagtypes', tagtypes => \@tag_types ); + my $self = shift; + my @tag_types = $self->app->repo->tagTypes_all; + $self->render(template => 'tagtypes/tagtypes', tagtypes => \@tag_types); } #################################################################################### sub add { - my $self = shift; - $self->render( template => 'tagtypes/add' ); + my $self = shift; + $self->render(template => 'tagtypes/add'); } #################################################################################### sub add_post { - my $self = shift; - my $dbh = $self->app->db; - my $name = $self->param('new_name'); - my $comment = $self->param('new_comment'); - - my $tt = $self->app->repo->tagTypes_find( sub { $_->name eq $name } ); - - if ( defined $tt ) { - $self->flash( - msg_type => 'error', - msg => 'Tag type with such name already exists.' - ); - } - else{ - $tt = $self->app->entityFactory->new_TagType(name => $name, comment => $comment); - $self->app->repo->tagTypes_save($tt); - $self->flash( msg_type => 'success', msg => 'Tag type added.' ); - } - - $self->redirect_to( $self->url_for('all_tag_types') ); + my $self = shift; + my $dbh = $self->app->db; + my $name = $self->param('new_name'); + my $comment = $self->param('new_comment'); + + my $tt = $self->app->repo->tagTypes_find(sub { $_->name eq $name }); + + if (defined $tt) { + $self->flash( + msg_type => 'error', + msg => 'Tag type with such name already exists.' + ); + } + else { + $tt = $self->app->entityFactory->new_TagType( + name => $name, + comment => $comment + ); + $self->app->repo->tagTypes_save($tt); + $self->flash(msg_type => 'success', msg => 'Tag type added.'); + } + + $self->redirect_to($self->url_for('all_tag_types')); } #################################################################################### sub delete { - my $self = shift; - my $id = $self->param('id'); + my $self = shift; + my $id = $self->param('id'); - # we do not allow to delete the two first tag types! - if ( $id == 1 or $id == 2 ) { - $self->flash( - msg_type => 'error', - msg => 'Tag Types 1 or 2 are essential and cannot be deleted.' - ); - $self->redirect_to( $self->url_for('all_tag_types') ); - return; - } + # we do not allow to delete the two first tag types! + if ($id == 1 or $id == 2) { + $self->flash( + msg_type => 'error', + msg => 'Tag Types 1 or 2 are essential and cannot be deleted.' + ); + $self->redirect_to($self->url_for('all_tag_types')); + return; + } - my $tt = $self->app->repo->tagTypes_find( sub { $_->id == $id } ); + my $tt = $self->app->repo->tagTypes_find(sub { $_->id == $id }); - my @tags_of_tag_type = $self->app->repo->tags_filter( sub { $_->type == $id } ); - $self->app->repo->tags_delete(@tags_of_tag_type); - $self->app->repo->tagTypes_delete($tt); + my @tags_of_tag_type = $self->app->repo->tags_filter(sub { $_->type == $id }); + $self->app->repo->tags_delete(@tags_of_tag_type); + $self->app->repo->tagTypes_delete($tt); - $self->flash( msg_type => 'success', msg => 'Tag type deleted.' ); + $self->flash(msg_type => 'success', msg => 'Tag type deleted.'); - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } #################################################################################### sub edit { - my $self = shift; - my $id = $self->param('id'); - - my $name = $self->param('new_name'); - my $comment = $self->param('new_comment'); - my $saved = 0; - - - my $tt = $self->app->repo->tagTypes_find( sub { $_->id == $id } ); - - if ( !defined $tt ) { - $self->flash( - msg_type => 'error', - msg => 'Tag Type does not exist.' - ); - $self->redirect_to( $self->url_for('all_tag_types') ); - return; - } - - if ( defined $name or defined $comment ) { - $tt->name($name) if defined $name; - $tt->comment($comment) if defined $comment; - $self->app->repo->tagTypes_update($tt); - - $self->flash( msg_type => 'success', msg => 'Update successful.' ); - $self->redirect_to( $self->url_for('all_tag_types') ); - return; - } - else { - $self->flash( - msg_type => 'warning', - msg => 'No change made or empty input.' - ); - } - - $self->stash( obj => $tt ); - $self->render( template => 'tagtypes/edit' ); - + my $self = shift; + my $id = $self->param('id'); + + my $name = $self->param('new_name'); + my $comment = $self->param('new_comment'); + my $saved = 0; + + my $tt = $self->app->repo->tagTypes_find(sub { $_->id == $id }); + + if (!defined $tt) { + $self->flash(msg_type => 'error', msg => 'Tag Type does not exist.'); + $self->redirect_to($self->url_for('all_tag_types')); + return; + } + + if (defined $name or defined $comment) { + $tt->name($name) if defined $name; + $tt->comment($comment) if defined $comment; + $self->app->repo->tagTypes_update($tt); + + $self->flash(msg_type => 'success', msg => 'Update successful.'); + $self->redirect_to($self->url_for('all_tag_types')); + return; + } + else { + $self->flash( + msg_type => 'warning', + msg => 'No change made or empty input.' + ); + } + + $self->stash(obj => $tt); + $self->render(template => 'tagtypes/edit'); } #################################################################################### diff --git a/lib/BibSpace/Controller/Teams.pm b/lib/BibSpace/Controller/Teams.pm index 3e8e5d9..7f47143 100644 --- a/lib/BibSpace/Controller/Teams.pm +++ b/lib/BibSpace/Controller/Teams.pm @@ -2,6 +2,7 @@ package BibSpace::Controller::Teams; use utf8; use DateTime; + # use File::Slurp; use v5.16; #because of ~~ @@ -20,58 +21,62 @@ use Mojo::Base 'Mojolicious::Plugin::Config'; sub show { my $self = shift; my @teams = $self->app->repo->teams_all; - $self->stash( teams => \@teams ); - $self->render( template => 'teams/teams', layout => 'admin' ); + $self->stash(teams => \@teams); + $self->render(template => 'teams/teams', layout => 'admin'); } ################################################################################################################ sub edit { my $self = shift; my $id = $self->param('id'); - my $team = $self->app->repo->teams_find( sub { $_->id == $id } ); + my $team = $self->app->repo->teams_find(sub { $_->id == $id }); - if ( !defined $team ) { - $self->flash( msg => "There is no team with id $id", msg_type => 'danger' ); - $self->redirect_to( $self->get_referrer ); + if (!defined $team) { + $self->flash(msg => "There is no team with id $id", msg_type => 'danger'); + $self->redirect_to($self->get_referrer); return; } my @team_members = $team->get_members; - - $self->stash( members => \@team_members, team => $team ); - $self->render( template => 'teams/members' ); + $self->stash(members => \@team_members, team => $team); + $self->render(template => 'teams/members'); } ################################################################################################################ sub add_team { my $self = shift; - $self->render( template => 'teams/add_team' ); + $self->render(template => 'teams/add_team'); } ############################################################################################################## sub add_team_post { my $self = shift; my $new_team_name = $self->param('new_team'); + my $existing_mteam + = $self->app->repo->teams_find(sub { $_->name eq $new_team_name }); - my $existing_mteam = $self->app->repo->teams_find( sub { $_->name eq $new_team_name } ); - - if ( defined $existing_mteam ) { - $self->app->logger->info( "add new team: team with proposed name ($new_team_name) exists!!" ); - $self->flash( msg => "Team with such name exists already! Pick a different one." ); - $self->redirect_to( $self->url_for('add_team_get') ); + if (defined $existing_mteam) { + $self->app->logger->info( + "add new team: team with proposed name ($new_team_name) exists!!"); + $self->flash( + msg => "Team with such name exists already! Pick a different one."); + $self->redirect_to($self->url_for('add_team_get')); return; } my $new_mteam = $self->app->entityFactory->new_Team(name => $new_team_name); $self->app->repo->teams_save($new_mteam); my $new_team_id = $new_mteam->id; - if ( !defined $new_team_id or $new_team_id <= 0 ) { - $self->flash( msg => "Something went wrong. The Team $new_team_name has not been added." ); - $self->redirect_to( $self->url_for('add_team_get') ); + if (!defined $new_team_id or $new_team_id <= 0) { + $self->flash(msg => + "Something went wrong. The Team $new_team_name has not been added."); + $self->redirect_to($self->url_for('add_team_get')); return; } - $self->app->logger->info( "Add new team: Added new team with proposed name ($new_team_name). Team id is $new_team_id." ); - $self->redirect_to( 'edit_team', id => $new_team_id ); + $self->app->logger->info( + "Add new team: Added new team with proposed name ($new_team_name). Team id is $new_team_id." + ); + $self->redirect_to('edit_team', id => $new_team_id); } ############################################################################################################## @@ -79,19 +84,19 @@ sub delete_team { my $self = shift; my $id = $self->param('id'); - my $team = $self->app->repo->teams_find( sub { $_->id == $id } ); + my $team = $self->app->repo->teams_find(sub { $_->id == $id }); - if ( $team and $team->can_be_deleted ) { + if ($team and $team->can_be_deleted) { my $msg = "Team " . $team->name . " ID " . $team->id . " has been deleted"; - $self->app->logger->info( $msg ); - $self->flash( msg => $msg, msg_type => 'success' ); + $self->app->logger->info($msg); + $self->flash(msg => $msg, msg_type => 'success'); $self->do_delete_team($team); } else { - $self->flash( msg_type => 'warning', msg => "Unable to delete team id $id." ); + $self->flash(msg_type => 'warning', msg => "Unable to delete team id $id."); } - $self->redirect_to( $self->url_for('all_teams') ); + $self->redirect_to($self->url_for('all_teams')); } ############################################################################################################## @@ -100,18 +105,18 @@ sub delete_team_force { my $dbh = $self->app->db; my $id = $self->param('id'); - my $team = $self->app->repo->teams_find( sub { $_->id == $id } ); + my $team = $self->app->repo->teams_find(sub { $_->id == $id }); if ($team) { my $msg = "Team " . $team->name . " ID " . $team->id . " has been deleted"; - $self->app->logger->info( $msg ); - $self->flash( msg => $msg, msg_type => 'success' ); + $self->app->logger->info($msg); + $self->flash(msg => $msg, msg_type => 'success'); $self->do_delete_team($team); } - else{ - $self->flash( msg_type => 'warning', msg => "Unable to delete team id $id." ); + else { + $self->flash(msg_type => 'warning', msg => "Unable to delete team id $id."); } - $self->redirect_to( $self->url_for('all_teams') ); + $self->redirect_to($self->url_for('all_teams')); } ############################################################################################################## @@ -121,16 +126,17 @@ sub do_delete_team { ## Deleting memberships my @memberships = $team->memberships_all; + # for each team, remove membership in this team - foreach my $membership ( @memberships ){ - $membership->author->remove_membership($membership); + foreach my $membership (@memberships) { + $membership->author->remove_membership($membership); } $self->app->repo->memberships_delete(@memberships); + # remove all memberships for this team $team->memberships_clear; $self->app->repo->teams_delete($team); } ############################################################################################################## - 1; diff --git a/lib/BibSpace/Controller/Types.pm b/lib/BibSpace/Controller/Types.pm index baea45f..6c04ccc 100644 --- a/lib/BibSpace/Controller/Types.pm +++ b/lib/BibSpace/Controller/Types.pm @@ -2,7 +2,7 @@ package BibSpace::Controller::Types; use Data::Dumper; use utf8; -use v5.16; #because of ~~ +use v5.16; #because of ~~ use strict; use warnings; @@ -20,26 +20,26 @@ use Mojo::Log; sub all_our { my $self = shift; + my @types + = sort { $a->our_type cmp $b->our_type } $self->app->repo->types_all; - my @types = sort { $a->our_type cmp $b->our_type } $self->app->repo->types_all; - - $self->stash( otypes => \@types ); - $self->render( template => 'types/types' ); + $self->stash(otypes => \@types); + $self->render(template => 'types/types'); } #################################################################################### sub add_type { my $self = shift; - $self->render( template => 'types/add' ); + $self->render(template => 'types/add'); } #################################################################################### sub post_add_type { my $self = shift; my $new_type = $self->param('new_type'); - my $type = $self->app->entityFactory->new_Type(our_type => $new_type ); + my $type = $self->app->entityFactory->new_Type(our_type => $new_type); $self->app->repo->types_save($type); - $self->redirect_to( $self->url_for('all_types') ); + $self->redirect_to($self->url_for('all_types')); } #################################################################################### sub manage { @@ -47,19 +47,16 @@ sub manage { my $type_name = $self->param('name'); my @all = $self->app->repo->types_all; - my $type = $self->app->repo->types_find( sub { $_->our_type eq $type_name } ); - + my $type = $self->app->repo->types_find(sub { $_->our_type eq $type_name }); my @all_our_types = uniq map { $_->our_type } @all; my @all_bibtex_types = BibSpace::Functions::Core::official_bibtex_types; my @assigned_bibtex_types = $type->bibtexTypes_all; - # # cannot use objects as keysdue to stringification! my %types_hash = map { $_ => 1 } @assigned_bibtex_types; my @unassigned_btypes = grep { not $types_hash{$_} } @all_bibtex_types; - $self->stash( all_otypes => \@all_our_types, unassigned_btypes => \@unassigned_btypes, @@ -67,16 +64,17 @@ sub manage { assigned_btypes => \@assigned_bibtex_types, type => $type ); - $self->render( template => 'types/manage_types' ); + $self->render(template => 'types/manage_types'); } #################################################################################### sub toggle_landing { - my $self = shift; + my $self = shift; my $type_name = $self->param('name'); - my $type_obj = $self->app->repo->types_find( sub { $_->our_type eq $type_name } ); + my $type_obj + = $self->app->repo->types_find(sub { $_->our_type eq $type_name }); - if ( $type_obj->onLanding == 0 ) { + if ($type_obj->onLanding == 0) { $type_obj->onLanding(1); } else { @@ -84,36 +82,45 @@ sub toggle_landing { } $self->app->repo->types_update($type_obj); - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } #################################################################################### sub post_store_description { my $self = shift; my $type_name = $self->param('our_type'); my $description = $self->param('new_description'); - my $type_obj = $self->app->repo->types_find( sub { $_->our_type eq $type_name } ); + my $type_obj + = $self->app->repo->types_find(sub { $_->our_type eq $type_name }); - if ( defined $type_obj and defined $description ) { + if (defined $type_obj and defined $description) { $type_obj->description($description); $self->app->repo->types_update($type_obj); } - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } #################################################################################### sub delete_type { - my $self = shift; + my $self = shift; my $type_name = $self->param('name'); - my $type_obj = $self->app->repo->types_find( sub { $_->our_type eq $type_name } ); - if( $type_obj and $type_obj->can_be_deleted ){ - $self->app->repo->types_delete($type_obj); + my $type_obj + = $self->app->repo->types_find(sub { $_->our_type eq $type_name }); + if ($type_obj and $type_obj->can_be_deleted) { + $self->app->repo->types_delete($type_obj); - $self->flash( msg_type => 'success', message => "$type_name and its mappings have been deleted." ); + $self->flash( + msg_type => 'success', + message => "$type_name and its mappings have been deleted." + ); } - else{ - $self->flash( msg_type => 'warning', message => "$type_name cannot be deleted. Possible reasons: mappings exist or it is native bibtex type." ); + else { + $self->flash( + msg_type => 'warning', + message => + "$type_name cannot be deleted. Possible reasons: mappings exist or it is native bibtex type." + ); } - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } #################################################################################### sub map_types { @@ -121,11 +128,14 @@ sub map_types { my $o_type = $self->param('our_type'); my $b_type = $self->param('bibtex_type'); - my $type_obj = $self->app->repo->types_find( sub { $_->our_type eq $o_type } ); + my $type_obj = $self->app->repo->types_find(sub { $_->our_type eq $o_type }); - if ( !$o_type or !$b_type or !$type_obj ) { - $self->flash( message => "Cannot map. Incomplete input.", msg_type => 'danger' ); - $self->redirect_to( $self->get_referrer ); + if (!$o_type or !$b_type or !$type_obj) { + $self->flash( + message => "Cannot map. Incomplete input.", + msg_type => 'danger' + ); + $self->redirect_to($self->get_referrer); return; } @@ -135,16 +145,22 @@ sub map_types { if ($found) { $type_obj->bibtexTypes_add($b_type); $self->app->repo->types_update($type_obj); - $self->flash( message => "Mapping successful!", msg_type => 'success' ); + $self->flash(message => "Mapping successful!", msg_type => 'success'); } else { - $self->flash( message => "MAP ERROR: $b_type is not a valid bibtex type!", msg_type => 'danger' ); + $self->flash( + message => "MAP ERROR: $b_type is not a valid bibtex type!", + msg_type => 'danger' + ); } } else { - $self->flash( message => "Cannot map. Type not found.", msg_type => 'danger' ); + $self->flash( + message => "Cannot map. Type not found.", + msg_type => 'danger' + ); } - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } #################################################################################### @@ -153,29 +169,38 @@ sub unmap_types { my $o_type = $self->param('our_type'); my $b_type = $self->param('bibtex_type'); - my $type_obj = $self->app->repo->types_find( sub { $_->our_type eq $o_type } ); + my $type_obj = $self->app->repo->types_find(sub { $_->our_type eq $o_type }); - if ( !$b_type or !$type_obj ) { - $self->flash( message => "Cannot unmap. Incomplete input.", msg_type => 'danger' ); - $self->redirect_to( $self->get_referrer ); + if (!$b_type or !$type_obj) { + $self->flash( + message => "Cannot unmap. Incomplete input.", + msg_type => 'danger' + ); + $self->redirect_to($self->get_referrer); return; } elsif ($type_obj) { - my $idx_to_del = $type_obj->bibtexTypes_find_index( sub{ $_ eq $b_type } ); - if( $idx_to_del > -1 ){ - $type_obj->bibtexTypes_delete($idx_to_del); - $self->app->repo->types_update($type_obj); - $self->flash( message => "Unmapping successful!", msg_type => 'success' ); + my $idx_to_del = $type_obj->bibtexTypes_find_index(sub { $_ eq $b_type }); + if ($idx_to_del > -1) { + $type_obj->bibtexTypes_delete($idx_to_del); + $self->app->repo->types_update($type_obj); + $self->flash(message => "Unmapping successful!", msg_type => 'success'); } else { - $self->flash( message => "Unmap error: $b_type is not a valid bibtex type!", msg_type => 'danger' ); + $self->flash( + message => "Unmap error: $b_type is not a valid bibtex type!", + msg_type => 'danger' + ); } } else { - $self->flash( message => "Cannot unmap. Type not found.", msg_type => 'danger' ); + $self->flash( + message => "Cannot unmap. Type not found.", + msg_type => 'danger' + ); } - $self->redirect_to( $self->get_referrer ); + $self->redirect_to($self->get_referrer); } #################################################################################### diff --git a/lib/BibSpace/Converter/BibStyleConverter.pm b/lib/BibSpace/Converter/BibStyleConverter.pm index f48c9f1..c5bb93c 100644 --- a/lib/BibSpace/Converter/BibStyleConverter.pm +++ b/lib/BibSpace/Converter/BibStyleConverter.pm @@ -5,14 +5,13 @@ use BibSpace::Functions::Core; use List::MoreUtils qw(any uniq); - use Path::Tiny; use File::Spec; use Data::Dumper; use utf8; use Text::BibTeX; -use v5.16; +use v5.16; use Try::Tiny; use TeX::Encode; @@ -20,67 +19,64 @@ use Encode; use BibStyle::LocalBibStyle; - use Moose; use Moose::Util::TypeConstraints; use BibSpace::Util::ILogger; use BibSpace::Converter::IHtmlBibtexConverter; with 'IHtmlBibtexConverter'; -has 'logger' => ( is => 'ro', does => 'ILogger', required => 1 ); -has 'bst' => ( is => 'rw', isa => 'Maybe[Str]' ); -has 'html' => ( is => 'rw', isa => 'Maybe[Str]' ); +has 'logger' => (is => 'ro', does => 'ILogger', required => 1); +has 'bst' => (is => 'rw', isa => 'Maybe[Str]'); +has 'html' => (is => 'rw', isa => 'Maybe[Str]'); has 'warnings' => - ( is => 'rw', isa => 'Maybe[ArrayRef[Str]]', default => sub { [] } ); - + (is => 'rw', isa => 'Maybe[ArrayRef[Str]]', default => sub { [] }); sub set_template { - my ( $self, $template ) = @_; - $self->bst($template); + my ($self, $template) = @_; + $self->bst($template); } sub convert { - my ( $self, $bib, $bst ) = @_; - $bst ||= $self->bst; - die "Template not provided" unless $bst and -e $bst; - - - my ( $bbl_dirty, $dirty_bbl_array_ref, $warnings_arr_ref ) - = _convert_bib_to_bbl( $bib, $bst ); - $self->warnings($warnings_arr_ref); - # stateless call - my $clean_bbl = _clean_bbl($dirty_bbl_array_ref); - my $html_code = _add_html_links( $clean_bbl, $bib ); - $self->html($html_code); + my ($self, $bib, $bst) = @_; + $bst ||= $self->bst; + die "Template not provided" unless $bst and -e $bst; + + my ($bbl_dirty, $dirty_bbl_array_ref, $warnings_arr_ref) + = _convert_bib_to_bbl($bib, $bst); + $self->warnings($warnings_arr_ref); + + # stateless call + my $clean_bbl = _clean_bbl($dirty_bbl_array_ref); + my $html_code = _add_html_links($clean_bbl, $bib); + $self->html($html_code); } sub get_html { - my $self = shift; - $self->html; + my $self = shift; + $self->html; } sub get_warnings { - my $self = shift; - return @{ $self->warnings }; + my $self = shift; + return @{$self->warnings}; } #################################################################################### sub _convert_bib_to_bbl { my ($input_bib, $bst_file_path) = @_; - my $bibstyle = BibStyle::LocalBibStyle->new(); #Text::BibTeX::BibStyle->new(); die "Cannot find bst file under: $bst_file_path ." if !-e $bst_file_path; $bibstyle->read_bibstyle($bst_file_path); my $bbl = $bibstyle->execute([], $input_bib); - my $out = $bibstyle->get_output(); - + my $out = $bibstyle->get_output(); + my @bibstyle_output = @{$bibstyle->{output}}; my $warnings_arr_ref = $bibstyle->{warnings}; - my $bbl_dirty = join '', @bibstyle_output ; - my $dirty_bbl_array_ref = \@bibstyle_output ; + my $bbl_dirty = join '', @bibstyle_output; + my $dirty_bbl_array_ref = \@bibstyle_output; return ($bbl_dirty, $dirty_bbl_array_ref, $warnings_arr_ref); } @@ -93,26 +89,28 @@ sub _clean_bbl { my @arr = @{$bbl_arr_ref}; my @useful_lines; - foreach my $f (@arr){ + foreach my $f (@arr) { chomp $f; - # fix strange commas - # before: J\'{o}akim von Kistowski, , Hansfried Block, , John Beckett, , Cloyce Spradling, , Klaus-Dieter Lange, , and Samuel Kounev, . - # after: J\'{o}akim von Kistowski, Hansfried Block, John Beckett, Cloyce Spradling, Klaus-Dieter Lange, and Samuel Kounev. +# fix strange commas +# before: J\'{o}akim von Kistowski, , Hansfried Block, , John Beckett, , Cloyce Spradling, , Klaus-Dieter Lange, , and Samuel Kounev, . +# after: J\'{o}akim von Kistowski, Hansfried Block, John Beckett, Cloyce Spradling, Klaus-Dieter Lange, and Samuel Kounev. $f =~ s/(\w+),\s+,/$1,/g; $f =~ s/(\w+),\s+([\.,])/$1$2/g; - - if($f =~ m/^\\begin/ or $f =~ m/^\\end/){ + + if ($f =~ m/^\\begin/ or $f =~ m/^\\end/) { + # say "BB".$f; } - elsif($f =~ m/\\bibitem/){ + elsif ($f =~ m/\\bibitem/) { + # say "II".$f; push @useful_lines, $f; } - elsif($f =~ m/^\s*$/){ # line containing only whitespaces + elsif ($f =~ m/^\s*$/) { # line containing only whitespaces ; } - else{ + else { push @useful_lines, $f; } } @@ -120,35 +118,37 @@ sub _clean_bbl { my $useful_str = join '', @useful_lines; my $s = $useful_str; - # say "\nXXXX1\n".$s."\nXXXX\n"; - $s =~ s/\\newblock/\n\\newblock/g; # every newblock = newline in bbl (but not in html!) - $s =~ s/\\bibitem\{([^\}]*)\}/\\bibitem\{$1\}\n/; # new line after the bibtex key + $s =~ s/\\newblock/\n\\newblock/g + ; # every newblock = newline in bbl (but not in html!) + $s =~ s/\\bibitem\{([^\}]*)\}/\\bibitem\{$1\}\n/ + ; # new line after the bibtex key - my ($bibtex_key, $rest) = $s =~ m/\\bibitem\{([^\}.]*)\}(.*)/; # match until the first closing bracket - # extract the bibtex key and the rest - just in case you need it + my ($bibtex_key, $rest) + = $s + =~ m/\\bibitem\{([^\}.]*)\}(.*)/; # match until the first closing bracket + # extract the bibtex key and the rest - just in case you need it - $s =~ s/\\bibitem\{([^\}]*)\}\n?//; #remove first line with bibitem - $s =~ s/\\newblock\s+//g; # remove newblocks + $s =~ s/\\bibitem\{([^\}]*)\}\n?//; #remove first line with bibitem + $s =~ s/\\newblock\s+//g; # remove newblocks +# nested parenthesis cannot be handled with regexp :( +# I use this because it counts brackets! +# string_replace_with_counting($s, $opening, $closing, $avoid_l, $avoid_r, $opening_replace, $closing_replace) + $s = string_replace_with_counting($s, '{\\em', '}', '{', '}', + '', ''); - # nested parenthesis cannot be handled with regexp :( - # I use this because it counts brackets! - # string_replace_with_counting($s, $opening, $closing, $avoid_l, $avoid_r, $opening_replace, $closing_replace) - $s = string_replace_with_counting($s, '{\\em', '}', '{', '}', '', ''); - # if there are more (what is very rare), just ignore $s = string_replace_with_counting($s, '{\\em', '}', '{', '}', '', ''); - # find all that is between {}, count all pairs of {} replace the outermost with nothing - # does {zzz {aaa} ggg} => zzz {aaa} ggg - - $s = string_replace_with_counting($s, '\\url{', '}', '{', '}', '', ''); +# find all that is between {}, count all pairs of {} replace the outermost with nothing +# does {zzz {aaa} ggg} => zzz {aaa} ggg + $s = string_replace_with_counting($s, '\\url{', '}', '{', '}', + '', ''); - - # and here are the custom replacement functions in case something goes wrong... + # and here are the custom replacement functions in case something goes wrong... $s = german_letters_latex_to_html($s); $s = polish_letters_latex_to_html($s); $s = other_letters_latex_to_html($s); @@ -157,25 +157,24 @@ sub _clean_bbl { my $new_s = ""; $new_s = string_replace_with_counting($s, '{', '}', '{', '}', '', ''); - while($new_s ne $s){ + while ($new_s ne $s) { $s = $new_s; $new_s = string_replace_with_counting($s, '{', '}', '{', '}', '', ''); } - $s = str_replace_as_pod_latex($s); # this should catch everything but it doesn't + $s = str_replace_as_pod_latex($s) + ; # this should catch everything but it doesn't - $s =~ s!\\%!%!g; # replace % escape - $s =~ s!\\&!&!g; # replace & escape + $s =~ s!\\%!%!g; # replace % escape + $s =~ s!\\&!&!g; # replace & escape return $s; } - - #################################################################################### sub _add_html_links { my ($bbl_clean, $bib) = @_; - + my $s = $bbl_clean; my $entry = new Text::BibTeX::Entry(); @@ -193,106 +192,113 @@ sub _add_html_links { my @code = (); - if($entry->exists('pdf')){ + if ($entry->exists('pdf')) { push @code, build_link('pdf', $entry->get('pdf')); } - if($entry->exists('slides')){ + if ($entry->exists('slides')) { push @code, build_link('slides', $entry->get('slides')); } - if($entry->exists('doi')){ - push @code, build_link('DOI', "http://dx.doi.org/".$entry->get('doi')); + if ($entry->exists('doi')) { + push @code, build_link('DOI', "http://dx.doi.org/" . $entry->get('doi')); } - if($entry->exists('url')){ + if ($entry->exists('url')) { push @code, build_link('http', $entry->get('url')); } my $abstract_preview_a; my $abstract_preview_div; - if($entry->exists('abstract')){ + if ($entry->exists('abstract')) { my $content = $entry->get('abstract'); - # $abstract_preview_a = 'abstract'; - $abstract_preview_a = 'abstract'; - # $abstract_preview_div = ''; - $abstract_preview_div = '