From 15f4597637cdd6e041fce61a186145cc93bb96b1 Mon Sep 17 00:00:00 2001 From: Erik Thuning <boooink@gmail.com> Date: Mon, 2 May 2022 15:21:56 +0200 Subject: [PATCH 01/18] Fixed formatting bugs in the outgoing emails --- include/Cron.php | 48 +++++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/include/Cron.php b/include/Cron.php index ad6d26b..eeb26bd 100644 --- a/include/Cron.php +++ b/include/Cron.php @@ -9,8 +9,8 @@ class Cron { $this->now = new DateTimeImmutable(); $this->sender = $sender; $this->error = $error; - $warn_time = DateInterval::createFromDateString('3 days'); - $this->warn_date = $this->now->add($warn_time); + $this->warn_time = DateInterval::createFromDateString('3 days'); + $this->warn_date = $this->now->add($this->warn_time); $this->run_interval = DateInterval::createFromDateString('1 day'); $this->kvs = new Kvs(); $this->ldap = new Ldap(); @@ -58,7 +58,7 @@ class Cron { if(!$expiring) { return ''; } - $days = $this->warn_date->d; + $days = $this->warn_time->d; switch($lang) { case 'sv': $msg = "Följande lån går ut om mindre än ".$days." dagar:"; @@ -76,16 +76,16 @@ class Cron { default: throw new Exception("Invalid language: ".$lang); } - $msg .= "\n\n"; + $lines = array(); foreach($expiring as $loan) { $product = $loan->get_product(); $serial = $product->get_serial(); $brand = $product->get_brand(); $name = $product->get_name(); $endtime = format_date($loan->get_endtime()); - $msg .= $serial.": ".$brand." ".$name.$itemglue.$endtime; + $lines[] = $serial.": ".$brand." ".$name.$itemglue.$endtime; } - return $msg; + return $msg."\n\n".implode("\n", $lines); } private function make_overdue_notice($lang, $overdue) { @@ -108,16 +108,16 @@ class Cron { default: throw new Exception("Invalid language: ".$lang); } - $msg .= "\n\n"; + $lines = array(); foreach($overdue as $loan) { $product = $loan->get_product(); $serial = $product->get_serial(); $brand = $product->get_brand(); $name = $product->get_name(); $endtime = format_date($loan->get_endtime()); - $msg .= $serial.": ".$brand." ".$name.$itemglue.$endtime; + $lines[] = $serial.": ".$brand." ".$name.$itemglue.$endtime; } - return $msg; + return $msg."\n\n".implode("\n", $lines); } private function make_return_info($lang, $count) { @@ -148,25 +148,31 @@ class Cron { } - private function send_reminder($user, $expiring, $expired) { + private function send_reminder($user, $expiring, $overdue) { $uid = $user->get_name(); $name = $this->ldap->get_firstname($uid); - $subject = $this->make_subject(count($expiring), count($expired)); + $expiring_count = count($expiring); + $overdue_count = count($overdue); + $total = $expiring_count + $overdue_count; + + $subject = $this->make_subject($expiring_count, $overdue_count); $info_sv = array(); - $info_sv[] = $this->make_expiring_notice('sv', $expiring); - $info_sv[] = $this->make_overdue_notice('sv', $expired); - $info_sv = implode("\n\n", $info_sv); - $returns_sv = $this->make_return_info( - 'sv', count($expiring) + count($expired)); - $info_en = array(); - $info_en[] = $this->make_expiring_notice('en', $expiring); - $info_en[] = $this->make_overdue_notice('en', $expired); + if($expiring_count > 0) { + $info_sv[] = $this->make_expiring_notice('sv', $expiring); + $info_en[] = $this->make_expiring_notice('en', $expiring); + } + if($overdue_count > 0) { + $info_sv[] = $this->make_overdue_notice('sv', $overdue); + $info_en[] = $this->make_overdue_notice('en', $overdue); + } + $info_sv = implode("\n\n", $info_sv); + $returns_sv = $this->make_return_info('sv', $total); + $info_en = implode("\n\n", $info_en); - $returns_en = $this->make_return_info( - 'en', count($expiring) + count($expired)); + $returns_en = $this->make_return_info('en', $total); $message = <<<EOF Hej $name! From 79d9f45c383a061ad1899a4bd14c1145deb25b1d Mon Sep 17 00:00:00 2001 From: Erik Thuning <boooink@gmail.com> Date: Mon, 16 May 2022 13:49:22 +0200 Subject: [PATCH 02/18] Refactored the reminder generaion --- include/Cron.php | 106 +++++++++++++++++++++++------------------------ 1 file changed, 52 insertions(+), 54 deletions(-) diff --git a/include/Cron.php b/include/Cron.php index eeb26bd..a8a5159 100644 --- a/include/Cron.php +++ b/include/Cron.php @@ -14,6 +14,38 @@ class Cron { $this->run_interval = DateInterval::createFromDateString('1 day'); $this->kvs = new Kvs(); $this->ldap = new Ldap(); + + $days = $this->warn_time->d; + $this->strings = array( + 'en' => array( + 'expiring' => array( + 'single' => "The following loan expires in less than $days days:", + 'multi' => "The following loans expire in less than $days days:", + 'expiry' => "expires on", + 'serial' => "serial number", + ), + 'overdue' => array( + 'single' => "The following loan has expired:", + 'multi' => "The following loans have expired:", + 'expiry' => "expired on", + 'serial' => "serial number", + ), + ), + 'sv' => array( + 'expiring' => array( + 'single' => "Följande lån går ut om mindre än $days dagar:", + 'multi' => "Följande lån går ut om mindre än $days dagar:", + 'expiry' => "går ut", + 'serial' => "artikelnummer", + ), + 'overdue' => array( + 'single' => "Följande lån har gått ut:", + 'multi' => "Följande lån har gått ut:", + 'expiry' => "gick ut", + 'serial' => "artikelnummer", + ), + ), + ); } public function run() { @@ -54,68 +86,34 @@ class Cron { return $subject.implode(" och ", $messages); } - private function make_expiring_notice($lang, $expiring) { - if(!$expiring) { + private function make_notice($lang, $type, $list) { + if(!$list) { return ''; } - $days = $this->warn_time->d; - switch($lang) { - case 'sv': - $msg = "Följande lån går ut om mindre än ".$days." dagar:"; - $itemglue = ", går ut "; - break; - case 'en': - if(count($expiring) == 1) { - $loanstr = "loan expires"; - } else { - $loanstr = "loans expire"; - } - $msg = "The following ".$loanstr." in less than ".$days." days:"; - $itemglue = ", expires on "; - break; - default: - throw new Exception("Invalid language: ".$lang); + if(!array_key_exists($lang, $this->strings)) { + throw new Exception("Invalid languange: $lang"); } - $lines = array(); - foreach($expiring as $loan) { - $product = $loan->get_product(); - $serial = $product->get_serial(); - $brand = $product->get_brand(); - $name = $product->get_name(); - $endtime = format_date($loan->get_endtime()); - $lines[] = $serial.": ".$brand." ".$name.$itemglue.$endtime; + $strings = $this->strings[$lang]; + if(!array_key_exists($type, $strings)) { + throw new Exception("Invalid type: $type"); } - return $msg."\n\n".implode("\n", $lines); - } + $strings = $strings[$type]; - private function make_overdue_notice($lang, $overdue) { - if(!$overdue) { - return ''; - } - switch($lang) { - case 'sv': - $msg = "Följande lån har gått ut:"; - $itemglue = ", gick ut "; - break; - case 'en': - if(count($overdue) == 1) { - $msg = "The following loan has expired:"; - } else { - $msg = "The following loans have expired:"; - } - $itemglue = ", expired on "; - break; - default: - throw new Exception("Invalid language: ".$lang); + $msg = $strings['single']; + if(count($list) > 1) { + $msg = $strings['multi']; } + $lines = array(); - foreach($overdue as $loan) { + foreach($list as $loan) { $product = $loan->get_product(); $serial = $product->get_serial(); $brand = $product->get_brand(); $name = $product->get_name(); $endtime = format_date($loan->get_endtime()); - $lines[] = $serial.": ".$brand." ".$name.$itemglue.$endtime; + + $lines[] = "$brand $name, ".$strings['serial'] + ." $serial, ".$strings['expiry']." $endtime"; } return $msg."\n\n".implode("\n", $lines); } @@ -161,12 +159,12 @@ class Cron { $info_sv = array(); $info_en = array(); if($expiring_count > 0) { - $info_sv[] = $this->make_expiring_notice('sv', $expiring); - $info_en[] = $this->make_expiring_notice('en', $expiring); + $info_sv[] = $this->make_notice('sv', 'expiring', $expiring); + $info_en[] = $this->make_notice('en', 'expiring', $expiring); } if($overdue_count > 0) { - $info_sv[] = $this->make_overdue_notice('sv', $overdue); - $info_en[] = $this->make_overdue_notice('en', $overdue); + $info_sv[] = $this->make_notice('sv', 'overdue', $overdue); + $info_en[] = $this->make_notice('en', 'overdue', $overdue); } $info_sv = implode("\n\n", $info_sv); $returns_sv = $this->make_return_info('sv', $total); From c1ba468807b5ceb9201c5b08888857e70055d928 Mon Sep 17 00:00:00 2001 From: Erik Thuning <boooink@gmail.com> Date: Tue, 19 Jul 2022 15:27:31 +0200 Subject: [PATCH 03/18] Whitespace cleanup --- include/User.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/User.php b/include/User.php index a34ca03..ba218b4 100644 --- a/include/User.php +++ b/include/User.php @@ -3,7 +3,7 @@ class User extends Entity { private $id = 0; private $name = ''; private $notes = ''; - + public static function create_user($name) { $ins_user = prepare('insert into `user`(`name`) values (?)'); bind($ins_user, 's', $name); @@ -34,7 +34,7 @@ class User extends Entity { $this->id = $id; $this->update_fields(); } - + private function update_fields() { $get = prepare('select * from `user` where `id`=?'); bind($get, 'i', $this->id); @@ -125,11 +125,11 @@ class User extends Entity { public function get_id() { return $this->id; } - + public function get_name() { return $this->name; } - + public function set_name($newname) { $update = prepare('update `user` set `name`=? where `id`=?'); bind($update, 'si', $newname, $this->id); @@ -141,7 +141,7 @@ class User extends Entity { public function get_notes() { return $this->notes; } - + public function set_notes($newnotes) { $update = prepare('update `user` set `notes`=? where `id`=?'); bind($update, 'si', $newnotes, $this->id); @@ -151,7 +151,7 @@ class User extends Entity { } public function get_loans($type = 'both') { - $statement = "select `id` from `event` + $statement = "select `id` from `event` left join `loan` on `event`.`id` = `loan`.`event` where `type`='loan' and `user`=?"; From 396a3f067e2394b288a87fef6c07e44c90a6734b Mon Sep 17 00:00:00 2001 From: Erik Thuning <boooink@gmail.com> Date: Tue, 19 Jul 2022 15:29:58 +0200 Subject: [PATCH 04/18] Config item $reminder_sender changed to $sender --- config.php.example | 2 +- cron.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config.php.example b/config.php.example index 982e551..9a6fbb8 100644 --- a/config.php.example +++ b/config.php.example @@ -7,7 +7,7 @@ $db_pass = 'dbpassword'; $db_name = 'dbuser'; # Address to use as the sender for reminder emails -$reminder_sender = 'noreply@example.com'; +$sender = 'noreply@example.com'; # Address to send cron error messages to $error_address = 'root@example.com'; diff --git a/cron.php b/cron.php index d2170df..5e52909 100755 --- a/cron.php +++ b/cron.php @@ -7,7 +7,7 @@ require('./include/functions.php'); header('Content-Type: text/html; charset=UTF-8'); -$cron = new Cron($reminder_sender, $error_address); +$cron = new Cron($sender, $error_address); $cron->run(); ?> From a16e5f2479b45db07e92f3bc253750302d6e7c3c Mon Sep 17 00:00:00 2001 From: Erik Thuning <boooink@gmail.com> Date: Tue, 19 Jul 2022 15:48:35 +0200 Subject: [PATCH 05/18] Whitespace cleanup --- include/Event.php | 6 +++--- include/Loan.php | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/include/Event.php b/include/Event.php index 865a5d3..711bb5b 100644 --- a/include/Event.php +++ b/include/Event.php @@ -44,7 +44,7 @@ class Event { $event_id = $insert->insert_id; return new Event($event_id); } - + public function __construct($id) { $search = prepare('select `id` from `event` where `id`=?'); @@ -57,7 +57,7 @@ class Event { $this->id = $result['id']; $this->update_fields(); } - + protected function update_fields() { $get = prepare('select * from `event` where `id`=?'); bind($get, 'i', $this->id); @@ -83,7 +83,7 @@ class Event { public function get_returntime() { return $this->returntime; } - + public function is_active() { if($this->returntime === null) { return true; diff --git a/include/Loan.php b/include/Loan.php index 08311b7..fe8346b 100644 --- a/include/Loan.php +++ b/include/Loan.php @@ -15,7 +15,7 @@ class Loan extends Event { commit_trans(); return new Loan($event_id); } - + public function __construct($id) { parent::__construct($id); $search = prepare('select * from `loan` where `event`=?'); @@ -27,7 +27,7 @@ class Loan extends Event { } $this->update_fields(); } - + protected function update_fields() { parent::update_fields(); $get = prepare('select * from `loan` where `event`=?'); From 04af074849a7b602d9c58b440e4115376c2d6c34 Mon Sep 17 00:00:00 2001 From: Erik Thuning <boooink@gmail.com> Date: Tue, 19 Jul 2022 16:57:42 +0200 Subject: [PATCH 06/18] Whitespace cleanup --- include/Ajax.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/Ajax.php b/include/Ajax.php index 4ede200..1fa756d 100644 --- a/include/Ajax.php +++ b/include/Ajax.php @@ -1,14 +1,14 @@ <?php class Ajax extends Responder { private $action = ''; - + public function __construct() { parent::__construct(); if(isset($_GET['action'])) { $this->action = $_GET['action']; } } - + public function render() { $out = ''; switch($this->action) { @@ -98,7 +98,7 @@ class Ajax extends Responder { return new Failure('Artikeln är redan utlånad.'); } } - + private function return_product() { $product = null; try { @@ -138,7 +138,7 @@ class Ajax extends Responder { } return new Failure('Lån saknas.'); } - + private function start_inventory() { try { Inventory::begin(); @@ -147,7 +147,7 @@ class Ajax extends Responder { return new Failure('Inventering redan igång.'); } } - + private function end_inventory() { $inventory = Inventory::get_active(); if($inventory === null) { @@ -156,7 +156,7 @@ class Ajax extends Responder { $inventory->end(); return new Success('Inventering avslutad.'); } - + private function inventory_product() { $inventory = Inventory::get_active(); if($inventory === null) { @@ -273,7 +273,7 @@ class Ajax extends Responder { } return new Success('Ändringarna sparade.'); } - + private function update_user() { $id = $_POST['id']; $name = $_POST['name']; @@ -355,7 +355,7 @@ class Ajax extends Responder { return new Failure('Det finns ingen mall med det namnet.'); } } - + private function suggest() { return new Success(suggest($_POST['type'])); } From 311402e1b894a74cac0af05941fba5ac8867ca0e Mon Sep 17 00:00:00 2001 From: Erik Thuning <boooink@gmail.com> Date: Tue, 19 Jul 2022 17:00:08 +0200 Subject: [PATCH 07/18] Initial implementation of loan receipts --- include/Cron.php | 105 ++++++++++++++++++++++++++++++++++++++++--- include/Loan.php | 16 ++++++- pending_receipts.sql | 9 ++++ 3 files changed, 122 insertions(+), 8 deletions(-) create mode 100644 pending_receipts.sql diff --git a/include/Cron.php b/include/Cron.php index a8a5159..66e65d2 100644 --- a/include/Cron.php +++ b/include/Cron.php @@ -18,6 +18,10 @@ class Cron { $days = $this->warn_time->d; $this->strings = array( 'en' => array( + 'new' => array( + 'expiry' => "expires on", + 'serial' => "serial number", + ), 'expiring' => array( 'single' => "The following loan expires in less than $days days:", 'multi' => "The following loans expire in less than $days days:", @@ -32,6 +36,10 @@ class Cron { ), ), 'sv' => array( + 'new' => array( + 'expiry' => "går ut", + 'serial' => "artikelnummer", + ), 'expiring' => array( 'single' => "Följande lån går ut om mindre än $days dagar:", 'multi' => "Följande lån går ut om mindre än $days dagar:", @@ -49,6 +57,81 @@ class Cron { } public function run() { + $this->run_receipts(); + $this->run_reminders(); + } + + private function run_receipts() { + begin_trans(); + $get = prepare('select * from `pending_receipt` + where `send_time` < ?'); + bind($get, 'i', $this->now->getTimestamp()); + execute($get); + foreach(result_list($get) as $row) { + $user = new User($row['user']); + $since_time = $row['since_time']; + + $notify_loans = array(); + foreach($user->get_loans('active') as $loan) { + if($loan->get_starttime() >= $since_time) { + $notify_loans[] = $loan; + } + } + $this->send_receipt($user, $loans); + $delete = prepare('delete from `pending_receipt` + where `user = ? and `send_time` < ?'); + bind($delete, 'ii', $user->get_id(), $this->now->getTimestamp()); + execute($delete); + } + commit_trans(); + } + + private function send_receipt($user, $loans) { + $count = count($loans); + if($count > 1) { + $new_sv = "nya lån"; + $new_en = "new loans"; + $have = "have"; + } else { + $new_sv = "nytt lån"; + $new_en = "new loan"; + $have = "has"; + } + $subject = "DSV Helpdesk: $count $new_sv"; + + $intro_sv = ""; + $intro_en = "$count $new_en $have been registered to your user:"; + + $list_sv = $this->make_notice('sv', 'new', $loans); + $list_en = $this->make_notice('en', 'new', $loans); + + $message = <<<EOF +Hej $name! + +Det här är ett automatiskt meddelande om att $count $new_sv har registrerats på din användare: + +$list_sv + +Eventuella artiklar du inte hämtat ut redan kan hämtas från Helpdesk. Svara på det här mailet om du har några frågor. + +---- + +This is an automated message to inform you that $count $new_en $have been registered in your name: + +$list_en + +Any products you haven't already picked up can be collected from Helpdesk. Please reply to this email if you have any questions. + +Mvh +DSV Helpdesk +helpdesk@dsv.su.se +08 - 16 16 48 + +EOF; + $this->send_email($uid, $subject, $message); + } + + private function run_reminders() { $lastrun = $this->kvs->get_value('lastrun', 0); $nextrun = $this->now ->setTimestamp($lastrun) @@ -99,11 +182,6 @@ class Cron { } $strings = $strings[$type]; - $msg = $strings['single']; - if(count($list) > 1) { - $msg = $strings['multi']; - } - $lines = array(); foreach($list as $loan) { $product = $loan->get_product(); @@ -115,6 +193,15 @@ class Cron { $lines[] = "$brand $name, ".$strings['serial'] ." $serial, ".$strings['expiry']." $endtime"; } + + if($type === 'new') { + return implode("\n", $lines); + } + + $msg = $strings['single']; + if(count($list) > 1) { + $msg = $strings['multi']; + } return $msg."\n\n".implode("\n", $lines); } @@ -195,6 +282,10 @@ helpdesk@dsv.su.se 08 - 16 16 48 EOF; + $this->send_email($uid, $subject, $message); + } + + private function send_email($uid, $subject, $message) { try { mb_send_mail($this->ldap->get_user_email($uid), $subject, @@ -203,8 +294,8 @@ EOF; } catch(Exception $e) { error_log($e->message); mb_send_mail($this->error, - "Kunde inte skicka påminnelse", - "Påminnelse kunde inte skickas till ".$uid); + "Kunde inte skicka mail", + "Mail kunde inte skickas till ".$uid); } } } diff --git a/include/Loan.php b/include/Loan.php index fe8346b..cb7f0ae 100644 --- a/include/Loan.php +++ b/include/Loan.php @@ -12,8 +12,22 @@ class Loan extends Event { $endtime .= '13:00'; bind($insert, 'iii', $event_id, $user->get_id(), strtotime($endtime)); execute($insert); + $loan = new Loan($event_id); + + $now = time(); + $pending = prepare('select * from `pending_receipt` where `user` = ? + and `since_time` < ? and `send_time` > ?'); + bind($pending, 'iii', $user->get_id(), $now, $now); + execute($pending); + if(result_single($pending) === null) { + $add = prepare('insert into `pending_receipt` + (`user`, `since_time`, `send_time`) + values(?, ?, ?)'); + bind($add, 'iii', $user->get_id(), $now, $now + 3600); + execute($add); + } commit_trans(); - return new Loan($event_id); + return $loan; } public function __construct($id) { diff --git a/pending_receipts.sql b/pending_receipts.sql new file mode 100644 index 0000000..550da5b --- /dev/null +++ b/pending_receipts.sql @@ -0,0 +1,9 @@ +create table `pending_receipt` ( + `user` bigint(20) not null, + primary key (`user`), + constraint `pr_f_user` + foreign key(`user`) references `user`(`id`), + `send_time` bigint(20) not null, + `since_time` bigint(20) not null +) character set utf8mb4, + collate utf8mb4_unicode_ci; From cd627f811d8d894843df06702d397bfd73fa91d2 Mon Sep 17 00:00:00 2001 From: Erik Thuning <boooink@gmail.com> Date: Wed, 20 Jul 2022 10:41:35 +0200 Subject: [PATCH 08/18] Made it work --- include/Cron.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/include/Cron.php b/include/Cron.php index 66e65d2..3303ff5 100644 --- a/include/Cron.php +++ b/include/Cron.php @@ -77,9 +77,9 @@ class Cron { $notify_loans[] = $loan; } } - $this->send_receipt($user, $loans); + $this->send_receipt($user, $notify_loans); $delete = prepare('delete from `pending_receipt` - where `user = ? and `send_time` < ?'); + where `user` = ? and `send_time` < ?'); bind($delete, 'ii', $user->get_id(), $this->now->getTimestamp()); execute($delete); } @@ -87,6 +87,9 @@ class Cron { } private function send_receipt($user, $loans) { + $uid = $user->get_name(); + $name = $this->ldap->get_firstname($uid); + $count = count($loans); if($count > 1) { $new_sv = "nya lån"; @@ -292,7 +295,7 @@ EOF; $message, 'From: '.$this->sender); } catch(Exception $e) { - error_log($e->message); + error_log($e->getMessage()); mb_send_mail($this->error, "Kunde inte skicka mail", "Mail kunde inte skickas till ".$uid); From 0af33c5dd660c510e992b2274de9f781bf1fbb2a Mon Sep 17 00:00:00 2001 From: Erik Thuning <boooink@gmail.com> Date: Tue, 26 Jul 2022 10:23:09 +0200 Subject: [PATCH 09/18] Added buttons to quickly change the runtime of a loan --- html/fragments.html | 5 +++++ script.js | 19 ++++++++++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/html/fragments.html b/html/fragments.html index 1e0e633..9dbb066 100644 --- a/html/fragments.html +++ b/html/fragments.html @@ -610,6 +610,11 @@ Låna ut </button> <br/> + <label>Löptid:</label> + <button onClick="JavaScript:loanLength(event, 7)">1 vecka</button> + <button onClick="JavaScript:loanLength(event, 365)">1 år</button> + <button onClick="JavaScript:loanLength(event, 1095)">3 år</button> + <br/> <label for="end">Slutdatum:</label> <input type="text" id="end" diff --git a/script.js b/script.js index a708d23..d79d1d6 100644 --- a/script.js +++ b/script.js @@ -97,7 +97,7 @@ function getFragment(name, callback) { console.log(result); } } - + var data = new FormData() data.append('fragment', name) ajaxRequest('getfragment', data, unpack) @@ -573,3 +573,20 @@ function showFile(event) { var filefield = event.currentTarget.parentNode.filename filefield.value = event.currentTarget.files[0].name } + +function loanLength(event, days) { + event.preventDefault() + var end = document.getElementById('end') + var enddate = new Date() + enddate.setDate(enddate.getDate() + days) + console.log(enddate) + var month = enddate.getMonth() + if(month < 10) { + month = '0' + month + } + var day = enddate.getDay() + if(day < 10) { + day = '0' + day + } + end.value = enddate.getFullYear() + '-' + month + '-' + day +} From dbe2a0ee9070583a2591818e19faa3a6a91870b4 Mon Sep 17 00:00:00 2001 From: Erik Thuning <boooink@gmail.com> Date: Tue, 26 Jul 2022 10:24:16 +0200 Subject: [PATCH 10/18] Removed debug line --- script.js | 1 - 1 file changed, 1 deletion(-) diff --git a/script.js b/script.js index d79d1d6..2dc75e4 100644 --- a/script.js +++ b/script.js @@ -579,7 +579,6 @@ function loanLength(event, days) { var end = document.getElementById('end') var enddate = new Date() enddate.setDate(enddate.getDate() + days) - console.log(enddate) var month = enddate.getMonth() if(month < 10) { month = '0' + month From 1d0caf95134f9149428d3f41eb012a34b7a6bf54 Mon Sep 17 00:00:00 2001 From: Erik Thuning <boooink@gmail.com> Date: Tue, 26 Jul 2022 10:56:59 +0200 Subject: [PATCH 11/18] Sorted the bugs when picking an interval via button --- html/fragments.html | 6 +++--- script.js | 17 +++++++++++++---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/html/fragments.html b/html/fragments.html index 9dbb066..761a059 100644 --- a/html/fragments.html +++ b/html/fragments.html @@ -611,9 +611,9 @@ </button> <br/> <label>Löptid:</label> - <button onClick="JavaScript:loanLength(event, 7)">1 vecka</button> - <button onClick="JavaScript:loanLength(event, 365)">1 år</button> - <button onClick="JavaScript:loanLength(event, 1095)">3 år</button> + <button onClick="JavaScript:loanLength(event, 7, 'day')">1 vecka</button> + <button onClick="JavaScript:loanLength(event, 1, 'year')">1 år</button> + <button onClick="JavaScript:loanLength(event, 3, 'year')">3 år</button> <br/> <label for="end">Slutdatum:</label> <input type="text" diff --git a/script.js b/script.js index 2dc75e4..3478b9a 100644 --- a/script.js +++ b/script.js @@ -483,6 +483,7 @@ function calendar(event) { if(!input.cal) { var cal = new dhtmlXCalendarObject(input.id) cal.hideTime() + cal.setDate(input.value) input.cal = cal cal.show() } @@ -574,16 +575,24 @@ function showFile(event) { filefield.value = event.currentTarget.files[0].name } -function loanLength(event, days) { +function loanLength(event, length, unit) { event.preventDefault() var end = document.getElementById('end') var enddate = new Date() - enddate.setDate(enddate.getDate() + days) - var month = enddate.getMonth() + switch(unit) { + case 'day': + enddate.setDate(enddate.getDate() + length) + break + case 'year': + enddate.setFullYear(enddate.getFullYear() + length) + break; + } + // javascript zero-indexes months because of course + var month = enddate.getMonth() + 1 if(month < 10) { month = '0' + month } - var day = enddate.getDay() + var day = enddate.getDate() if(day < 10) { day = '0' + day } From 22d760a0af2828933f14e24be121f225a126d20f Mon Sep 17 00:00:00 2001 From: Erik Thuning <boooink@gmail.com> Date: Wed, 27 Jul 2022 10:48:53 +0200 Subject: [PATCH 12/18] Whitespace cleanup --- include/Page.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/Page.php b/include/Page.php index 1d2607e..4d97158 100644 --- a/include/Page.php +++ b/include/Page.php @@ -1,7 +1,7 @@ <?php abstract class Page extends Responder { protected abstract function render_body(); - + protected $page = 'checkout'; protected $title = "DSV Utlåning"; protected $subtitle = ''; @@ -15,11 +15,11 @@ abstract class Page extends Responder { 'history' => 'Historik', 'search' => 'Sök'); private $template_parts = array(); - + public function __construct() { parent::__construct(); $this->template_parts = get_fragments('./html/base.html'); - + if(isset($_GET['page'])) { $this->page = $_GET['page']; } @@ -27,7 +27,7 @@ abstract class Page extends Responder { $this->subtitle = $this->menuitems[$this->page]; } } - + public function render() { $this->render_head(); $this->render_body(); @@ -36,7 +36,7 @@ abstract class Page extends Responder { } $this->render_foot(); } - + final private function render_head() { $headtitle = $this->title; $pagetitle = $this->title; @@ -83,7 +83,7 @@ abstract class Page extends Responder { 'message' => $this->error), $this->fragments['message'])); } - + final private function render_foot() { print($this->template_parts['foot']); } From b3434dbb6a66813d0d6281618c562353994abd5d Mon Sep 17 00:00:00 2001 From: Erik Thuning <boooink@gmail.com> Date: Wed, 27 Jul 2022 10:52:52 +0200 Subject: [PATCH 13/18] Changed loan extension form to have a saner default end time. --- include/Page.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/Page.php b/include/Page.php index 4d97158..7c4060a 100644 --- a/include/Page.php +++ b/include/Page.php @@ -211,7 +211,7 @@ abstract class Page extends Responder { $status = $loan->get_status(); $note = ''; if($status !== 'inactive_loan') { - $extend = format_date(default_loan_end(time())); + $extend = format_date($loan->get_endtime()); $note = replace(array('id' => $product->get_id(), 'end_new' => $extend), $this->fragments['loan_extend_form']); From b967c7dde296c8638a5c4fb5377c42e789dbbfee Mon Sep 17 00:00:00 2001 From: Erik Thuning <boooink@gmail.com> Date: Wed, 27 Jul 2022 10:58:11 +0200 Subject: [PATCH 14/18] Added the new tables pending_receipt and loan_extension to the schema --- database.sql | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/database.sql b/database.sql index 840e57b..7f459e7 100644 --- a/database.sql +++ b/database.sql @@ -147,3 +147,24 @@ create table `kvs` ( `value` varchar(64) not null default '' ) character set utf8mb4, collate utf8mb4_unicode_ci; + +create table `pending_receipt` ( + `user` bigint(20) not null, + primary key (`user`), + constraint `pr_f_user` + foreign key(`user`) references `user`(`id`), + `send_time` bigint(20) not null, + `since_time` bigint(20) not null +) character set utf8mb4, + collate utf8mb4_unicode_ci; + +create table `loan_extension` ( + `loan` bigint(20) not null, + constraint `le_f_loan` + foreign key(`loan`) references `loan`(`event`), + `extend_time` bigint(20) not null, + primary key (`loan`, `extend_time`), + `old_end` bigint(20) not null, + `new_end` bigint(20) not null +) character set utf8mb4, + collate utf8mb4_unicode_ci; From 4e9e5b93af0bac7343e37854402112dfef8c0fe4 Mon Sep 17 00:00:00 2001 From: Erik Thuning <boooink@gmail.com> Date: Wed, 27 Jul 2022 11:04:09 +0200 Subject: [PATCH 15/18] Added a function to get the latest loan extension. Broke receipt queueing into a function. Changed $this->id to $this->get_id() for the sake of consistency. --- include/Loan.php | 61 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 18 deletions(-) diff --git a/include/Loan.php b/include/Loan.php index cb7f0ae..2640d73 100644 --- a/include/Loan.php +++ b/include/Loan.php @@ -13,19 +13,7 @@ class Loan extends Event { bind($insert, 'iii', $event_id, $user->get_id(), strtotime($endtime)); execute($insert); $loan = new Loan($event_id); - - $now = time(); - $pending = prepare('select * from `pending_receipt` where `user` = ? - and `since_time` < ? and `send_time` > ?'); - bind($pending, 'iii', $user->get_id(), $now, $now); - execute($pending); - if(result_single($pending) === null) { - $add = prepare('insert into `pending_receipt` - (`user`, `since_time`, `send_time`) - values(?, ?, ?)'); - bind($add, 'iii', $user->get_id(), $now, $now + 3600); - execute($add); - } + $loan->queue_receipt($user); commit_trans(); return $loan; } @@ -45,13 +33,28 @@ class Loan extends Event { protected function update_fields() { parent::update_fields(); $get = prepare('select * from `loan` where `event`=?'); - bind($get, 'i', $this->id); + bind($get, 'i', $this->get_id()); execute($get); $loan = result_single($get); $this->user = $loan['user']; $this->endtime = $loan['endtime']; } + protected function queue_receipt($user) { + $now = time(); + $pending = prepare('select * from `pending_receipt` where `user` = ? + and `since_time` < ? and `send_time` > ?'); + bind($pending, 'iii', $user->get_id(), $now, $now); + execute($pending); + if(result_single($pending) === null) { + $add = prepare('insert into `pending_receipt` + (`user`, `since_time`, `send_time`) + values(?, ?, ?)'); + bind($add, 'iii', $user->get_id(), $now, $now + 3600); + execute($add); + } + } + public function get_user() { return new User($this->user); } @@ -61,18 +64,40 @@ class Loan extends Event { } public function extend($time) { + $oldend = $this->get_endtime(); + $now = time(); $ts = strtotime($time . ' 13:00'); - $query = prepare('update `loan` set `endtime`=? where `event`=?'); - bind($query, 'ii', $ts, $this->id); - execute($query); + + begin_trans(); + $extend = prepare('update `loan` set `endtime`=? where `event`=?'); + bind($extend, 'ii', $ts, $this->get_id()); + execute($extend); + + $log = prepare('insert into `loan_extension` + (`loan`, `extend_time`, `old_end`, `new_end`) + values (?, ?, ?, ?)'); + bind($log, 'iiii', $this->get_id(), $now, $oldend, $ts); + execute($log); + + $this->queue_receipt($this->get_user()); + $this->endtime = $ts; + commit_trans(); return true; } + public function get_last_extension() { + $select = prepare('select max(`extend_time`) as `extend_time` + from `loan_extension` where `loan`=?'); + bind($select, 'i', $this->get_id()); + execute($select); + return result_single($select)['max(`extend_time`)']; + } + public function end() { $now = time(); $query = prepare('update `event` set `returntime`=? where `id`=?'); - bind($query, 'ii', $now, $this->id); + bind($query, 'ii', $now, $this->get_id()); execute($query); $this->returntime = $now; return true; From ae8b73cb88fecd4afb470c14270794a5bab117f4 Mon Sep 17 00:00:00 2001 From: Erik Thuning <boooink@gmail.com> Date: Wed, 27 Jul 2022 13:15:06 +0200 Subject: [PATCH 16/18] Trim whitespace from serials when checking out and returning products --- include/Ajax.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/Ajax.php b/include/Ajax.php index 1fa756d..99abc22 100644 --- a/include/Ajax.php +++ b/include/Ajax.php @@ -87,7 +87,7 @@ class Ajax extends Responder { } $product = null; try { - $product = new Product($_POST['product'], 'serial'); + $product = new Product(trim($_POST['product']), 'serial'); } catch(Exception $e) { return new Failure('Ogiltigt serienummer.'); } @@ -102,7 +102,7 @@ class Ajax extends Responder { private function return_product() { $product = null; try { - $product = new Product($_POST['serial'], 'serial'); + $product = new Product(trim($_POST['serial']), 'serial'); } catch(Exception $e) { return new Failure('Ogiltigt serienummer.'); } From 1b8c2e1e181d7b5070f62e670053be3a5bc91b78 Mon Sep 17 00:00:00 2001 From: Erik Thuning <boooink@gmail.com> Date: Wed, 27 Jul 2022 13:52:54 +0200 Subject: [PATCH 17/18] Corrected a database field name --- include/Loan.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/Loan.php b/include/Loan.php index 2640d73..89b8465 100644 --- a/include/Loan.php +++ b/include/Loan.php @@ -91,7 +91,7 @@ class Loan extends Event { from `loan_extension` where `loan`=?'); bind($select, 'i', $this->get_id()); execute($select); - return result_single($select)['max(`extend_time`)']; + return result_single($select)['extend_time']; } public function end() { From 9307604aa08b4c793cdd44b53fb8bb0ae00e4243 Mon Sep 17 00:00:00 2001 From: Erik Thuning <boooink@gmail.com> Date: Wed, 27 Jul 2022 13:53:18 +0200 Subject: [PATCH 18/18] Implemented receipts for loan extensions --- include/Cron.php | 108 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 75 insertions(+), 33 deletions(-) diff --git a/include/Cron.php b/include/Cron.php index 3303ff5..80bc67a 100644 --- a/include/Cron.php +++ b/include/Cron.php @@ -19,9 +19,17 @@ class Cron { $this->strings = array( 'en' => array( 'new' => array( + 'single' => "The following loan has been registered in your name:", + 'multi' => "The following loans have been registered in your name:", 'expiry' => "expires on", 'serial' => "serial number", ), + 'extend' => array( + 'single' => "The following loan has been extended:", + 'multi' => "The following loans have been extended:", + 'expiry' => "extended to", + 'serial' => "serial number", + ), 'expiring' => array( 'single' => "The following loan expires in less than $days days:", 'multi' => "The following loans expire in less than $days days:", @@ -37,9 +45,17 @@ class Cron { ), 'sv' => array( 'new' => array( + 'single' => "Följande lån har registrerats på din användare:", + 'multi' => "Följande lån har registrerats på din användare:", 'expiry' => "går ut", 'serial' => "artikelnummer", ), + 'extend' => array( + 'single' => "Följande lån har förlängts:", + 'multi' => "Följande lån har förlängts:", + 'expiry' => "förlängt till", + 'serial' => "artikelnummer", + ), 'expiring' => array( 'single' => "Följande lån går ut om mindre än $days dagar:", 'multi' => "Följande lån går ut om mindre än $days dagar:", @@ -71,13 +87,16 @@ class Cron { $user = new User($row['user']); $since_time = $row['since_time']; - $notify_loans = array(); + $new_loans = array(); + $extended_loans = array(); foreach($user->get_loans('active') as $loan) { if($loan->get_starttime() >= $since_time) { - $notify_loans[] = $loan; + $new_loans[] = $loan; + } else if($loan->get_last_extension() >= $since_time) { + $extended_loans[] = $loan; } } - $this->send_receipt($user, $notify_loans); + $this->send_receipt($user, $new_loans, $extended_loans); $delete = prepare('delete from `pending_receipt` where `user` = ? and `send_time` < ?'); bind($delete, 'ii', $user->get_id(), $this->now->getTimestamp()); @@ -86,44 +105,70 @@ class Cron { commit_trans(); } - private function send_receipt($user, $loans) { + private function make_receipt_subject($num_new, $num_extended) { + $subject = "DSV Helpdesk: "; + $messages = array(); + if($num_new > 1) { + $messages[] = $num_new." nya"; + } else if($num_new > 0) { + $messages[] = $num_new." nytt"; + } + if($num_extended > 1) { + $messages[] = $num_extended." förlängda"; + } else if($num_extended > 0) { + $messages[] = $num_extended." förlängt"; + } + return $subject.implode(" och ", $messages)." lån"; + } + + private function send_receipt($user, $new, $extended) { $uid = $user->get_name(); $name = $this->ldap->get_firstname($uid); - $count = count($loans); - if($count > 1) { - $new_sv = "nya lån"; - $new_en = "new loans"; - $have = "have"; - } else { - $new_sv = "nytt lån"; - $new_en = "new loan"; - $have = "has"; + $new_count = count($new); + $extended_count = count($extended); + $subject = $this->make_receipt_subject($new_count, $extended_count); + + $list_sv = array(); + $list_en = array(); + if($new_count > 0) { + $list_sv[] = $this->make_notice('sv', 'new', $new); + $list_en[] = $this->make_notice('en', 'new', $new); } - $subject = "DSV Helpdesk: $count $new_sv"; + if($extended_count > 0) { + $list_sv[] = $this->make_notice('sv', 'extend', $extended); + $list_en[] = $this->make_notice('en', 'extend', $extended); + } + $list_sv = implode("\n\n", $list_sv); + $list_en = implode("\n\n", $list_en); - $intro_sv = ""; - $intro_en = "$count $new_en $have been registered to your user:"; - - $list_sv = $this->make_notice('sv', 'new', $loans); - $list_en = $this->make_notice('en', 'new', $loans); + $info_sv = array(); + $info_en = array(); + if($new_count > 0) { + $info_sv[] = "Eventuella artiklar du inte hämtat ut redan kan hämtas från Helpdesk."; + $info_en[] = "Any products you haven't already picked up can be collected from Helpdesk."; + } + $info_sv[] = "Svara på det här mailet om du har några frågor."; + $info_en[] = "Please reply to this email if you have any questions."; + $info_sv = implode(' ', $info_sv); + $info_en = implode(' ', $info_en); $message = <<<EOF Hej $name! -Det här är ett automatiskt meddelande om att $count $new_sv har registrerats på din användare: +Det här är ett automatiskt meddelande från Helpdesk. $list_sv -Eventuella artiklar du inte hämtat ut redan kan hämtas från Helpdesk. Svara på det här mailet om du har några frågor. +$info_sv ---- -This is an automated message to inform you that $count $new_en $have been registered in your name: +This is an automated message from Helpdesk. $list_en -Any products you haven't already picked up can be collected from Helpdesk. Please reply to this email if you have any questions. +$info_en Mvh DSV Helpdesk @@ -158,18 +203,18 @@ EOF; } } - private function make_subject($num_expiring, $num_expired) { + private function make_reminder_subject($num_expiring, $num_expired) { $subject = "DSV Helpdesk: "; $messages = array(); if($num_expiring > 0) { - $messages[] = $num_expiring." utgående lån"; + $messages[] = $num_expiring." utgående"; } if($num_expired > 1) { - $messages[] = $num_expired." försenade lån"; + $messages[] = $num_expired." försenade"; } elseif($num_expired > 0) { - $messages[] = $num_expired." försenat lån"; + $messages[] = $num_expired." försenat"; } - return $subject.implode(" och ", $messages); + return $subject.implode(" och ", $messages)." lån"; } private function make_notice($lang, $type, $list) { @@ -197,10 +242,6 @@ EOF; ." $serial, ".$strings['expiry']." $endtime"; } - if($type === 'new') { - return implode("\n", $lines); - } - $msg = $strings['single']; if(count($list) > 1) { $msg = $strings['multi']; @@ -244,7 +285,8 @@ EOF; $overdue_count = count($overdue); $total = $expiring_count + $overdue_count; - $subject = $this->make_subject($expiring_count, $overdue_count); + $subject = $this->make_reminder_subject($expiring_count, + $overdue_count); $info_sv = array(); $info_en = array();