Completed implementation of search v2. It is not posible to find discarded products via search at this time.
This commit is contained in:
parent
f6b8258a14
commit
eb1987d83a
@ -261,8 +261,10 @@
|
|||||||
|
|
||||||
¤¤ tag ¤¤
|
¤¤ tag ¤¤
|
||||||
<p>
|
<p>
|
||||||
<span class="tag"
|
<span class="tag">
|
||||||
data-name="¤tag¤">
|
<input type="hidden"
|
||||||
|
name="tag[]"
|
||||||
|
value="¤tag¤" />
|
||||||
¤tag¤
|
¤tag¤
|
||||||
<a class="tagremove"
|
<a class="tagremove"
|
||||||
onClick="JavaScript:removeTag(event)">
|
onClick="JavaScript:removeTag(event)">
|
||||||
@ -578,55 +580,41 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
¤¤ search_help ¤¤
|
¤¤ search_form ¤¤
|
||||||
<p>
|
|
||||||
Sökfunktionen matchar normalt på låntagares namn och användarnamn,
|
<form onSubmit="JavaScript:doSearch(event)"
|
||||||
samt artiklars namn. Alla ord måste matcha för att generera en
|
id="search"
|
||||||
träff. Flera sökningar kan kombineras mha nyckelordet 'or'.
|
class="dark">
|
||||||
|
<p>
|
||||||
|
<input type="hidden"
|
||||||
|
name="page"
|
||||||
|
value="search" />
|
||||||
|
<input type="text"
|
||||||
|
onKeyPress="JavaScript:searchInput(event)"
|
||||||
|
name="q"
|
||||||
|
placeholder="Vad letar du efter?"
|
||||||
|
value="" />
|
||||||
|
<button type="submit">
|
||||||
|
Sök
|
||||||
|
</button>
|
||||||
|
</p>
|
||||||
|
<div id="terms">
|
||||||
|
¤terms¤
|
||||||
|
</div>
|
||||||
|
<div class="clear"></div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
¤¤ search_term ¤¤
|
||||||
|
|
||||||
|
<p class="left">
|
||||||
|
<span class="term">
|
||||||
|
<input type="hidden"
|
||||||
|
name="¤key¤"
|
||||||
|
value="¤value¤" />
|
||||||
|
¤term¤
|
||||||
|
<a class="termremove"
|
||||||
|
onClick="JavaScript:removeTerm(event)">
|
||||||
|
x
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
|
||||||
Övriga fält är sökbara med syntaxen [fält]:[värde]. Saknas [värde]
|
|
||||||
så returneras alla träffar som överhuvud taget har fältet.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Alla sökningar matchar på delar av ord, utom taggar som bara matchas exakt.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
De fält som alltid finns är:
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<strong>Artikel:</strong> id, namn, serienummer, fakturanummer, status<br/>
|
|
||||||
Giltiga värden för status är: inne, ute, utlånad, sen, försenad
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>Låntagare:</strong> id, namn, användarnamn, anteckningar
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
Artiklar kan ha fler fält beroende på vad som lagts till.
|
|
||||||
</p>
|
|
||||||
<h3>Lite exempel:</h3>
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<strong>"mac"</strong>
|
|
||||||
- sök efter artiklar och låntagare vars namn eller användarnamn
|
|
||||||
innehåller strängen 'mac'
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>"tag:trasig"</strong>
|
|
||||||
- sök efter artiklar med taggen "trasig"<br/>
|
|
||||||
(Det är bara artiklar som kan ha taggar)
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>"anteckning:"</strong>
|
|
||||||
- sök efter artiklar som har ett fält vid namn "anteckning"<br/>
|
|
||||||
(Anteckningsfältet för låntagare heter 'anteckningar', så bara
|
|
||||||
artiklar kommer hittas)
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<strong>"mac or tag:trasig"</strong>
|
|
||||||
- sök efter artiklar och låntagare som matchar "mac", samt
|
|
||||||
artiklar som har taggen "trasig"<br/>
|
|
||||||
(Bara artiklar kan ha taggar, men eftersom 'mac' är fritext så
|
|
||||||
kan den matcha både artiklar och låntagare)
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
|
146
include/db.php
146
include/db.php
@ -120,6 +120,20 @@ function suggest($type) {
|
|||||||
return $out;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
class Product {
|
class Product {
|
||||||
private $id = 0;
|
private $id = 0;
|
||||||
private $name = '';
|
private $name = '';
|
||||||
@ -228,61 +242,31 @@ class Product {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function matches($terms) {
|
public function matches($terms) {
|
||||||
foreach($terms as $fieldtype => $values) {
|
foreach($terms as $field => $values) {
|
||||||
$testfield = null;
|
$matchvalues = array();
|
||||||
switch($fieldtype) {
|
if(property_exists($this, $field)) {
|
||||||
case 'tag':
|
$matchvalues[] = $this->$field;
|
||||||
foreach($values as $value) {
|
} else if(array_key_exists($field, $this->get_info())) {
|
||||||
if(!in_array($value, $this->tags)) {
|
$matchvalues[] = $this->get_info()[$field];
|
||||||
return false;
|
} else {
|
||||||
}
|
switch($field) {
|
||||||
}
|
case 'tag':
|
||||||
break;
|
$matchvalues = $this->get_tags();
|
||||||
case 'status':
|
case 'status':
|
||||||
$loan = $this->get_active_loan();
|
$matchvalues[] = $this->get_loan_status();
|
||||||
foreach($values as $value) {
|
case 'fritext':
|
||||||
switch($value) {
|
$matchvalues[] = $this->name;
|
||||||
case 'on_loan':
|
$matchvalues[] = $this->serial;
|
||||||
if(!$loan) {
|
$matchvalues[] = $this->invoice;
|
||||||
return false;
|
$matchvalues = array_merge($matchvalues,
|
||||||
}
|
$this->get_tags(),
|
||||||
break;
|
array_values(
|
||||||
case 'no_loan':
|
$this->get_info()));
|
||||||
if($loan) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'overdue':
|
|
||||||
if(!$loan || !$loan->is_overdue()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'words':
|
|
||||||
$testfield = $this->name;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if(property_exists($this, $fieldtype)) {
|
|
||||||
$testfield = $this->$fieldtype;
|
|
||||||
} elseif(array_key_exists($fieldtype, $this->info)) {
|
|
||||||
$tesfield = $this->info[$fieldtype];
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if($testfield !== null) {
|
|
||||||
foreach($values as $value) {
|
|
||||||
$test = strtolower($testfield);
|
|
||||||
if(strpos($test, $value) === false) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if(!match($values, $matchvalues)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -427,6 +411,20 @@ class Product {
|
|||||||
return true;
|
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() {
|
public function get_active_loan() {
|
||||||
$find = prepare('select `id` from `loan`
|
$find = prepare('select `id` from `loan`
|
||||||
where `returntime` is null and product=?');
|
where `returntime` is null and product=?');
|
||||||
@ -694,35 +692,27 @@ class User {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function matches($terms) {
|
public function matches($terms) {
|
||||||
foreach($terms as $fieldtype => $values) {
|
foreach($terms as $field => $values) {
|
||||||
switch($fieldtype) {
|
$matchvalues = array();
|
||||||
case 'words':
|
if($field == 'name') {
|
||||||
foreach($values as $value) {
|
$matchvalues[] = $this->name;
|
||||||
if(strpos($this->name, $value) !== false) {
|
$matchvalues[] = $this->get_displayname();
|
||||||
continue;
|
} else if(property_exists($this, $field)) {
|
||||||
}
|
$matchvalues[] = $this->$field;
|
||||||
$name = strtolower($this->get_displayname());
|
} else if($field == 'fritext') {
|
||||||
if(strpos($name, $value) !== false) {
|
$matchvalues[] = $this->name;
|
||||||
continue;
|
$matchvalues[] = $this->get_displayname();
|
||||||
}
|
$matchvalues[] = $this->notes;
|
||||||
return false;
|
} else {
|
||||||
}
|
return false;
|
||||||
break;
|
}
|
||||||
default:
|
if(!match($values, $matchvalues)) {
|
||||||
if(!property_exists($this, $fieldtype)) {
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
foreach($values as $value) {
|
|
||||||
if($this->$fieldtype != $value) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function get_displayname() {
|
public function get_displayname() {
|
||||||
global $ldap;
|
global $ldap;
|
||||||
try {
|
try {
|
||||||
|
268
include/view.php
268
include/view.php
@ -32,6 +32,28 @@ abstract class Responder {
|
|||||||
public function __construct() {
|
public function __construct() {
|
||||||
$this->fragments = get_fragments('./html/fragments.html');
|
$this->fragments = get_fragments('./html/fragments.html');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final protected function escape_tags($tags) {
|
||||||
|
foreach($tags as $key => $tag) {
|
||||||
|
$tags[$key] = str_replace(array("'",
|
||||||
|
'"'),
|
||||||
|
array(''',
|
||||||
|
'"'),
|
||||||
|
strtolower($tag));
|
||||||
|
}
|
||||||
|
return $tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
final protected function unescape_tags($tags) {
|
||||||
|
foreach($tags as $key => $tag) {
|
||||||
|
$tags[$key] = str_replace(array(''',
|
||||||
|
'"'),
|
||||||
|
array("'",
|
||||||
|
'"'),
|
||||||
|
strtolower($tag));
|
||||||
|
}
|
||||||
|
return $tags;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class Page extends Responder {
|
abstract class Page extends Responder {
|
||||||
@ -46,7 +68,8 @@ abstract class Page extends Responder {
|
|||||||
'products' => 'Artiklar',
|
'products' => 'Artiklar',
|
||||||
'users' => 'Låntagare',
|
'users' => 'Låntagare',
|
||||||
'inventory' => 'Inventera',
|
'inventory' => 'Inventera',
|
||||||
'history' => 'Historik');
|
'history' => 'Historik',
|
||||||
|
'search' => 'Sök');
|
||||||
private $template_parts = array();
|
private $template_parts = array();
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
@ -94,12 +117,17 @@ abstract class Page extends Responder {
|
|||||||
private function build_menu() {
|
private function build_menu() {
|
||||||
$menu = '';
|
$menu = '';
|
||||||
foreach($this->menuitems as $page => $title) {
|
foreach($this->menuitems as $page => $title) {
|
||||||
|
$align = 'left';
|
||||||
$active = '';
|
$active = '';
|
||||||
if($this->page == $page) {
|
if($this->page == $page) {
|
||||||
$active = 'active';
|
$active = 'active';
|
||||||
}
|
}
|
||||||
|
if($page == 'search') {
|
||||||
|
$align = 'right';
|
||||||
|
}
|
||||||
$menu .= replace(array('title' => $title,
|
$menu .= replace(array('title' => $title,
|
||||||
'page' => $page,
|
'page' => $page,
|
||||||
|
'align' => $align,
|
||||||
'active' => $active),
|
'active' => $active),
|
||||||
$this->template_parts['menuitem']);
|
$this->template_parts['menuitem']);
|
||||||
}
|
}
|
||||||
@ -334,121 +362,152 @@ abstract class Page extends Responder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class SearchPage extends Page {
|
class SearchPage extends Page {
|
||||||
private $querystr = '';
|
|
||||||
private $terms = array();
|
private $terms = array();
|
||||||
|
|
||||||
public function __construct() {
|
public function __construct() {
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
$this->subtitle = 'Sökresultat för ';
|
$this->terms = $_GET;
|
||||||
if(isset($_GET['q'])) {
|
unset($this->terms['q'], $this->terms['page']);
|
||||||
$this->querystr = $_GET['q'];
|
|
||||||
$this->subtitle .= "'$this->querystr'";
|
|
||||||
$orterms = preg_split('/[[:space:]]+or[[:space:]]+/',
|
|
||||||
strtolower($this->querystr),
|
|
||||||
-1,
|
|
||||||
PREG_SPLIT_NO_EMPTY);
|
|
||||||
foreach($orterms as $orterm) {
|
|
||||||
$searchpart = array();
|
|
||||||
$terms = preg_split('/[[:space:]]+/',
|
|
||||||
$orterm,
|
|
||||||
-1,
|
|
||||||
PREG_SPLIT_NO_EMPTY);
|
|
||||||
foreach($terms as $term) {
|
|
||||||
$key = '';
|
|
||||||
$value = '';
|
|
||||||
if(strpos($term, ':') !== false) {
|
|
||||||
$pair = explode(':', $term);
|
|
||||||
$key = $pair[0];
|
|
||||||
switch($key) {
|
|
||||||
case 'namn':
|
|
||||||
$key = 'name';
|
|
||||||
break;
|
|
||||||
case 'fakturanummer':
|
|
||||||
$key = 'invoice';
|
|
||||||
break;
|
|
||||||
case 'serienummer':
|
|
||||||
$key = 'serial';
|
|
||||||
break;
|
|
||||||
case 'anteckningar':
|
|
||||||
$key = 'notes';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$value = $pair[1];
|
|
||||||
if($key == 'status') {
|
|
||||||
switch($value) {
|
|
||||||
case 'inne':
|
|
||||||
$value = 'no_loan';
|
|
||||||
break;
|
|
||||||
case 'ute':
|
|
||||||
case 'utlånad':
|
|
||||||
$value = 'on_loan';
|
|
||||||
break;
|
|
||||||
case 'sen':
|
|
||||||
case 'försenad':
|
|
||||||
case 'försenat':
|
|
||||||
$value = 'overdue';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$key = 'words';
|
|
||||||
$value = $term;
|
|
||||||
}
|
|
||||||
if(!isset($searchpart[$key])) {
|
|
||||||
$searchpart[$key] = array();
|
|
||||||
}
|
|
||||||
$searchpart[$key][] = $value;
|
|
||||||
}
|
|
||||||
$this->terms[] = $searchpart;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function do_search() {
|
private function do_search() {
|
||||||
$out = array('users' => array(),
|
$out = array();
|
||||||
'products' => array());
|
if(!$this->terms) {
|
||||||
if(!$this->querystr) {
|
|
||||||
return $out;
|
return $out;
|
||||||
}
|
}
|
||||||
$out['users'] = $this->search('user');
|
$terms = $this->translate_keys($this->terms);
|
||||||
$out['products'] = $this->search('product');
|
foreach(array('user', 'product') as $type) {
|
||||||
|
$result = $this->search($type, $terms);
|
||||||
|
if($result) {
|
||||||
|
$out[$type] = $result;
|
||||||
|
}
|
||||||
|
}
|
||||||
return $out;
|
return $out;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function search($type) {
|
private function translate_keys($terms) {
|
||||||
|
$translated = array();
|
||||||
|
foreach($terms as $key => $value) {
|
||||||
|
$newkey = $key;
|
||||||
|
switch($key) {
|
||||||
|
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);
|
$items = get_items($type);
|
||||||
$out = array();
|
$out = array();
|
||||||
foreach($items as $item) {
|
foreach($items as $item) {
|
||||||
foreach($this->terms as $term) {
|
if($item->matches($terms)) {
|
||||||
if($item->matches($term)) {
|
$out[] = $item;
|
||||||
$out[] = $item;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $out;
|
return $out;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function render_body() {
|
protected function render_body() {
|
||||||
$hits = $this->do_search();
|
$terms = '';
|
||||||
$nohits = true;
|
foreach($this->terms as $key => $value) {
|
||||||
if($hits['users']) {
|
if(!is_array($value)) {
|
||||||
print(replace(array('title' => 'Låntagare'),
|
$terms .= replace(array('term' => ucfirst($key).": $value",
|
||||||
$this->fragments['subtitle']));
|
'key' => $key,
|
||||||
print($this->build_user_table($hits['users']));
|
'value' => $value),
|
||||||
$nohits = false;
|
$this->fragments['search_term']);
|
||||||
|
} else {
|
||||||
|
foreach($value as $item) {
|
||||||
|
$terms .= replace(array('term' => ucfirst($key).": $item",
|
||||||
|
'key' => $key,
|
||||||
|
'value' => $item),
|
||||||
|
$this->fragments['search_term']);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if($hits['products']) {
|
print(replace(array('terms' => $terms),
|
||||||
print(replace(array('title' => 'Artiklar'),
|
$this->fragments['search_form']));
|
||||||
$this->fragments['subtitle']));
|
if($this->terms) {
|
||||||
print($this->build_product_table($hits['products']));
|
$hits = $this->do_search();
|
||||||
$nohits = false;
|
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);
|
||||||
}
|
}
|
||||||
if($nohits) {
|
|
||||||
print('Inga träffar.');
|
|
||||||
}
|
|
||||||
print(replace(array('title' => 'Hjälp'),
|
|
||||||
$this->fragments['subtitle']));
|
|
||||||
print($this->fragments['search_help']);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -522,7 +581,7 @@ class ProductPage extends Page {
|
|||||||
$this->fragments['info_item']);
|
$this->fragments['info_item']);
|
||||||
}
|
}
|
||||||
$tags = '';
|
$tags = '';
|
||||||
foreach($this->product->get_tags() as $tag) {
|
foreach($this->escape_tags($this->product->get_tags()) as $tag) {
|
||||||
$tags .= replace(array('tag' => ucfirst($tag)),
|
$tags .= replace(array('tag' => ucfirst($tag)),
|
||||||
$this->fragments['tag']);
|
$this->fragments['tag']);
|
||||||
}
|
}
|
||||||
@ -962,8 +1021,8 @@ class Ajax extends Responder {
|
|||||||
$name = $info['name'];
|
$name = $info['name'];
|
||||||
$serial = $info['serial'];
|
$serial = $info['serial'];
|
||||||
$invoice = $info['invoice'];
|
$invoice = $info['invoice'];
|
||||||
$tags = $this->extract_tags($info['tags']);
|
$tags = $this->unescape_tags($info['tag']);
|
||||||
foreach(array('id', 'name', 'serial', 'invoice', 'tags') as $key) {
|
foreach(array('id', 'name', 'serial', 'invoice', 'tag') as $key) {
|
||||||
unset($info[$key]);
|
unset($info[$key]);
|
||||||
}
|
}
|
||||||
if(!$name) {
|
if(!$name) {
|
||||||
@ -1062,7 +1121,7 @@ class Ajax extends Responder {
|
|||||||
private function save_template() {
|
private function save_template() {
|
||||||
$info = $_POST;
|
$info = $_POST;
|
||||||
$name = $info['template'];
|
$name = $info['template'];
|
||||||
$tags = $this->extract_tags($info['tags']);
|
$tags = $this->unescape_tags($info['tag']);
|
||||||
foreach(array('template',
|
foreach(array('template',
|
||||||
'id',
|
'id',
|
||||||
'name',
|
'name',
|
||||||
@ -1137,21 +1196,6 @@ class Ajax extends Responder {
|
|||||||
return new Failure('Artikeln är redan skrotad.');
|
return new Failure('Artikeln är redan skrotad.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function extract_tags($string) {
|
|
||||||
$tags = explode(',', strtolower($string));
|
|
||||||
# Unescape specials
|
|
||||||
foreach($tags as $key => $tag) {
|
|
||||||
$tags[$key] = str_replace(array(',',
|
|
||||||
''',
|
|
||||||
'"'),
|
|
||||||
array(',',
|
|
||||||
"'",
|
|
||||||
'"'),
|
|
||||||
$tag);
|
|
||||||
}
|
|
||||||
return $tags;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class Result {
|
class Result {
|
||||||
|
95
script.js
95
script.js
@ -61,9 +61,31 @@ function reloadOrError(result) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ucfirst(string) {
|
||||||
|
return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase()
|
||||||
|
}
|
||||||
|
|
||||||
|
function fixDuplicateInputNames(fields) {
|
||||||
|
var names = {}
|
||||||
|
for(var i = 0; i < fields.length; i++) {
|
||||||
|
var name = fields[i].name
|
||||||
|
if(name.endsWith('[]')) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if(names.hasOwnProperty(name)) {
|
||||||
|
fields[i].name = name + '[]'
|
||||||
|
fields[names[name]].name = name + '[]'
|
||||||
|
} else {
|
||||||
|
names[name] = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
function dataListFromForm(form, filter = function(field) {return true}) {
|
function dataListFromForm(form, filter = function(field) {return true}) {
|
||||||
var out = []
|
var out = []
|
||||||
var fields = form.querySelectorAll('input,textarea')
|
var fields = form.querySelectorAll('input,textarea')
|
||||||
|
fixDuplicateInputNames(fields)
|
||||||
for(var i = 0; i < fields.length; i++) {
|
for(var i = 0; i < fields.length; i++) {
|
||||||
if(filter(fields[i])) {
|
if(filter(fields[i])) {
|
||||||
out.push([fields[i].name, fields[i].value])
|
out.push([fields[i].name, fields[i].value])
|
||||||
@ -166,9 +188,9 @@ function suggest(input, type) {
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'tag':
|
case 'tag':
|
||||||
var taglist = document.querySelectorAll('#tags > p')
|
var taglist = document.querySelectorAll('#tags .tag > input')
|
||||||
for(var i = 0; i < taglist.length; i++) {
|
for(var i = 0; i < taglist.length; i++) {
|
||||||
var tag = taglist[i].firstElementChild.dataset.name
|
var tag = taglist[i].name
|
||||||
existing.push(tag.toLowerCase())
|
existing.push(tag.toLowerCase())
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
@ -191,8 +213,7 @@ function suggest(input, type) {
|
|||||||
}
|
}
|
||||||
var next = document.createElement('option')
|
var next = document.createElement('option')
|
||||||
if(capitalize) {
|
if(capitalize) {
|
||||||
next.value = suggestion.charAt(0).toUpperCase()
|
next.value = ucfirst(suggestion)
|
||||||
+ suggestion.slice(1)
|
|
||||||
} else {
|
} else {
|
||||||
next.value = suggestion
|
next.value = suggestion
|
||||||
}
|
}
|
||||||
@ -220,7 +241,7 @@ function addField(event) {
|
|||||||
{'type': 'error',
|
{'type': 'error',
|
||||||
'message': 'Det finns redan ett fält med det namnet.'})
|
'message': 'Det finns redan ett fält med det namnet.'})
|
||||||
}
|
}
|
||||||
var name = key.charAt(0).toUpperCase() + key.slice(1)
|
var name = ucfirst(key)
|
||||||
var render = function(fragment) {
|
var render = function(fragment) {
|
||||||
var temp = document.createElement('template')
|
var temp = document.createElement('template')
|
||||||
fragment = replace(fragment, [
|
fragment = replace(fragment, [
|
||||||
@ -245,9 +266,8 @@ function addField(event) {
|
|||||||
getFragment('info_item', render)
|
getFragment('info_item', render)
|
||||||
}
|
}
|
||||||
|
|
||||||
function escapeTag(tag) {
|
function escapeText(text) {
|
||||||
return tag
|
return text
|
||||||
.replace(/,/, ',')
|
|
||||||
.replace(/'/, ''')
|
.replace(/'/, ''')
|
||||||
.replace(/"/, '"')
|
.replace(/"/, '"')
|
||||||
}
|
}
|
||||||
@ -259,15 +279,15 @@ function addTag(event) {
|
|||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
var tr = event.currentTarget.parentNode.parentNode
|
var tr = event.currentTarget.parentNode.parentNode
|
||||||
var field = tr.querySelector('.newtag')
|
var field = tr.querySelector('.newtag')
|
||||||
var tagname = escapeTag(field.value)
|
var tagname = escapeText(field.value)
|
||||||
if(!tagname) {
|
if(!tagname) {
|
||||||
return showResult({'type': 'error',
|
return showResult({'type': 'error',
|
||||||
'message': 'Taggen måste ha ett namn.'})
|
'message': 'Taggen måste ha ett namn.'})
|
||||||
}
|
}
|
||||||
tagname = tagname.charAt(0).toUpperCase() + tagname.slice(1)
|
tagname = ucfirst(tagname)
|
||||||
var tagElements = tr.querySelectorAll('.tag')
|
var tagElements = tr.querySelectorAll('.tag > input')
|
||||||
for(var i = 0; i < tagElements.length; i++) {
|
for(var i = 0; i < tagElements.length; i++) {
|
||||||
var oldtag = tagElements[i].dataset['name']
|
var oldtag = tagElements[i].name
|
||||||
if(tagname.toLowerCase() == oldtag.toLowerCase()) {
|
if(tagname.toLowerCase() == oldtag.toLowerCase()) {
|
||||||
return showResult({'type': 'error',
|
return showResult({'type': 'error',
|
||||||
'message': 'Det finns redan en sån tagg på artikeln.'})
|
'message': 'Det finns redan en sån tagg på artikeln.'})
|
||||||
@ -383,12 +403,6 @@ function productDataList(form) {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
var datalist = dataListFromForm(form, filter)
|
var datalist = dataListFromForm(form, filter)
|
||||||
var tagElements = form.querySelectorAll('.tag')
|
|
||||||
var tags = []
|
|
||||||
for(var i = 0; i < tagElements.length; i++) {
|
|
||||||
tags.push(escapeTag(tagElements[i].dataset['name']))
|
|
||||||
}
|
|
||||||
datalist.push(['tags', tags])
|
|
||||||
return datalist
|
return datalist
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -419,3 +433,48 @@ function discardProduct(event) {
|
|||||||
}
|
}
|
||||||
ajaxRequest('discardproduct', dataListFromForm(form), render)
|
ajaxRequest('discardproduct', dataListFromForm(form), render)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function searchInput(event) {
|
||||||
|
if(event.key != "Enter") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var input = event.target
|
||||||
|
var term = input.value.toLowerCase()
|
||||||
|
if(term === '') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
event.preventDefault()
|
||||||
|
var terms = document.querySelector('#terms')
|
||||||
|
var parts = escapeText(term).trim().split(':')
|
||||||
|
var parsedTerm = 'Fritext: ' + parts[0]
|
||||||
|
var key = 'fritext'
|
||||||
|
var value = parts[0]
|
||||||
|
if(parts.length > 1) {
|
||||||
|
key = parts[0].trim()
|
||||||
|
value = parts.slice(1).join(':').trim()
|
||||||
|
parsedTerm = ucfirst(key) + ': ' + value
|
||||||
|
}
|
||||||
|
var render = function(fragment) {
|
||||||
|
var temp = document.createElement('template')
|
||||||
|
fragment = replace(fragment, [['term', parsedTerm],
|
||||||
|
['key', key],
|
||||||
|
['value', value]])
|
||||||
|
temp.innerHTML = fragment
|
||||||
|
terms.append(temp.content.firstChild)
|
||||||
|
input.value = ''
|
||||||
|
}
|
||||||
|
getFragment('search_term', render)
|
||||||
|
}
|
||||||
|
|
||||||
|
function doSearch(event) {
|
||||||
|
var form = document.querySelector('#search')
|
||||||
|
var fields = form.querySelectorAll('input,textarea')
|
||||||
|
fixDuplicateInputNames(fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeTerm(event) {
|
||||||
|
event.preventDefault()
|
||||||
|
var term = event.currentTarget.parentNode
|
||||||
|
var parent = term.parentNode
|
||||||
|
parent.remove(term)
|
||||||
|
}
|
||||||
|
10
style.css
10
style.css
@ -118,14 +118,20 @@ input[type="text"].newtemplate {
|
|||||||
padding-left: 2px;
|
padding-left: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tagremove {
|
.tagremove, .termremove {
|
||||||
background-color: #e17e33;
|
background-color: #e17e33;
|
||||||
padding-left: 2px;
|
padding-left: 2px;
|
||||||
padding-right: 2px;
|
padding-right: 2px;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tagremove:hover {
|
.tagremove:hover, .termremove:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.term {
|
||||||
|
background-color: #ebf0f5;
|
||||||
|
margin: 5px;
|
||||||
|
padding-left: 2px;
|
||||||
|
}
|
||||||
|
10
template.css
10
template.css
@ -220,28 +220,22 @@ button:disabled, input[type="submit"]:disabled {
|
|||||||
background-color: #002E5F;
|
background-color: #002E5F;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
font-size: 130%;
|
font-size: 130%;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu .item {
|
#menu .item {
|
||||||
padding-left: 12px;
|
padding: 8px 12px;
|
||||||
padding-right: 12px;
|
|
||||||
margin-left:6px;
|
margin-left:6px;
|
||||||
margin-right: 6px;
|
margin-right: 6px;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: #005B7F;
|
color: #005B7F;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
padding-top: 8px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu .active {
|
#menu .active {
|
||||||
background-color: #FFF;
|
background-color: #FFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
#menu #search {
|
|
||||||
padding-top: 7px;
|
|
||||||
padding-right: 0px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#contents {
|
#contents {
|
||||||
font-family: Georgia, "Times New Roman", Times, serif;
|
font-family: Georgia, "Times New Roman", Times, serif;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user