Broke all classes into separate files and activated class autoloading.

All free functions are now in functions.php
This commit is contained in:
Erik Thuning 2019-06-11 17:05:48 +02:00
parent fe4e92b6e9
commit 692c2e0aeb
27 changed files with 2493 additions and 2483 deletions

349
include/Ajax.php Normal file

@ -0,0 +1,349 @@
<?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) {
default:
$out = new Success('ajax endpoint');
break;
case 'getfragment':
$out = $this->get_fragment();
break;
case 'checkout':
$out = $this->checkout_product();
break;
case 'return':
$out = $this->return_product();
break;
case 'extend':
$out = $this->extend_loan();
break;
case 'startinventory':
$out = $this->start_inventory();
break;
case 'endinventory':
$out = $this->end_inventory();
break;
case 'inventoryproduct':
$out = $this->inventory_product();
break;
case 'updateproduct':
$out = $this->update_product();
break;
case 'updateuser':
$out = $this->update_user();
break;
case 'savetemplate':
$out = $this->save_template();
break;
case 'deletetemplate':
$out = $this->delete_template();
break;
case 'suggest':
$out = $this->suggest();
break;
case 'discardproduct':
$out = $this->discard_product();
break;
}
print($out->toJson());
}
private function get_fragment() {
$fragment = $_POST['fragment'];
if(isset($this->fragments[$fragment])) {
return new Success($this->fragments[$fragment]);
}
return new Failure("Ogiltigt fragment '$fragment'");
}
private function checkout_product() {
$user = new User($_POST['user'], 'name');
$product = null;
try {
$product = new Product($_POST['product'], 'serial');
} catch(Exception $e) {
return new Failure('Ogiltigt serienummer.');
}
try {
$user->create_loan($product, $_POST['end']);
return new Success($product->get_name() . 'utlånad.');
} catch(Exception $e) {
return new Failure('Artikeln är redan utlånad.');
}
}
private function return_product() {
$product = null;
try {
$product = new Product($_POST['serial'], 'serial');
} catch(Exception $e) {
return new Failure('Ogiltigt serienummer.');
}
$loan = $product->get_active_loan();
if($loan) {
$loan->end();
$user = $loan->get_user();
$userlink = replace(array('page' => 'users',
'id' => $user->get_id(),
'name' => $user->get_displayname()),
$this->fragments['item_link']);
$productlink = replace(array('page' => 'products',
'id' => $product->get_id(),
'name' => $product->get_name()),
$this->fragments['item_link']);
$user = $loan->get_user();
return new Success($productlink . ' åter från ' . $userlink);
}
return new Failure('Artikeln är inte utlånad.');
}
private function extend_loan() {
$product = null;
try {
$product = new Product($_POST['product']);
} catch(Exception $e) {
return new Failure('Ogiltigt ID.');
}
$loan = $product->get_active_loan();
if($loan) {
$loan->extend($_POST['end']);
return new Success('Lånet förlängt');
}
return new Failure('Lån saknas.');
}
private function start_inventory() {
try {
Inventory::begin();
return new Success('Inventering startad.');
} catch(Exception $e) {
return new Failure('Inventering redan igång.');
}
}
private function end_inventory() {
$inventory = Inventory::get_active();
if($inventory === null) {
return new Failure('Ingen inventering pågår.');
}
$inventory->end();
return new Success('Inventering avslutad.');
}
private function inventory_product() {
$inventory = Inventory::get_active();
if($inventory === null) {
return new Failure('Ingen inventering pågår.');
}
$product = null;
try {
$product = new Product($_POST['serial'], 'serial');
} catch(Exception $e) {
return new Failure('Ogiltigt serienummer.');
}
$result = $inventory->add_product($product);
if(!$result) {
return new Failure('Artikeln är redan registrerad.');
}
return new Success('Artikeln registrerad.');
}
private function update_product() {
$info = $_POST;
$id = $info['id'];
$name = $info['name'];
$serial = $info['serial'];
$invoice = $info['invoice'];
$tags = array();
if(isset($info['tag'])) {
$tags = $this->unescape_tags($info['tag']);
}
foreach(array('id', 'name', 'serial', 'invoice', 'tag') as $key) {
unset($info[$key]);
}
if(!$name) {
return new Failure('Artikeln måste ha ett namn.');
}
if(!$serial) {
return new Failure('Artikeln måste ha ett serienummer.');
}
if(!$invoice) {
return new Failure('Artikeln måste ha ett fakturanummer.');
}
$product = null;
if(!$id) {
try {
$temp = new Product($serial, 'serial');
return new Failure(
'Det angivna serienumret finns redan på en annan artikel.');
} catch(Exception $e) {}
try {
$product = Product::create_product($name,
$invoice,
$serial,
$info,
$tags);
$prodlink = replace(array('page' => 'products',
'id' => $product->get_id(),
'name' => $product->get_name()),
$this->fragments['item_link']);
return new Success("Artikeln '$prodlink' sparad.");
} catch(Exception $e) {
return new Failure($e->getMessage());
}
}
$product = new Product($id);
if($product->get_discardtime()) {
return new Failure('Skrotade artiklar får inte modifieras.');
}
if($name != $product->get_name()) {
$product->set_name($name);
}
if($serial != $product->get_serial()) {
try {
$product->set_serial($serial);
} catch(Exception $e) {
return new Failure('Det angivna serienumret finns redan på en annan artikel.');
}
}
if($invoice != $product->get_invoice()) {
$product->set_invoice($invoice);
}
foreach($product->get_info() as $key => $prodvalue) {
if(!isset($info[$key]) || !$info[$key]) {
$product->remove_info($key);
continue;
}
if($prodvalue != $info[$key]) {
$product->set_info($key, $info[$key]);
}
unset($info[$key]);
}
foreach($info as $key => $invalue) {
if($invalue) {
$product->set_info($key, $invalue);
}
}
foreach($product->get_tags() as $tag) {
if(!in_array($tag, $tags)) {
$product->remove_tag($tag);
continue;
}
unset($tags[array_search($tag, $tags)]);
}
foreach($tags as $tag) {
$product->add_tag($tag);
}
return new Success('Ändringarna sparade.');
}
private function update_user() {
$id = $_POST['id'];
$name = $_POST['name'];
$notes = $_POST['notes'];
if(!$name) {
return new Failure('Användarnamnet får inte vara tomt.');
}
$user = new User($id);
if($user->get_name() != $name) {
$user->set_name($name);
}
if($user->get_notes() != $notes) {
$user->set_notes($notes);
}
return new Success('Ändringarna sparade.');
}
private function save_template() {
$info = $_POST;
$name = $info['template'];
$tags = array();
if(isset($info['tag'])) {
$tags = $this->unescape_tags($info['tag']);
}
foreach(array('template',
'id',
'name',
'serial',
'invoice',
'tags') as $key) {
unset($info[$key]);
}
if(!$name) {
return new Failure('Mallen måste ha ett namn.');
}
$template = null;
try {
$template = new Template($name, 'name');
} catch(Exception $e) {
$template = Template::create_template($name, $info, $tags);
$name = $template->get_name();
return new Success(
"Aktuella fält och taggar har sparats till mallen '$name'.");
}
foreach($template->get_fields() as $field) {
if(!isset($info[$field])) {
$template->remove_field($field);
}
}
$existingfields = $template->get_fields();
foreach($info as $field) {
if(!in_array($field, $existingfields)) {
$template->add_field($field);
}
}
foreach($template->get_tags() as $tag) {
if(!in_array($tag, $tags)) {
$template->remove_tag($tag);
}
}
$existingtags = $template->get_tags();
foreach($tags as $tag) {
if(!in_array($tag, $existingtags)) {
$template->add_tag($tag);
}
}
$name = $template->get_name();
return new Success("Mallen '$name' uppdaterad.");
}
private function delete_template() {
try {
$template = $_POST['template'];
Template::delete_template($template);
$name = ucfirst(strtolower($template));
return new Success("Mallen '$name' har raderats.");
} catch(Exception $e) {
return new Failure('Det finns ingen mall med det namnet.');
}
}
private function suggest() {
return new Success(suggest($_POST['type']));
}
private function discard_product() {
$product = new Product($_POST['id']);
if(!$product->get_discardtime()) {
if($product->get_active_loan()) {
return new Failure('Artikeln har ett aktivt lån.<br/>'
.'Lånet måste avslutas innan artikeln skrotas.');
}
$product->discard();
return new Success('Artikeln skrotad.');
} else {
return new Failure('Artikeln är redan skrotad.');
}
}
}
?>

