Merge branch 'test' into prod

This commit is contained in:
root 2022-09-02 13:15:59 +02:00
commit 183166c4d0
12 changed files with 351 additions and 112 deletions

@ -7,7 +7,7 @@ $db_pass = 'dbpassword';
$db_name = 'dbuser'; $db_name = 'dbuser';
# Address to use as the sender for reminder emails # 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 # Address to send cron error messages to
$error_address = 'root@example.com'; $error_address = 'root@example.com';

@ -7,7 +7,7 @@ require('./include/functions.php');
header('Content-Type: text/html; charset=UTF-8'); header('Content-Type: text/html; charset=UTF-8');
$cron = new Cron($reminder_sender, $error_address); $cron = new Cron($sender, $error_address);
$cron->run(); $cron->run();
?> ?>

@ -147,3 +147,24 @@ create table `kvs` (
`value` varchar(64) not null default '' `value` varchar(64) not null default ''
) character set utf8mb4, ) character set utf8mb4,
collate utf8mb4_unicode_ci; 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;

@ -610,6 +610,11 @@
Låna ut Låna ut
</button> </button>
<br/> <br/>
<label>Löptid:</label>
<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> <label for="end">Slutdatum:</label>
<input type="text" <input type="text"
id="end" id="end"

@ -87,7 +87,7 @@ class Ajax extends Responder {
} }
$product = null; $product = null;
try { try {
$product = new Product($_POST['product'], 'serial'); $product = new Product(trim($_POST['product']), 'serial');
} catch(Exception $e) { } catch(Exception $e) {
return new Failure('Ogiltigt serienummer.'); return new Failure('Ogiltigt serienummer.');
} }
@ -102,7 +102,7 @@ class Ajax extends Responder {
private function return_product() { private function return_product() {
$product = null; $product = null;
try { try {
$product = new Product($_POST['serial'], 'serial'); $product = new Product(trim($_POST['serial']), 'serial');
} catch(Exception $e) { } catch(Exception $e) {
return new Failure('Ogiltigt serienummer.'); return new Failure('Ogiltigt serienummer.');
} }

@ -9,14 +9,177 @@ class Cron {
$this->now = new DateTimeImmutable(); $this->now = new DateTimeImmutable();
$this->sender = $sender; $this->sender = $sender;
$this->error = $error; $this->error = $error;
$warn_time = DateInterval::createFromDateString('3 days'); $this->warn_time = DateInterval::createFromDateString('3 days');
$this->warn_date = $this->now->add($warn_time); $this->warn_date = $this->now->add($this->warn_time);
$this->run_interval = DateInterval::createFromDateString('1 day'); $this->run_interval = DateInterval::createFromDateString('1 day');
$this->kvs = new Kvs(); $this->kvs = new Kvs();
$this->ldap = new Ldap(); $this->ldap = new Ldap();
$days = $this->warn_time->d;
$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:",
'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(
'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:",
'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() { 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'];
$new_loans = array();
$extended_loans = array();
foreach($user->get_loans('active') as $loan) {
if($loan->get_starttime() >= $since_time) {
$new_loans[] = $loan;
} else if($loan->get_last_extension() >= $since_time) {
$extended_loans[] = $loan;
}
}
$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());
execute($delete);
}
commit_trans();
}
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);
$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);
}
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);
$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 från Helpdesk.
$list_sv
$info_sv
----
This is an automated message from Helpdesk.
$list_en
$info_en
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); $lastrun = $this->kvs->get_value('lastrun', 0);
$nextrun = $this->now $nextrun = $this->now
->setTimestamp($lastrun) ->setTimestamp($lastrun)
@ -40,84 +203,50 @@ class Cron {
} }
} }
private function make_subject($num_expiring, $num_expired) { private function make_reminder_subject($num_expiring, $num_expired) {
$subject = "DSV Helpdesk: "; $subject = "DSV Helpdesk: ";
$messages = array(); $messages = array();
if($num_expiring > 0) { if($num_expiring > 0) {
$messages[] = $num_expiring." utgående lån"; $messages[] = $num_expiring." utgående";
} }
if($num_expired > 1) { if($num_expired > 1) {
$messages[] = $num_expired." försenade lån"; $messages[] = $num_expired." försenade";
} elseif($num_expired > 0) { } 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_expiring_notice($lang, $expiring) { private function make_notice($lang, $type, $list) {
if(!$expiring) { if(!$list) {
return ''; return '';
} }
$days = $this->warn_date->d; if(!array_key_exists($lang, $this->strings)) {
switch($lang) { throw new Exception("Invalid languange: $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:"; $strings = $this->strings[$lang];
$itemglue = ", expires on "; if(!array_key_exists($type, $strings)) {
break; throw new Exception("Invalid type: $type");
default:
throw new Exception("Invalid language: ".$lang);
} }
$msg .= "\n\n"; $strings = $strings[$type];
foreach($expiring as $loan) {
$lines = array();
foreach($list as $loan) {
$product = $loan->get_product(); $product = $loan->get_product();
$serial = $product->get_serial(); $serial = $product->get_serial();
$brand = $product->get_brand(); $brand = $product->get_brand();
$name = $product->get_name(); $name = $product->get_name();
$endtime = format_date($loan->get_endtime()); $endtime = format_date($loan->get_endtime());
$msg .= $serial.": ".$brand." ".$name.$itemglue.$endtime;
} $lines[] = "$brand $name, ".$strings['serial']
return $msg; ." $serial, ".$strings['expiry']." $endtime";
} }
private function make_overdue_notice($lang, $overdue) { $msg = $strings['single'];
if(!$overdue) { if(count($list) > 1) {
return ''; $msg = $strings['multi'];
} }
switch($lang) { return $msg."\n\n".implode("\n", $lines);
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 .= "\n\n";
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;
}
return $msg;
} }
private function make_return_info($lang, $count) { private function make_return_info($lang, $count) {
@ -148,25 +277,32 @@ class Cron {
} }
private function send_reminder($user, $expiring, $expired) { private function send_reminder($user, $expiring, $overdue) {
$uid = $user->get_name(); $uid = $user->get_name();
$name = $this->ldap->get_firstname($uid); $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_reminder_subject($expiring_count,
$overdue_count);
$info_sv = array(); $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 = array();
$info_en[] = $this->make_expiring_notice('en', $expiring); if($expiring_count > 0) {
$info_en[] = $this->make_overdue_notice('en', $expired); $info_sv[] = $this->make_notice('sv', 'expiring', $expiring);
$info_en[] = $this->make_notice('en', 'expiring', $expiring);
}
if($overdue_count > 0) {
$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);
$info_en = implode("\n\n", $info_en); $info_en = implode("\n\n", $info_en);
$returns_en = $this->make_return_info( $returns_en = $this->make_return_info('en', $total);
'en', count($expiring) + count($expired));
$message = <<<EOF $message = <<<EOF
Hej $name! Hej $name!
@ -191,16 +327,20 @@ helpdesk@dsv.su.se
08 - 16 16 48 08 - 16 16 48
EOF; EOF;
$this->send_email($uid, $subject, $message);
}
private function send_email($uid, $subject, $message) {
try { try {
mb_send_mail($this->ldap->get_user_email($uid), mb_send_mail($this->ldap->get_user_email($uid),
$subject, $subject,
$message, $message,
'From: '.$this->sender); 'From: '.$this->sender);
} catch(Exception $e) { } catch(Exception $e) {
error_log($e->message); error_log($e->getMessage());
mb_send_mail($this->error, mb_send_mail($this->error,
"Kunde inte skicka minnelse", "Kunde inte skicka mail",
"Påminnelse kunde inte skickas till ".$uid); "Mail kunde inte skickas till ".$uid);
} }
} }
} }

@ -12,8 +12,10 @@ class Loan extends Event {
$endtime .= '13:00'; $endtime .= '13:00';
bind($insert, 'iii', $event_id, $user->get_id(), strtotime($endtime)); bind($insert, 'iii', $event_id, $user->get_id(), strtotime($endtime));
execute($insert); execute($insert);
$loan = new Loan($event_id);
$loan->queue_receipt($user);
commit_trans(); commit_trans();
return new Loan($event_id); return $loan;
} }
public function __construct($id) { public function __construct($id) {
@ -31,13 +33,28 @@ class Loan extends Event {
protected function update_fields() { protected function update_fields() {
parent::update_fields(); parent::update_fields();
$get = prepare('select * from `loan` where `event`=?'); $get = prepare('select * from `loan` where `event`=?');
bind($get, 'i', $this->id); bind($get, 'i', $this->get_id());
execute($get); execute($get);
$loan = result_single($get); $loan = result_single($get);
$this->user = $loan['user']; $this->user = $loan['user'];
$this->endtime = $loan['endtime']; $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() { public function get_user() {
return new User($this->user); return new User($this->user);
} }
@ -47,18 +64,40 @@ class Loan extends Event {
} }
public function extend($time) { public function extend($time) {
$oldend = $this->get_endtime();
$now = time();
$ts = strtotime($time . ' 13:00'); $ts = strtotime($time . ' 13:00');
$query = prepare('update `loan` set `endtime`=? where `event`=?');
bind($query, 'ii', $ts, $this->id); begin_trans();
execute($query); $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; $this->endtime = $ts;
commit_trans();
return true; 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)['extend_time'];
}
public function end() { public function end() {
$now = time(); $now = time();
$query = prepare('update `event` set `returntime`=? where `id`=?'); $query = prepare('update `event` set `returntime`=? where `id`=?');
bind($query, 'ii', $now, $this->id); bind($query, 'ii', $now, $this->get_id());
execute($query); execute($query);
$this->returntime = $now; $this->returntime = $now;
return true; return true;

@ -211,7 +211,7 @@ abstract class Page extends Responder {
$status = $loan->get_status(); $status = $loan->get_status();
$note = ''; $note = '';
if($status !== 'inactive_loan') { if($status !== 'inactive_loan') {
$extend = format_date(default_loan_end(time())); $extend = format_date($loan->get_endtime());
$note = replace(array('id' => $product->get_id(), $note = replace(array('id' => $product->get_id(),
'end_new' => $extend), 'end_new' => $extend),
$this->fragments['loan_extend_form']); $this->fragments['loan_extend_form']);

9
pending_receipts.sql Normal file

@ -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;

@ -483,6 +483,7 @@ function calendar(event) {
if(!input.cal) { if(!input.cal) {
var cal = new dhtmlXCalendarObject(input.id) var cal = new dhtmlXCalendarObject(input.id)
cal.hideTime() cal.hideTime()
cal.setDate(input.value)
input.cal = cal input.cal = cal
cal.show() cal.show()
} }
@ -573,3 +574,27 @@ function showFile(event) {
var filefield = event.currentTarget.parentNode.filename var filefield = event.currentTarget.parentNode.filename
filefield.value = event.currentTarget.files[0].name filefield.value = event.currentTarget.files[0].name
} }
function loanLength(event, length, unit) {
event.preventDefault()
var end = document.getElementById('end')
var enddate = new Date()
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.getDate()
if(day < 10) {
day = '0' + day
}
end.value = enddate.getFullYear() + '-' + month + '-' + day
}