boka3/include/Page.php
Erik Thuning df796f6564 Consistency changes to handle dropped users being present in the system
Printing the users' names as unknown in various places, and excluding
purged users from autocomplete suggestions
2025-09-25 15:04:53 +02:00

377 lines
15 KiB
PHP

<?php
abstract class Page extends Responder {
protected abstract function render_body();
protected $page = 'checkout';
protected $title = '';
protected $subtitle = '';
protected $error = null;
protected $menuitems = array();
private $template_parts = array();
public function __construct() {
global $language, $name;
parent::__construct();
$this->title = $name;
$this->menuitems = array('checkout' => i18n('Check out'),
'return' => i18n('Return'),
'products' => i18n('Products'),
'new' => i18n('New product'),
'users' => i18n('Borrowers'),
'inventory' => i18n('Inventory'),
'history' => i18n('History'),
'search' => i18n('Search'));
$this->template_parts = get_fragments("./html/$language/base.html");
if(isset($_GET['page'])) {
$this->page = $_GET['page'];
}
if(isset($this->menuitems[$this->page])) {
$this->subtitle = $this->menuitems[$this->page];
}
}
public function render() {
$this->render_head();
$this->render_body();
if($this->error) {
$this->render_error();
}
$this->render_foot();
}
private function render_head() {
$headtitle = $this->title;
$pagetitle = $this->title;
if($this->subtitle) {
$headtitle .= ' - '. $this->subtitle;
$pagetitle = $this->subtitle;
}
$query = '';
if(isset($_GET['q'])) {
$query = $_GET['q'];
}
print(replace(
array('title' => $headtitle,
'menu' => $this->build_menu(),
'query'=> $query),
$this->template_parts['head']
));
print(replace(array('title' => $pagetitle),
$this->fragments['title']));
}
private function build_menu() {
$menu = '';
foreach($this->menuitems as $page => $title) {
$align = 'left';
$active = '';
if($this->page == $page) {
$active = 'active';
}
if($page == 'search') {
$align = 'right';
}
$menu .= replace(array('title' => $title,
'page' => $page,
'align' => $align,
'active' => $active),
$this->template_parts['menuitem']);
}
return $menu;
}
private function render_error() {
print(replace(array('type' => 'error',
'message' => $this->error),
$this->fragments['message']));
}
private function render_foot() {
print($this->template_parts['foot']);
}
final protected function build_user_table($users) {
$rows = '';
foreach($users as $user) {
if(!$user->get_name()) {
# Ignore purged users
continue;
}
$replacements = array('name' => '',
'loan' => '',
'has_notes' => '',
'notes' => '',
'item_link' => '');
$replacements['name'] = $user->get_name();
$notes = $user->get_notes();
if($notes) {
$replacements['notes'] = $notes;
$replacements['has_notes'] = '*';
}
$userlink = replace(array('id' => $user->get_id(),
'name' => $user->get_displayname($this->ldap),
'page' => 'users'),
$this->fragments['item_link']);
$replacements['item_link'] = $userlink;
$loans = $user->get_loans('active');
$loan_str = '';
$count = count($loans);
switch($count) {
case 0:
break;
case 1:
$product = $loans[0]->get_product();
$loan_str = $product->get_name();
break;
default:
$loan_str = i18n('{count} products', $count);
break;
}
$replacements['loan'] = $loan_str;
$rows .= replace($replacements, $this->fragments['user_row']);
}
return replace(array('rows' => $rows),
$this->fragments['user_table']);
}
final protected function build_loan_preset_buttons() {
global $loan_length_presets;
$buttons = '';
foreach($loan_length_presets as $preset) {
[$count, $type] = explode(' ', $preset);
$buttons .= replace(array('count' => $count,
'type' => $type,
'description' => $count." ".i18n($type)),
$this->fragments['loan_preset_button']);
}
return $buttons;
}
final protected function build_product_table($products) {
usort($products, ['Product', 'compare']);
$rows = '';
foreach($products as $product) {
$rows .= $this->build_product_row($product);
}
return replace(array('rows' => $rows,
'type' => 'single'),
$this->fragments['product_table']);
}
final protected function build_product_row($product, $matches = null) {
$prodlink = replace(array('id' => $product->get_id(),
'name' => $product->get_name(),
'page' => 'products'),
$this->fragments['item_link']);
$note = i18n('Available');
$status = $product->get_status();
switch($status) {
case 'discarded':
$discarded = format_date($product->get_discardtime());
$note = i18n('Discarded on {date}', $discarded);
break;
case 'service':
$service = $product->get_active_service();
$note = i18n('Being serviced since {date}',
$service->get_starttime());
break;
case 'on_loan':
case 'overdue':
$loan = $product->get_active_loan();
$user = $loan->get_user();
$replacements = array(
'name' => $user->get_displayname($this->ldap),
'id' => $user->get_id(),
'page' => 'users'
);
$userlink = replace($replacements,
$this->fragments['item_link']);
$note = i18n('Borrowed by {user} {loan}', $userlink, $loan);
break;
}
$out = replace(array('status' => $status,
'item_link' => $prodlink,
'serial' => $product->get_serial(),
'note' => $note),
$this->fragments['product_row']);
if($matches) {
$details = $this->build_product_details($product, $matches);
$out .= replace(array('status' => $status,
'details' => $details),
$this->fragments['product_match_row']);
}
return $out;
}
final protected function build_product_details($product, $matches) {
$out = '';
foreach($matches as $name => $value) {
if(is_array($value)) {
$value = implode(', ', $value);
}
$out .= replace(array('name' => $product->get_label($name),
'value' => $value),
$this->fragments['product_match']);
}
return $out;
}
final protected function build_user_loan_table($loans) {
$rows = '';
foreach($loans as $loan) {
$product = $loan->get_product();
$prodlink = replace(array('id' => $product->get_id(),
'name' => $product->get_name(),
'page' => 'products'),
$this->fragments['item_link']);
$status = $loan->get_status();
$initiator = $loan->get_initiator();
$initiator_link = i18n('Unknown');
if($initiator) {
$initiator_name = $initiator->get_name();
if($initiator_name === null) {
$initiator_name = i18n('Dropped user');
}
$initiator_link = replace(
array('id' => $initiator->get_id(),
'name' => $initiator_name,
'page' => 'users'),
$this->fragments['item_link']);
}
$notes = $loan->get_notes();
$hidden = 'hidden';
if($notes) {
$hidden = '';
}
$misc = '';
if($status !== 'inactive_loan') {
$extend = format_date($loan->get_endtime());
$misc = replace(array('id' => $product->get_id(),
'end_new' => $extend),
$this->fragments['loan_extend_form']);
}
$start = $loan->get_starttime();
$end = $loan->get_returntime();
if(!$end) {
$end = $loan->get_endtime();
}
$rows .= replace(array('status' => $status,
'name' => $prodlink,
'serial' => $product->get_serial(),
'start_date' => format_date($start),
'end_date' => format_date($end),
'initiator' => $initiator_link,
'hidden' => $hidden,
'notes' => $notes,
'misc' => $misc),
$this->fragments['user_loan_card']);
}
return $rows;
}
final protected function build_seen_table($products, $inventory) {
$rows = '';
foreach($products as $product) {
$prodid = $product->get_id();
$prodlink = replace(array('id' => $prodid,
'name' => $product->get_name(),
'page' => 'products'),
$this->fragments['item_link']);
$regtime = $inventory->get_product_regtime($product);
$event = $product->get_historic_event($regtime);
$status = '';
$note = '';
if(!$event) {
$discardtime = $product->get_discardtime();
if($discardtime && $discardtime < $regtime) {
$status = 'discarded';
$note = i18n('Discarded on {date}',
format_date($discardtime));
} else {
$status = 'available';
}
} else if($event instanceof Service) {
$status = 'service';
$starttime = $event->get_starttime();
$returntime = $event->get_returntime();
$note = i18n('Being serviced since {date}', $starttime);
if($returntime) {
$note = i18n("Serviced between {start} and {end}",
$starttime,
$returntime);
}
} else if($event instanceof Loan) {
$user = $event->get_user();
$userlink = replace(
array('name' => $user->get_displayname($this->ldap),
'id' => $user->get_id(),
'page' => 'users'),
$this->fragments['item_link']);
$status = 'on_loan';
$note = i18n('Borrowed by {user} {loan} {inventorytime}',
$userlink,
$event,
$regtime);
if($event->get_endtime() < $regtime) {
$status = 'overdue';
}
}
$rows .= replace(array('status' => $status,
'item_link' => $prodlink,
'serial' => $product->get_serial(),
'note' => $note),
$this->fragments['product_row']);
}
return replace(array('rows' => $rows,
'type' => 'single'),
$this->fragments['product_table']);
}
final protected function build_inventory_details($inventory,
$interactive = true) {
$startdate = format_date($inventory->get_starttime());
$seen = $inventory->get_seen_products();
$unseen = array();
if($interactive) {
$all_products = get_items('product');
$total_count = count($all_products);
foreach($all_products as $product) {
if(!in_array($product, $seen)) {
$unseen[] = $product;
}
}
usort($unseen, ['Product', 'compare']);
} else {
$unseen = $inventory->get_unseen_products();
$total_count = count($unseen) + count($seen);
}
$missing = i18n('Missing products');
$hidden = 'hidden';
if($interactive) {
$missing = i18n('Remaining products');
$hidden = '';
}
$unseen_table = i18n('No products are missing.');
if($unseen) {
$unseen_table = $this->build_product_table($unseen);
}
$seen_table = i18n('No products registered');
if($seen) {
$seen_table = $this->build_seen_table($seen, $inventory);
}
return replace(array('start_date' => $startdate,
'total_count' => $total_count,
'seen_count' => count($seen),
'hide' => $hidden,
'unseen_title' => $missing,
'unseen' => $unseen_table,
'seen' => $seen_table),
$this->fragments['inventory_do']);
}
}
?>