58
include/CheckoutPage.php Normal file

@ -0,0 +1,58 @@
<?php
class CheckoutPage extends Page {
private $userstr = '';
private $user = null;
public function __construct() {
parent::__construct();
if(isset($_GET['user'])) {
$this->userstr = $_GET['user'];
try {
$this->user = new User($this->userstr, 'name');
} catch(Exception $ue) {
try {
$ldap = new Ldap();
$ldap->get_user($this->userstr);
$this->user = User::create_user($this->userstr);
} catch(Exception $le) {
$this->error = "Användarnamnet '";
$this->error .= $this->userstr;
$this->error .= "' kunde inte hittas.";
}
}
}
}
protected function render_body() {
$username = '';
$displayname = '';
$notes = '';
$loan_table = '';
$subhead = '';
$enddate = '';
$disabled = 'disabled';
if($this->user !== null) {
$username = $this->user->get_name();
$displayname = $this->user->get_displayname();
$notes = $this->user->get_notes();
$enddate = gmdate('Y-m-d', time() + 604800); # 1 week from now
$disabled = '';
$loans = $this->user->get_loans('active');
$loan_table = 'Inga pågående lån.';
if($loans) {
$loan_table = $this->build_user_loan_table($loans, 'renew');
}
$subhead = replace(array('title' => 'Lånade artiklar'),
$this->fragments['subtitle']);
}
print(replace(array('user' => $this->userstr,
'displayname' => $displayname,
'notes' => $notes,
'end' => $enddate,
'subtitle' => $subhead,
'disabled' => $disabled,
'loan_table' => $loan_table),
$this->fragments['checkout_page']));
}
}
?>

7
include/Failure.php Normal file

@ -0,0 +1,7 @@
<?php
class Failure extends Result {
public function __construct($message) {
parent::__construct('error', $message);
}
}
?>

80
include/HistoryPage.php Normal file

@ -0,0 +1,80 @@
<?php
class HistoryPage extends Page {
private $action = 'list';
private $inventory = null;
public function __construct() {
parent::__construct();
if(isset($_GET['action'])) {
$this->action = $_GET['action'];
}
if(isset($_GET['id'])) {
try {
$this->inventory = new Inventory($_GET['id']);
} catch(Exception $e) {
$this->inventory = null;
$this->action = 'list';
$this->error = 'Det finns ingen inventering med det ID-numret.';
}
}
switch($this->action) {
case 'show':
$this->subtitle = 'Inventeringsdetaljer';
break;
case 'list':
$this->subtitle = 'Genomförda inventeringar';
break;
}
}
protected function render_body() {
switch($this->action) {
case 'list':
print($this->build_inventory_table());
print(replace(array('title' => 'Skrotade artiklar'),
$this->fragments['title']));
$discards = get_items('product_discarded');
if($discards) {
print($this->build_product_table($discards));
} else {
print('Inga artiklar skrotade.');
}
break;
case 'show':
if($this->inventory &&
Inventory::get_active() !== $this->inventory) {
print($this->build_inventory_details($this->inventory,
false));
}
break;
}
}
private function build_inventory_table() {
$items = get_items('inventory_old');
if(!$items) {
return 'Inga inventeringar gjorda.';
}
$rows = '';
foreach($items as $inventory) {
$id = $inventory->get_id();
$inventory_link = replace(array('id' => $id,
'name' => $id,
'page' => 'history'),
$this->fragments['item_link']);
$duration = $inventory->get_duration();
$num_seen = count($inventory->get_seen_products());
$num_unseen = count($inventory->get_unseen_products());
$rows .= replace(array('item_link' => $inventory_link,
'start_date' => $duration['start'],
'end_date' => $duration['end'],
'num_seen' => $num_seen,
'num_unseen' => $num_unseen),
$this->fragments['inventory_row']);
}
return replace(array('item' => 'Tillfälle',
'rows' => $rows),
$this->fragments['inventory_table']);
}
}
?>

141
include/Inventory.php Normal file

@ -0,0 +1,141 @@
<?php
class Inventory {
private $id = '';
private $starttime = '';
private $endtime = null;
private $seen_products = array();
public static function begin() {
if(Inventory::get_active() !== null) {
throw new Exception('Inventory already in progress.');
}
$now = time();
$start = prepare('insert into `inventory`(`starttime`) values (?)');
bind($start, 'i', $now);
execute($start);
$invid = $start->insert_id;
$prodid = '';
$register = prepare('insert into
`inventory_product`(`inventory`, `product`)
values (?, ?)');
foreach(get_items('loan_active') as $loan) {
$prodid = $loan->get_product()->get_id();
bind($register, 'ii', $invid, $prodid);
execute($register);
}
return new Inventory($invid);
}
public static function get_active() {
$search = prepare('select * from `inventory` where `endtime` is null');
execute($search);
$result = result_single($search);
if($result === null) {
return null;
}
return new Inventory($result['id']);
}
public function __construct($id) {
$search = prepare('select `id` from `inventory` where `id`=?');
bind($search, 'i', $id);
execute($search);
$result = result_single($search);
if($result === null) {
throw new Exception('Invalid id');
}
$this->id = $result['id'];
$this->update_fields();
}
private function update_fields() {
$get = prepare('select * from `inventory` where `id`=?');
bind($get, 'i', $this->id);
execute($get);
$result = result_single($get);
$this->starttime = $result['starttime'];
$this->endtime = $result['endtime'];
$prodget = prepare('select * from `inventory_product`
where `inventory`=?');
bind($prodget, 'i', $this->id);
execute($prodget);
foreach(result_list($prodget) as $row) {
$this->seen_products[] = $row['product'];
}
}
public function end() {
$now = time();
$update = prepare('update `inventory` set `endtime`=?
where `id`=? and `endtime` is null');
bind($update, 'ii', $now, $this->id);
execute($update);
$this->endtime = $now;
return true;
}
public function add_product($product) {
$add = prepare('insert into `inventory_product`(`inventory`, `product`)
values (?, ?)');
bind($add, 'ii', $this->id, $product->get_id());
try {
execute($add);
} catch(Exception $e) {
return false;
}
$this->products[] = $product->get_id();
return true;
}
public function get_id() {
return $this->id;
}
public function get_duration($format = true) {
$style = function($time) {
return $time;
};
if($format) {
$style = function($time) {
return gmdate('Y-m-d', $time);
};
}
return array('start' => $style($this->starttime),
'end' => $style($this->endtime));
}
public function get_seen_products() {
$out = array();
foreach($this->seen_products as $prodid) {
$out[] = new Product($prodid);
}
return $out;
}
public function get_unseen_products() {
$all = get_items('product');
$out = array();
$include = function($product) {
if(!in_array($product->get_id(), $this->seen_products)) {
return true;
}
return false;
};
if($this->endtime) {
$include = function($product) {
if($product->get_createtime() < $this->endtime
&& !in_array($product->get_id(), $this->seen_products)) {
return true;
}
return false;
};
}
foreach($all as $product) {
if($include($product)) {
$out[] = $product;
}
}
return $out;
}
}
?>

18
include/InventoryPage.php Normal file

@ -0,0 +1,18 @@
<?php
class InventoryPage extends Page {
private $inventory = null;
public function __construct() {
parent::__construct();
$this->inventory = Inventory::get_active();
}
protected function render_body() {
if($this->inventory === null) {
print($this->fragments['inventory_start']);
return;
}
print($this->build_inventory_details($this->inventory));
}
}
?>

56
include/Kvs.php Normal file

@ -0,0 +1,56 @@
<?php
class Kvs {
private $items = array();
public function __construct() {
$get = prepare('select * from `kvs`');
execute($get);
foreach(result_list($get) as $row) {
$key = $row['key'];
$value = $row['value'];
$this->items[$key] = $value;
}
}
public function get_keys() {
return array_keys($this->items);
}
public function get_value($key) {
if(isset($this->items[$key])) {
return $this->items[$key];
}
return null;
}
public function set_key($key, $value) {
$find = prepare('select * from `kvs` where `key`=?');
bind($find, 's', $key);
execute($find);
if(result_single($find) === null) {
$update = prepare('insert into `kvs`(`value`, `key`)
values (?, ?)');
} else {
$update = prepare('update `kvs` set `value`=? where `key`=?');
}
bind($update, 'ss', $value, $key);
execute($update);
$this->items[$key] = $value;
return true;
}
public function remove_key($key) {
$find = prepare('select * from `kvs` where `key`=?');
bind($find, 's', $key);
execute($find);
if(result_single($find) === null) {
return true;
}
$update = prepare('delete from `kvs` where `key`=?');
bind($update, 's', $key);
execute($update);
unset($this->items[$key]);
return true;
}
}
?>

@ -1,5 +1,4 @@
<?php
class Ldap {
private $conn;
private $base_dn = "dc=su,dc=se";
@ -42,6 +41,4 @@ class Ldap {
return $out;
}
}
$ldap = new Ldap();
?>

93
include/Loan.php Normal file

@ -0,0 +1,93 @@
<?php
class Loan {
private $id = 0;
private $user = 0;
private $product = 0;
private $starttime = 0;
private $endtime = 0;
private $returntime = null;
public function __construct($id) {
$this->id = $id;
$this->update_fields();
}
private function update_fields() {
$get = prepare('select * from `loan` where `id`=?');
bind($get, 'i', $this->id);
execute($get);
$loan = result_single($get);
$this->user = $loan['user'];
$this->product = $loan['product'];
$this->starttime = $loan['starttime'];
$this->endtime = $loan['endtime'];
$this->returntime = $loan['returntime'];
}
public function get_id() {
return $this->id;
}
public function get_user() {
return new User($this->user);
}
public function get_product() {
return new Product($this->product);
}
public function get_duration($format = true) {
$style = function($time) {
return $time;
};
if($format) {
$style = function($time) {
if($time) {
return gmdate('Y-m-d', $time);
}
return $time;
};
}
return array('start' => $style($this->starttime),
'end' => $style($this->endtime),
'end_renew' => $style($this->endtime + 604800), # +1 week
'return' => $style($this->returntime));
}
public function is_active() {
if($this->returntime === null) {
return true;
}
return false;
}
public function extend($time) {
$ts = strtotime($time . ' 13:00');
$query = prepare('update `loan` set `endtime`=? where `id`=?');
bind($query, 'ii', $ts, $this->id);
execute($query);
$this->endtime = $ts;
return true;
}
public function end() {
$now = time();
$query = prepare('update `loan` set `returntime`=? where `id`=?');
bind($query, 'ii', $now, $this->id);
execute($query);
$this->returntime = $now;
return true;
}
public function is_overdue() {
if($this->returntime !== null) {
return false;
}
$now = time();
if($now > $this->endtime) {
return true;
}
return false;
}
}
?>

306
include/Page.php Normal file

@ -0,0 +1,306 @@
<?php
abstract class Page extends Responder {
protected abstract function render_body();
protected $page = 'checkout';
protected $title = "DSV Utlåning";
protected $subtitle = '';
protected $error = null;
protected $menuitems = array('checkout' => 'Låna',
'return' => 'Lämna',
'products' => 'Artiklar',
'users' => 'Låntagare',
'inventory' => 'Inventera',
'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'];
}
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();
}
final 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;
}
final private function render_error() {
print(replace(array('type' => 'error',
'message' => $this->error),
$this->fragments['message']));
}
final private function render_foot() {
print($this->template_parts['foot']);
}
final protected function build_user_table($users) {
$rows = '';
foreach($users as $user) {
$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(),
'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 = $count .' artiklar';
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_product_table($products) {
$rows = '';
foreach($products as $product) {
$prodlink = replace(array('id' => $product->get_id(),
'name' => $product->get_name(),
'page' => 'products'),
$this->fragments['item_link']);
$available = 'Tillgänglig';
$status = 'available';
$discarded = $product->get_discardtime();
if($discarded) {
$available = 'Skrotad '.$discarded;
$status = 'discarded';
} else {
$loan = $product->get_active_loan();
if($loan) {
$user = $loan->get_user();
$userlink = replace(array('name' => $user->get_displayname(),
'id' => $user->get_id(),
'page' => 'users'),
$this->fragments['item_link']);
$available = 'Utlånad till '.$userlink;
if($loan->is_overdue()) {
$status = 'overdue';
$available .= ', försenad';
} else {
$status = 'on_loan';
$available .= ', åter '.$loan->get_duration()['end'];
}
}
}
$rows .= replace(array('available' => $available,
'status' => $status,
'item_link' => $prodlink),
$this->fragments['product_row']);
}
return replace(array('rows' => $rows),
$this->fragments['product_table']);
}
final protected function build_user_loan_table($loans, $show = 'none') {
$vis_return = 'hidden';
$vis_renew = 'hidden';
switch($show) {
case 'return':
$vis_return = '';
break;
case 'renew':
$vis_renew = '';
break;
case 'both':
$vis_return = '';
$vis_renew = '';
break;
case 'none':
break;
default:
throw new Exception('Invalid argument.');
}
$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']);
$available = '';
$duration = $loan->get_duration();
$status = 'on_loan';
if($loan->is_overdue()) {
$status = 'overdue';
}
$returndate = '';
if($duration['return'] !== null) {
$returndate = $duration['return'];
}
$rows .= replace(array('id' => $product->get_id(),
'item_link' => $prodlink,
'start_date' => $duration['start'],
'end_date' => $duration['end'],
'return_date' => $returndate,
'status' => $status,
'vis_renew' => $vis_renew,
'vis_return' => $vis_return,
'end_new' => $duration['end_renew']),
$this->fragments['loan_row']);
}
return replace(array('rows' => $rows,
'vis_renew' => $vis_renew,
'vis_return' => $vis_return,
'item' => 'Artikel'),
$this->fragments['loan_table']);
}
final protected function build_product_loan_table($loans) {
$rows = '';
$renew_column_visible = 'hidden';
foreach($loans as $loan) {
$user = $loan->get_user();
$product = $loan->get_product();
$userlink = replace(array('id' => $user->get_id(),
'name' => $user->get_name(),
'page' => 'users'),
$this->fragments['item_link']);
$available = '';
$duration = $loan->get_duration();
$status = 'on_loan';
if($loan->is_overdue()) {
$status = 'overdue';
}
$returndate = '';
$renew_visible = '';
if($duration['return']) {
$returndate = $duration['return'];
$renew_visible = 'hidden';
} else {
$renew_column_visible = '';
}
$rows .= replace(array('item_link' => $userlink,
'start_date' => $duration['start'],
'end_date' => $duration['end'],
'return_date' => $returndate,
'status' => $status,
'vis_renew' => $renew_column_visible,
'vis_renew_button' => $renew_visible,
'vis_return' => '',
'id' => $product->get_id(),
'end_new' => $duration['end_renew']),
$this->fragments['loan_row']);
}
return replace(array('rows' => $rows,
'vis_renew' => $renew_column_visible,
'vis_return' => '',
'item' => 'Låntagare'),
$this->fragments['loan_table']);
}
final protected function build_inventory_details($inventory,
$interactive = true) {
$duration = $inventory->get_duration();
$all_products = get_items('product');
$seen = $inventory->get_seen_products();
$unseen = array();
foreach($all_products as $product) {
if(!in_array($product, $seen)) {
$unseen[] = $product;
}
}
$missing = 'Saknade artiklar';
$hidden = 'hidden';
if($interactive) {
$missing = 'Kvarvarande artiklar';
$hidden = '';
}
$out = replace(array('start_date' => $duration['start'],
'total_count' => count($all_products),
'seen_count' => count($seen),
'hide' => $hidden),
$this->fragments['inventory_do']);
$out .= replace(array('title' => $missing),
$this->fragments['subtitle']);
if($unseen) {
$out .= $this->build_product_table($unseen);
} else {
$out .= 'Inga artiklar saknas.';
}
$out .= replace(array('title' => 'Inventerade artiklar'),
$this->fragments['subtitle']);
if($seen) {
$out .= $this->build_product_table($seen);
} else {
$out .= 'Inga artiklar inventerade.';
}
return $out;
}
}
?>

18
include/Printer.php Normal file

@ -0,0 +1,18 @@
<?php
class Printer extends QR {
public function __construct() {
parent::__construct();
}
public function render() {
$label = replace(array('id' => $this->product->get_id(),
'name' => $this->product->get_name(),
'serial' => $this->product->get_serial()),
$this->fragments['product_label']);
$title = 'Etikett för artikel '.$this->product->get_serial();
print(replace(array('title' => $title,
'label' => $label),
$this->fragments['label_page']));
}
}
?>

318
include/Product.php Normal file

@ -0,0 +1,318 @@
<?php
class Product {
private $id = 0;
private $name = '';
private $invoice = '';
private $serial = '';
private $createtime = null;
private $discardtime = null;
private $info = array();
private $tags = array();
public static function create_product(
$name = '',
$invoice = '',
$serial = '',
$info = array(),
$tags = array()
) {
$now = time();
begin_trans();
try {
$stmt = 'insert into
`product`(`name`, `invoice`, `serial`, `createtime`)
values (?, ?, ?, ?)';
$ins_prod = prepare($stmt);
bind($ins_prod, 'sssi', $name, $invoice, $serial, $now);
execute($ins_prod);
$product = new Product($serial, 'serial');
foreach($info as $field => $value) {
$product->set_info($field, $value);
}
foreach($tags as $tag) {
$product->add_tag($tag);
}
commit_trans();
return $product;
} catch(Exception $e) {
revert_trans();
throw $e;
}
}
public function __construct($clue, $type = 'id') {
$search = null;
switch($type) {
case 'id':
$search = prepare('select `id` from `product`
where `id`=?');
bind($search, 'i', $clue);
break;
case 'serial':
$search = prepare('select `id` from `product`
where `serial`=?');
bind($search, 's', $clue);
break;
default:
throw new Exception('Invalid type.');
}
execute($search);
$result = result_single($search);
if($result === null) {
throw new Exception('Product does not exist..');
}
$this->id = $result['id'];
$this->update_fields();
$this->update_info();
$this->update_tags();
}
private function update_fields() {
$get = prepare('select * from `product` where `id`=?');
bind($get, 'i', $this->id);
execute($get);
$product = result_single($get);
$this->name = $product['name'];
$this->invoice = $product['invoice'];
$this->serial = $product['serial'];
$this->createtime = $product['createtime'];
$this->discardtime = $product['discardtime'];
return true;
}
private function update_info() {
$get = prepare('select * from `product_info`
where `product`=? order by `field`');
bind($get, 'i', $this->id);
execute($get);
foreach(result_list($get) as $row) {
$field = $row['field'];
$data = $row['data'];
$this->info[$field] = $data;
}
return true;
}
private function update_tags() {
$get = prepare('select * from `product_tag`
where `product`=? order by `tag`');
bind($get, 'i', $this->id);
execute($get);
$newtags = array();
foreach(result_list($get) as $row) {
$newtags[] = $row['tag'];
}
$this->tags = $newtags;
return true;
}
public function matches($terms) {
foreach($terms as $field => $values) {
$matchvalues = array();
if(property_exists($this, $field)) {
$matchvalues[] = $this->$field;
} else if(array_key_exists($field, $this->get_info())) {
$matchvalues[] = $this->get_info()[$field];
} else {
switch($field) {
case 'tag':
$matchvalues = $this->get_tags();
case 'status':
$matchvalues[] = $this->get_loan_status();
case 'fritext':
$matchvalues[] = $this->name;
$matchvalues[] = $this->serial;
$matchvalues[] = $this->invoice;
$matchvalues = array_merge($matchvalues,
$this->get_tags(),
array_values(
$this->get_info()));
}
}
if(!match($values, $matchvalues)) {
return false;
}
}
return true;
}
public function get_id() {
return $this->id;
}
public function get_createtime() {
return $this->createtime;
}
public function get_discardtime($format = true) {
if($this->discardtime && $format) {
return gmdate('Y-m-d', $this->discardtime);
}
return $this->discardtime;
}
public function discard() {
$now = time();
$update = prepare('update `product` set `discardtime`=? where `id`=?');
bind($update, 'ii', $now, $this->id);
execute($update);
$this->discardtime = $now;
return true;
}
public function get_name() {
return $this->name;
}
public function set_name($newname) {
$update = prepare('update `product` set `name`=? where `id`=?');
bind($update, 'si', $newname, $this->id);
execute($update);
$this->name = $newname;
return true;
}
public function get_invoice() {
return $this->invoice;
}
public function set_invoice($newinvoice) {
$update = prepare('update `product` set `invoice`=? where `id`=?');
bind($update, 'si', $newinvoice, $this->id);
execute($update);
$this->invoice = $newinvoice;
return true;
}
public function get_serial() {
return $this->serial;
}
public function set_serial($newserial) {
$update = prepare('update `product` set `serial`=? where `id`=?');
bind($update, 'si', $newserial, $this->id);
execute($update);
$this->serial = $newserial;
return true;
}
public function get_info() {
return $this->info;
}
public function set_info($field, $value) {
if(!$value) {
return true;
}
$find = prepare('select * from `product_info`
where `product`=? and `field`=?');
bind($find, 'is', $this->id, $field);
execute($find);
if(result_single($find) === null) {
$update = prepare('insert into
`product_info`(`data`, `product`, `field`)
values (?, ?, ?)');
} else {
$update = prepare('update `product_info` set `data`=?
where `product`=? and `field`=?');
}
bind($update, 'sis', $value, $this->id, $field);
execute($update);
$this->update_info();
return true;
}
public function remove_info($field) {
$find = prepare('select * from `product_info`
where `product`=? and `field`=?');
bind($find, 'is', $this->id, $field);
execute($find);
if(result_single($find) === null) {
return true;
}
$update = prepare('delete from `product_info`
where `field`=? and `product`=?');
bind($update, 'si', $field, $this->id);
execute($update);
$this->update_info();
return true;
}
public function get_tags() {
return $this->tags;
}
public function add_tag($tag) {
if(!$tag) {
return true;
}
$find = prepare('select * from `product_tag`
where `product`=? and `tag`=?');
bind($find, 'is', $this->id, $tag);
execute($find);
if(result_single($find) === null) {
$update = prepare('insert into `product_tag`(`tag`, `product`)
values (?, ?)');
bind($update, 'si', $tag, $this->id);
execute($update);
$this->update_tags();
}
return true;
}
public function remove_tag($tag) {
$find = prepare('select * from `product_tag`
where `product`=? and `tag`=?');
bind($find, 'is', $this->id, $tag);
execute($find);
if(result_single($find) === null) {
return true;
}
$update = prepare('delete from `product_tag`
where `tag`=? and `product`=?');
bind($update, 'si', $tag, $this->id);
execute($update);
$this->update_tags();
return true;
}
public function get_loan_status() {
if($this->get_discardtime(false)) {
return 'discarded';
}
$loan = $this->get_active_loan();
if(!$loan) {
return 'no_loan';
}
if($loan->is_overdue()) {
return 'overdue';
}
return 'on_loan';
}
public function get_active_loan() {
$find = prepare('select `id` from `loan`
where `returntime` is null and product=?');
bind($find, 'i', $this->id);
execute($find);
$result = result_single($find);
if($result === null) {
return null;
}
return new Loan($result['id']);
}
public function get_loan_history() {
$find = prepare('select `id` from `loan`
where product=? order by `starttime` desc');
bind($find, 'i', $this->id);
execute($find);
$loans = result_list($find);
$out = array();
foreach($loans as $loan) {
$out[] = new Loan($loan['id']);
}
return $out;
}
}
?>

133
include/ProductPage.php Normal file

@ -0,0 +1,133 @@
<?php
class ProductPage extends Page {
private $action = 'list';
private $template = null;
private $product = null;
public function __construct() {
parent::__construct();
if(isset($_GET['action'])) {
$this->action = $_GET['action'];
}
if(isset($_GET['template'])) {
$template = $_GET['template'];
if($template) {
try {
$this->template = new Template($template, 'name');
} catch(Exception $e) {
$this->template = null;
$this->error = 'Det finns ingen mall med det namnet.';
}
}
}
if(isset($_GET['id'])) {
$id = $_GET['id'];
if($id) {
try {
$this->product = new Product($id);
} catch(Exception $e) {
$this->action = 'list';
$this->product = null;
$this->error = 'Det finns ingen artikel med det ID-numret.';
}
}
}
switch($this->action) {
case 'show':
$this->subtitle = 'Artikeldetaljer';
break;
case 'new':
$this->subtitle = 'Ny artikel';
break;
case 'list':
$this->subtitle = 'Artikellista';
break;
}
}
protected function render_body() {
switch($this->action) {
case 'list':
print($this->fragments['create_product']);
print($this->build_product_table(get_items('product')));
break;
case 'show':
print($this->build_product_details());
break;
case 'new':
print($this->build_new_page());
break;
}
}
private function build_product_details() {
$info = '';
foreach($this->product->get_info() as $key => $value) {
$info .= replace(array('name' => ucfirst($key),
'key' => $key,
'value' => $value),
$this->fragments['info_item']);
}
$tags = '';
foreach($this->escape_tags($this->product->get_tags()) as $tag) {
$tags .= replace(array('tag' => ucfirst($tag)),
$this->fragments['tag']);
}
$fields = array('id' => $this->product->get_id(),
'name' => $this->product->get_name(),
'serial' => $this->product->get_serial(),
'invoice' => $this->product->get_invoice(),
'tags' => $tags,
'info' => $info);
$label = '';
if(class_exists('QRcode', false)) {
$label = replace($fields, $this->fragments['product_label']);
}
$fields['label'] = $label;
$out = replace($fields, $this->fragments['product_details']);
if(!$this->product->get_discardtime()) {
$out .= replace(array('id' => $this->product->get_id()),
$this->fragments['discard_button']);
}
$out .= replace(array('title' => 'Lånehistorik'),
$this->fragments['subtitle']);
$loan_table = 'Inga lån att visa.';
$history = $this->product->get_loan_history();
if($history) {
$loan_table = $this->build_product_loan_table($history);
}
$out .= $loan_table;
return $out;
}
private function build_new_page() {
$template = '';
$fields = '';
$tags = '';
if($this->template) {
$template = $this->template->get_name();
foreach($this->template->get_fields() as $field) {
$fields .= replace(array('name' => ucfirst($field),
'key' => $field,
'value' => ''),
$this->fragments['info_item']);
}
foreach($this->template->get_tags() as $tag) {
$tags .= replace(array('tag' => ucfirst($tag)),
$this->fragments['tag']);
}
}
$out = replace(array('template' => $template),
$this->fragments['template_management']);
$out .= replace(array('id' => '',
'name' => '',
'serial' => '',
'invoice' => '',
'tags' => $tags,
'info' => $fields,
'label' => ''),
$this->fragments['product_details']);
return $out;
}
}
?>

18
include/QR.php Normal file

@ -0,0 +1,18 @@
<?php
class QR extends Responder {
protected $product = '';
public function __construct() {
parent::__construct();
if(isset($_GET['id'])) {
$this->product = new Product($_GET['id']);
}
}
public function render() {
if(class_exists('QRcode', false)) {
QRcode::svg((string)$this->product->get_serial());
}
}
}
?>

31
include/Responder.php Normal file

@ -0,0 +1,31 @@
<?php
abstract class Responder {
protected $fragments = array();
public function __construct() {
$this->fragments = get_fragments('./html/fragments.html');
}
final protected function escape_tags($tags) {
foreach($tags as $key => $tag) {
$tags[$key] = str_replace(array("'",
'"'),
array('&#39;',
'&#34;'),
strtolower($tag));
}
return $tags;
}
final protected function unescape_tags($tags) {
foreach($tags as $key => $tag) {
$tags[$key] = str_replace(array('&#39;',
'&#34;'),
array("'",
'"'),
strtolower($tag));
}
return $tags;
}
}
?>

18
include/Result.php Normal file

@ -0,0 +1,18 @@
<?php
class Result {
private $type = '';
private $message = '';
public function __construct($type, $message) {
$this->type = $type;
$this->message = $message;
}
public function toJson() {
return json_encode(array(
'type' => $this->type,
'message' => $this->message
));
}
}
?>

7
include/ReturnPage.php Normal file

@ -0,0 +1,7 @@
<?php
class ReturnPage extends Page {
protected function render_body() {
print($this->fragments['return_page']);
}
}
?>

156
include/SearchPage.php Normal file

@ -0,0 +1,156 @@
<?php
class SearchPage extends Page {
private $terms = array();
public function __construct() {
parent::__construct();
unset($_GET['page']);
if(isset($_GET['q']) && !$_GET['q']) {
unset($_GET['q']);
}
$this->terms = $this->translate_keys($_GET);
}
private function do_search() {
$out = array();
if(!$this->terms) {
return $out;
}
foreach(array('user', 'product') as $type) {
$result = $this->search($type, $this->terms);
if($result) {
$out[$type] = $result;
}
}
return $out;
}
private function translate_keys($terms) {
$translated = array();
foreach($terms as $key => $value) {
$newkey = $key;
switch($key) {
case 'q':
$newkey = 'fritext';
break;
case 'namn':
$newkey = 'name';
break;
case 'faktura':
case 'fakturanummer':
$newkey = 'invoice';
break;
case 'serienummer':
$newkey = 'serial';
break;
case 'tagg':
$newkey = 'tag';
break;
case 'status':
$value = $this->translate_values($value);
break;
}
if(!array_key_exists($newkey, $translated)) {
$translated[$newkey] = $value;
} else {
$temp = $translated[$newkey];
$translated[$newkey] = array_merge((array)$temp, (array)$value);
}
}
return $translated;
}
private function translate_values($value) {
if(!is_array($value)) {
$value = array($value);
}
$translated = array();
foreach($value as $item) {
$newitem = $item;
switch($item) {
case 'ute':
case 'utlånad':
case 'utlånat':
case 'lånad':
case 'lånat':
$newitem = 'on_loan';
break;
case 'inne':
case 'ledig':
case 'ledigt':
case 'tillgänglig':
case 'tillgängligt':
$newitem = 'no_loan';
break;
case 'sen':
case 'sent':
case 'försenad':
case 'försenat':
case 'överdraget':
$newitem = 'overdue';
break;
case 'skrotad':
case 'skrotat':
case 'slängd':
case 'slängt':
$newitem = 'discarded';
break;
}
$translated[] = $newitem;
}
return $translated;
}
private function search($type, $terms) {
$items = get_items($type);
$out = array();
foreach($items as $item) {
if($item->matches($terms)) {
$out[] = $item;
}
}
return $out;
}
protected function render_body() {
$terms = '';
foreach($this->terms as $key => $value) {
if(!is_array($value)) {
$terms .= replace(array('term' => ucfirst($key).": $value",
'key' => $key,
'value' => $value),
$this->fragments['search_term']);
} else {
foreach($value as $item) {
$terms .= replace(array('term' => ucfirst($key).": $item",
'key' => $key,
'value' => $item),
$this->fragments['search_term']);
}
}
}
print(replace(array('terms' => $terms),
$this->fragments['search_form']));
if($this->terms) {
$hits = $this->do_search();
print(replace(array('title' => 'Sökresultat'),
$this->fragments['title']));
$result = '';
if(isset($hits['user'])) {
$result = replace(array('title' => 'Låntagare'),
$this->fragments['subtitle']);
$result .= $this->build_user_table($hits['user']);
}
if(isset($hits['product'])) {
$result .= replace(array('title' => 'Artiklar'),
$this->fragments['subtitle']);
$result .= $this->build_product_table($hits['product']);
}
if(!$result) {
$result = 'Inga träffar.';
}
print($result);
}
}
}
?>

7
include/Success.php Normal file

@ -0,0 +1,7 @@
<?php
class Success extends Result {
public function __construct($message) {
parent::__construct('success', $message);
}
}
?>

196
include/Template.php Normal file

@ -0,0 +1,196 @@
<?php
class Template {
private $id = 0;
private $name = '';
private $fields = array();
private $tags = array();
public static function create_template(
$name = '',
$fields = array(),
$tags = array()
) {
begin_trans();
try {
$stmt = 'insert into `template`(`name`) values (?)';
$ins_prod = prepare($stmt);
bind($ins_prod, 's', strtolower($name));
execute($ins_prod);
$template = new Template($name, 'name');
foreach(array_keys($fields) as $field) {
$template->add_field($field);
}
foreach($tags as $tag) {
$template->add_tag($tag);
}
commit_trans();
return $template;
} catch(Exception $e) {
revert_trans();
throw $e;
}
}
public static function delete_template($name) {
$template = new Template($name, 'name');
foreach($template->get_fields() as $field) {
$template->remove_field($field);
}
foreach($template->get_tags() as $tag) {
$template->remove_tag($tag);
}
$delete = prepare('delete from `template` where `id`=?');
bind($delete, 'i', $template->get_id());
execute($delete);
return true;
}
public function __construct($clue, $type = 'id') {
switch($type) {
case 'id':
$this->id = $clue;
$search = prepare('select `name` from `template`
where `id`=?');
bind($search, 'i', $this->id);
execute($search);
$result = result_single($search);
if($result === null) {
throw new Exception('Invalid id');
}
$this->name = $result['name'];
break;
case 'name':
$this->name = strtolower($clue);
$search = prepare('select `id` from `template`
where `name`=?');
bind($search, 's', $this->name);
execute($search);
$result = result_single($search);
if($result === null) {
throw new Exception('Invalid name.');
}
$this->id = $result['id'];
break;
default:
throw new Exception('Invalid type.');
}
$this->update_fields();
$this->update_tags();
}
public function get_id() {
return $this->id;
}
public function get_name() {
return ucfirst($this->name);
}
public function set_name($name) {
$update = prepare('update `template` set `name`=? where `id`=?');
bind($update, 'si', $name, $this->id);
execute($update);
$this->name = $name;
return true;
}
private function update_fields() {
$get = prepare('select `field` from `template_info`
where `template`=? order by `field`');
bind($get, 'i', $this->id);
execute($get);
$fields = array();
foreach(result_list($get) as $row) {
$fields[] = $row['field'];
}
$this->fields = $fields;
return true;
}
private function update_tags() {
$get = prepare('select * from `template_tag`
where `template`=? order by `tag`');
bind($get, 'i', $this->id);
execute($get);
$newtags = array();
foreach(result_list($get) as $row) {
$newtags[] = $row['tag'];
}
$this->tags = $newtags;
return true;
}
public function get_fields() {
return $this->fields;
}
public function add_field($field) {
$find = prepare('select * from `template_info`
where `template`=? and `field`=?');
bind($find, 'is', $this->id, $field);
execute($find);
if(result_single($find) === null) {
$update = prepare('insert into `template_info`(`template`, `field`)
values (?, ?)');
bind($update, 'is', $this->id, $field);
execute($update);
$this->update_fields();
}
return true;
}
public function remove_field($field) {
$find = prepare('select * from `template_info`
where `template`=? and `field`=?');
bind($find, 'is', $this->id, $field);
execute($find);
if(result_single($find) === null) {
return true;
}
$update = prepare('delete from `template_info`
where `field`=? and `template`=?');
bind($update, 'si', $field, $this->id);
execute($update);
$this->update_fields();
return true;
}
public function get_tags() {
return $this->tags;
}
public function add_tag($tag) {
if(!$tag) {
return true;
}
$find = prepare('select * from `template_tag`
where `template`=? and `tag`=?');
bind($find, 'is', $this->id, $tag);
execute($find);
if(result_single($find) === null) {
$update = prepare('insert into `template_tag`(`tag`, `template`)
values (?, ?)');
bind($update, 'si', $tag, $this->id);
execute($update);
$this->update_tags();
}
return true;
}
public function remove_tag($tag) {
$find = prepare('select * from `template_tag`
where `template`=? and `tag`=?');
bind($find, 'is', $this->id, $tag);
execute($find);
if(result_single($find) === null) {
return true;
}
$update = prepare('delete from `template_tag`
where `tag`=? and `template`=?');
bind($update, 'si', $tag, $this->id);
execute($update);
$this->update_tags();
return true;
}
}
?>

176
include/User.php Normal file

@ -0,0 +1,176 @@
<?php
class User {
private $id = 0;
private $name = '';
private $notes = '';
private $ldap = null;
public static function create_user($name) {
$ins_user = prepare('insert into `user`(`name`) values (?)');
bind($ins_user, 's', $name);
execute($ins_user);
return new User($ins_user->insert_id);
}
public function __construct($clue, $type = 'id') {
$find = null;
switch($type) {
case 'id':
$find = prepare('select `id` from `user` where `id`=?');
bind($find, 'i', $clue);
break;
case 'name':
$find = prepare('select `id` from `user` where `name`=?');
bind($find, 's', $clue);
break;
default:
throw new Exception('Invalid type');
}
execute($find);
$id = result_single($find)['id'];
if($id === null) {
throw new Exception("Invalid username '$clue'");
}
$this->id = $id;
$this->update_fields();
$this->ldap = new Ldap();
}
private function update_fields() {
$get = prepare('select * from `user` where `id`=?');
bind($get, 'i', $this->id);
execute($get);
$user = result_single($get);
$this->name = $user['name'];
$this->notes = $user['notes'];
return true;
}
public function matches($terms) {
foreach($terms as $field => $values) {
$matchvalues = array();
if($field == 'name') {
$matchvalues[] = $this->name;
$matchvalues[] = $this->get_displayname();
} else if(property_exists($this, $field)) {
$matchvalues[] = $this->$field;
} else if($field == 'fritext') {
$matchvalues[] = $this->name;
$matchvalues[] = $this->get_displayname();
$matchvalues[] = $this->notes;
} else {
return false;
}
if(!match($values, $matchvalues)) {
return false;
}
}
return true;
}
public function get_displayname() {
try {
return $this->ldap->get_user($this->name);
} catch(Exception $e) {
return 'Ej i SUKAT';
}
}
public function get_email() {
try {
return $this->ldap->get_user_email($this->name);
} catch(Exception $e) {
return 'Mailadress saknas';
}
}
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);
execute($update);
$this->name = $newname;
return true;
}
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);
execute($update);
$this->notes = $newnotes;
return true;
}
public function get_loans($type = 'both') {
$statement = 'select `id` from `loan` where `user`=?';
switch($type) {
case 'active':
$statement .= ' and `returntime` is null';
break;
case 'inactive':
$statement .= ' and `returntime` is not null';
break;
case 'both':
break;
default:
$err = "$type is not a valid argument. Valid arguments are active, inactive, both.";
throw new Exception($err);
break;
}
$statement .= ' order by `starttime` desc';
$get = prepare($statement);
bind($get, 'i', $this->id);
execute($get);
$loans = array();
foreach(result_list($get) as $row) {
$loans[] = new Loan($row['id']);
}
return $loans;
}
public function get_overdue_loans() {
$overdue = array();
foreach($this->get_loans('active') as $loan) {
if($loan->is_overdue()) {
$overdue[] = $loan;
}
}
return $overdue;
}
public function create_loan($product, $endtime) {
$find = prepare('select * from `loan`
where `product`=? and `returntime` is null');
$prod_id = $product->get_id();
bind($find, 'i', $prod_id);
execute($find);
$loan = result_single($find);
if($loan !== null) {
$loan_id = $loan['id'];
throw new Exception(
"Product $prod_id has an active loan (id $loan_id) already.");
}
$now = time();
$insert = prepare('insert into
`loan`(`user`, `product`, `starttime`, `endtime`)
values (?, ?, ?, ?)');
bind($insert, 'iiii',
$this->id, $prod_id,
$now, strtotime($endtime . ' 13:00'));
execute($insert);
$loan_id = $insert->insert_id;
return new Loan($loan_id);
}
}
?>

65
include/UserPage.php Normal file

@ -0,0 +1,65 @@
<?php
class UserPage extends Page {
private $action = 'list';
private $user = null;
public function __construct() {
parent::__construct();
if(isset($_GET['action'])) {
$this->action = $_GET['action'];
}
if(isset($_GET['id'])) {
$id = $_GET['id'];
if($id) {
try {
$this->user = new User($_GET['id']);
} catch(Exception $e) {
$this->user = null;
$this->action = 'list';
$this->error = 'Det finns ingen användare med det ID-numret.';
}
}
}
switch($this->action) {
case 'show':
$this->subtitle = 'Låntagardetaljer';
break;
case 'list':
$this->subtitle = 'Låntagarlista';
break;
}
}
protected function render_body() {
switch($this->action) {
case 'list':
print($this->build_user_table(get_items('user')));
break;
case 'show':
print($this->build_user_details());
break;
}
}
private function build_user_details() {
$active_loans = $this->user->get_loans('active');
$table_active = 'Inga aktuella lån.';
if($active_loans) {
$table_active = $this->build_user_loan_table($active_loans, 'renew');
}
$inactive_loans = $this->user->get_loans('inactive');
$table_inactive = 'Inga gamla lån.';
if($inactive_loans) {
$table_inactive = $this->build_user_loan_table($inactive_loans,
'return');
}
return replace(array('active_loans' => $table_active,
'inactive_loans' => $table_inactive,
'id' => $this->user->get_id(),
'name' => $this->user->get_name(),
'displayname' => $this->user->get_displayname(),
'notes' => $this->user->get_notes()),
$this->fragments['user_details']);
}
}
?>

File diff suppressed because it is too large Load Diff

@ -1,7 +1,5 @@
<?php
require_once("./config.php");
/*
Takes an html file containing named fragments.
Returns an associative array on the format array[name]=>fragment.
@ -76,4 +74,237 @@ function replace($assoc_arr, $subject) {
return str_replace($keys, $values, $subject);
}
function make_page($page) {
switch($page) {
default:
case 'checkout':
return new CheckoutPage();
case 'return':
return new ReturnPage();
case 'search':
return new SearchPage();
case 'products':
return new ProductPage();
case 'users':
return new UserPage();
case 'inventory':
return new InventoryPage();
case 'history':
return new HistoryPage();
case 'ajax':
return new Ajax();
case 'qr':
return new QR();
case 'print':
return new Printer();
}
}
function get_ids($type) {
$append = '';
switch($type) {
case 'user':
break;
case 'product':
$append = 'where `discardtime` is null';
break;
case 'loan':
break;
case 'inventory':
break;
case 'product_discarded':
$append = 'where `discardtime` is not null';
$type = 'product';
break;
case 'loan_active':
$append = 'where `returntime` is null';
$type = 'loan';
break;
case 'inventory_old':
$append = 'where `endtime` is not null order by `id` desc';
$type = 'inventory';
break;
default:
$err = "$type is not a valid argument.";
throw new Exception($err);
break;
}
$query = "select `id` from `$type`";
if($append) {
$query .= " $append";
}
$get = prepare($query);
execute($get);
$ids = array();
foreach(result_list($get) as $row) {
$ids[] = $row['id'];
}
return $ids;
}
function get_items($type) {
$construct = null;
switch($type) {
case 'user':
$construct = function($id) {
return new User($id);
};
break;
case 'product':
case 'product_discarded':
$construct = function($id) {
return new Product($id);
};
break;
case 'loan':
case 'loan_active':
$construct = function($id) {
return new Loan($id);
};
break;
case 'inventory':
case 'inventory_old':
$construct = function($id) {
return new Inventory($id);
};
break;
default:
$err = "$type is not a valid argument.";
throw new Exception($err);
break;
}
$ids = get_ids($type);
$list = array();
foreach($ids as $id) {
$list[] = $construct($id);
}
return $list;
}
function suggest($type) {
$search = '';
$typename = 'name';
switch($type) {
case 'user':
$search = prepare('select `name` from `user` order by `name`');
break;
case 'template':
$search = prepare('select `name` from `template` order by `name`');
break;
case 'tag':
$search = prepare(
'(select `tag` from `product_tag`)
union
(select `tag` from `template_tag`)
order by `tag`');
$typename = 'tag';
break;
case 'field':
$search = prepare(
'(select `field` from `product_info`)
union
(select `field` from `template_info`)
order by `field`');
$typename = 'field';
break;
default:
return array();
}
execute($search);
$out = array();
foreach(result_list($search) as $row) {
$out[] = $row[$typename];
}
return $out;
}
function match($testvalues, $matchvalues) {
if(!is_array($testvalues)) {
$testvalues = array($testvalues);
}
foreach($testvalues as $value) {
foreach($matchvalues as $candidate) {
if(fnmatch($value, $candidate, FNM_CASEFOLD)) {
return true;
}
}
}
return false;
}
### Database interaction functions ###
$db = new mysqli($db_host, $db_user, $db_pass, $db_name);
if($db->connect_errno) {
$error = 'Failed to connect to db. The error was: '.$db->connect_error;
throw new Exception($error);
}
function prepare($statement) {
global $db;
if(!($s = $db->prepare($statement))) {
$error = 'Failed to prepare the following statement: '.$statement;
$error .= '\n';
$error .= $db->error.' ('.$db->errno.')';
throw new Exception($error);
}
return $s;
}
function bind($statement, $types, ...$values) {
global $db;
return $statement->bind_param($types, ...$values);
}
function execute($statement) {
if(!$statement->execute()) {
$error = 'Failed to execute statement.';
$error .= '\n';
$error .= $statement->error.' ('.$statement->errno.')';
throw new Exception($error);
}
return true;
}
function result_list($statement) {
return $statement->get_result()->fetch_all(MYSQLI_ASSOC);
}
function result_single($statement) {
$out = result_list($statement);
switch(count($out)) {
case 0:
return null;
case 1:
foreach($out as $value) {
return $value;
}
default:
throw new Exception('More than one result available.');
}
}
function begin_trans() {
global $db;
$db->begin_transaction(MYSQLI_TRANS_START_WITH_CONSISTENT_SNAPSHOT);
}
function commit_trans() {
global $db;
$db->commit();
return true;
}
function revert_trans() {
global $db;
$db->rollback();
return false;
}
?>

@ -1,77 +0,0 @@
<?php
require_once("./config.php");
$db = new mysqli($db_host, $db_user, $db_pass, $db_name);
if($db->connect_errno) {
$error = 'Failed to connect to db. The error was: '.$db->connect_error;
throw new Exception($error);
}
function prepare($statement) {
global $db;
if(!($s = $db->prepare($statement))) {
$error = 'Failed to prepare the following statement: '.$statement;
$error .= '\n';
$error .= $db->error.' ('.$db->errno.')';
throw new Exception($error);
}
return $s;
}
function bind($statement, $types, ...$values) {
global $db;
return $statement->bind_param($types, ...$values);
}
function execute($statement) {
if(!$statement->execute()) {
$error = 'Failed to execute statement.';
$error .= '\n';
$error .= $statement->error.' ('.$statement->errno.')';
throw new Exception($error);
}
return true;
}
function result_list($statement) {
return $statement->get_result()->fetch_all(MYSQLI_ASSOC);
}
function result_single($statement) {
$out = result_list($statement);
switch(count($out)) {
case 0:
return null;
case 1:
foreach($out as $value) {
return $value;
}
default:
throw new Exception('More than one result available.');
}
}
function begin_trans() {
global $db;
$db->begin_transaction(MYSQLI_TRANS_START_WITH_CONSISTENT_SNAPSHOT);
}
function commit_trans() {
global $db;
$db->commit();
return true;
}
function revert_trans() {
global $db;
$db->rollback();
return false;
}
?>

File diff suppressed because it is too large Load Diff

@ -1,5 +1,13 @@
<?php
require_once('./include/view.php');
set_include_path(get_include_path().PATH_SEPARATOR.'include/');
spl_autoload_register(function ($class) {
if($class == 'qrcode') {
include('./phpqrcode/qrlib.php');
}
});
require('./config.php');
require('functions.php');
header('Content-Type: text/html; charset=UTF-8');