From 1ac861570bfecf35d4bf44a31e52f3d85b7d173e Mon Sep 17 00:00:00 2001 From: jklmnn Date: Wed, 23 Sep 2015 15:15:47 +0200 Subject: [PATCH 01/42] Fixed undefined index notices. --- api.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/api.php b/api.php index 4dfc75c..09e31a4 100644 --- a/api.php +++ b/api.php @@ -10,11 +10,11 @@ function hash_equals($a, $b) { } } -$itemName = $_POST['item']; -$itemCount = $_POST['count']; -$jsonData = $_POST['jsonArray']; -$function = $_POST['function']; -$auth = $_POST['auth']; +$itemName = array_key_exists('item', $_POST) ? $_POST['item'] : null; +$itemCount = array_key_exists('count', $_POST) ? $_POST['count'] : null; +$jsonData = array_key_exists('jsonArray', $_POST) ? $_POST['jsonArray'] : null; +$function = array_key_exists('function', $_POST) ? $_POST['function'] : null; +$auth = array_key_exists('auth', $_POST) ? $_POST['auth'] : null; include('config.php'); From 8e1f31636b6b29142ff7dd3422deb835bfab40e4 Mon Sep 17 00:00:00 2001 From: jklmnn Date: Thu, 24 Sep 2015 17:04:12 +0200 Subject: [PATCH 02/42] Added pdo db_connector.php. --- db_connector.php | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 db_connector.php diff --git a/db_connector.php b/db_connector.php new file mode 100644 index 0000000..fb53dd9 --- /dev/null +++ b/db_connector.php @@ -0,0 +1,34 @@ + API_ERROR_DATABASE_CONNECT, 'content' => $e->getMessage()))); + } + break; + case 'MySQL': + $db_pdo="mysql:host=".$dbargs['host'].";dbname=".$dbargs['db']; + try{ + $db = new PDO($db_pdo, $dbargs['user'], $dbargs['password']); + }catch(PDOException $e){ + die(json_encode(array('type' => API_ERROR_DATABASE_CONNECT, 'content' => $e->getMessage()))); + } + break; + default: + die(json_encode(array('type' => API_ERROR_MISSING_PARAMETER, 'content' => "Missing database parameters."))); + } + } + + } From de9c62248063955ee7050e74e29e1589aff7b11a Mon Sep 17 00:00:00 2001 From: Gerrit Giehl Date: Thu, 24 Sep 2015 18:49:19 +0200 Subject: [PATCH 03/42] modified: .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 96374c4..3b7c92d 100644 --- a/.gitignore +++ b/.gitignore @@ -41,3 +41,5 @@ $RECYCLE.BIN/ Network Trash Folder Temporary Items .apdisk + +qr-config.png From f3ce8dc942f2cf6da2c1a7c70b01055d208044e9 Mon Sep 17 00:00:00 2001 From: Gerrit Giehl Date: Thu, 24 Sep 2015 18:50:19 +0200 Subject: [PATCH 04/42] qr-code generation added --- .gitmodules | 3 +++ INSTALL.php | 19 +++++++++++++++++++ README.md | 1 + lib/phpqrcode-git | 1 + tmp/.gitkeep | 0 5 files changed, 24 insertions(+) create mode 100644 .gitmodules create mode 160000 lib/phpqrcode-git create mode 100644 tmp/.gitkeep diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..29febea --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/phpqrcode-git"] + path = lib/phpqrcode-git + url = git://git.code.sf.net/p/phpqrcode/git diff --git a/INSTALL.php b/INSTALL.php index fa05722..5072392 100644 --- a/INSTALL.php +++ b/INSTALL.php @@ -137,6 +137,7 @@ function generateRandomPWD($length = 25) { EOCONFIG; } + //when DB Type MySQL create table if($dbtype == "MySQL") { @@ -197,7 +198,25 @@ function generateRandomPWD($length = 25) { } else { + echo ''; + + if(is_writable(dirname("tmp/config-qr.png"))) { + include('lib/phpqrcode-git/lib/full/qrlib.php'); + $url = "http" . (($_SERVER['SERVER_PORT'] == 443) ? "s://" : "://") . $_SERVER['HTTP_HOST']; + $uri = substr($_SERVER['REQUEST_URI'], 0, strrpos($_SERVER['REQUEST_URI'], '/') + 1); + $api_url = $url . $uri . "api.php"; + $ak = $_POST['apikey']; + $ssl = (($_SERVER['SERVER_PORT'] == 443) ? "true" : "false"); + + $qr_code = "{ \"url\": \"$api_url\", \"apikey\": \"$ak\", \"ssl\": $ssl}"; + + QRcode::png("$qr_code", "tmp/config-qr.png"); + echo <<
+

Open your app and scan code for automatically configuration

+EOQR; + } } } else diff --git a/README.md b/README.md index 8937e32..4e14a4f 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,7 @@ ###Requirements * PHP >= 5.3.7 +* php-gd * Apache Websever (we recommend a TLS Connection to the Server) * MySQL or SQLite (you can select in the Install Script) diff --git a/lib/phpqrcode-git b/lib/phpqrcode-git new file mode 160000 index 0000000..863ffff --- /dev/null +++ b/lib/phpqrcode-git @@ -0,0 +1 @@ +Subproject commit 863ffffac4c9d22e522464e325cbcbadfbb26470 diff --git a/tmp/.gitkeep b/tmp/.gitkeep new file mode 100644 index 0000000..e69de29 From 458100c1b491d6a098dfd24e5762b33e8a0f8e47 Mon Sep 17 00:00:00 2001 From: jklmnn Date: Fri, 25 Sep 2015 12:58:17 +0200 Subject: [PATCH 05/42] Completed db connector according to current api. --- CONSTANTS.php | 1 + db_connector.php | 122 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 120 insertions(+), 3 deletions(-) diff --git a/CONSTANTS.php b/CONSTANTS.php index 32a6cc5..287d56f 100644 --- a/CONSTANTS.php +++ b/CONSTANTS.php @@ -26,4 +26,5 @@ define('API_ERROR_DELETE', 6003); define('API_ERROR_SAVE', 6004); define('API_ERROR_CLEAR', 6005); + define('API_ERROR_LIST', 6006); ?> diff --git a/db_connector.php b/db_connector.php index fb53dd9..a9f93ca 100644 --- a/db_connector.php +++ b/db_connector.php @@ -6,14 +6,15 @@ class DataBase{ var $db; var $type; + var $table = "shoppinglist"; function __construct($dbtype, $dbargs){ - $type = $dbtype; + $this->type = $dbtype; switch($dbtype){ case 'SQLite': $db_pdo="sqlite:".$dbargs['file']; try{ - $db = new PDO($db_pdo); + $this->db = new PDO($db_pdo); }catch(PDOException $e){ die(json_encode(array('type' => API_ERROR_DATABASE_CONNECT, 'content' => $e->getMessage()))); } @@ -21,7 +22,7 @@ function __construct($dbtype, $dbargs){ case 'MySQL': $db_pdo="mysql:host=".$dbargs['host'].";dbname=".$dbargs['db']; try{ - $db = new PDO($db_pdo, $dbargs['user'], $dbargs['password']); + $this->db = new PDO($db_pdo, $dbargs['user'], $dbargs['password']); }catch(PDOException $e){ die(json_encode(array('type' => API_ERROR_DATABASE_CONNECT, 'content' => $e->getMessage()))); } @@ -29,6 +30,121 @@ function __construct($dbtype, $dbargs){ default: die(json_encode(array('type' => API_ERROR_MISSING_PARAMETER, 'content' => "Missing database parameters."))); } + $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); } + function init(){ + $sql = "CREATE table $this->table( + item STRING PRIMARY KEY, + count INT NOT NULL, + checked INT NOT NULL, + category STRING);"; + try{ + $this->db->exec($sql); + }catch(PDOException $e){ + die(json_encode(array('type' => API_ERROR_UNKNOWN, 'content' => $e->getMessage()))); + } + } + + function listall(){ + try{ + $sql = "SELECT * FROM $this->table;"; + $val = $this->db->query($sql); + $stack = array(); + foreach($val as $row){ + array_push($stack, array( + 'itemTitle' => $row['item'], + 'itemCount' => $row['count'], + 'checked' => (bool)$row['checked'], + 'itemCategory' => $row['category'])); + } + if(count($stack) == 0){ + return json_encode(array('type' => API_SUCCESS_LIST_EMPTY)); + }else{ + return json_encode(array('type' => API_SUCCESS_LIST, 'items' => $stack)); + } + }catch(PDOException $e){ + die(json_encode(array('type' => API_ERROR_LIST, 'content' => $e->getMessage()))); + } + } + + function exists($item){ + $stmt = $this->db->prepare("SELECT * from $this->table WHERE item=:item;"); + $stmt->execute(array(':item'=>$item)); + return (bool)count($stmt->fetchAll()); + } + + function save($item, $count){ + $checked = false; + try{ + $stmt = $this->db->prepare("INSERT INTO $this->table (item, count, checked) VALUES (:item, :count, :checked);"); + $stmt->execute(array(':item' => $item, ':count' => $count, ':checked' => (int)$checked)); + return json_encode(array('type' => API_SUCCESS_SAVE, 'content' => $item.' saved.')); + }catch(PDOException $e){ + return json_encode(array('type' => API_ERROR_SAVE, 'content' => $e->getMessage())); + } + } + + function saveMultiple($jsonData){ + if(empty($jsonData)){ + die(json_encode(array('type' => API_ERROR_MISSING_PARAMETER, 'content' => 'parameter missing for saveMultiple'))); + } + $itemList = json_decode($jsonData, true); + try{ + $stmt = $this->db->prepare("INSERT INTO $this->table (item, count, checked) VALUES (:item, :count, :checked);"); + $checked = false; + foreach($itemList as $item){ + $stmt->execute(array(':item' => $item['itemTitle'], ':count' => $item['itemCount'], ':checked' => (int)$checked)); + } + return json_encode(array('type' => API_SUCCESS_SAVE, 'content' => 'Multiple items saved.')); + }catch(PDOException $e){ + return json_encode(array('type' => API_ERROR_SAVE, 'content' => $e->getMessage())); + } + } + + function update($item, $count){ + try{ + $stmt = $this->db->prepare("UPDATE $this->table SET count=:count WHERE item=:item;"); + $stmt->execute(array(':item' => $item, ':count' => $count)); + return json_encode(array('type' => API_SUCCESS_UPDATE, 'content' => 'Update successfull.')); + }catch(PDOException $e){ + return json_encode(array('type' => API_ERROR_UPDATE_, 'content' => $e->getMessage())); + } + } + + function deleteMultiple($jsonData){ + if(empty($jsonData)) { + die(json_encode(array('type' => API_ERROR_MISSING_PARAMETER, 'content' => 'parameter missing for deleteMultiple'))); + } + $itemList = json_decode($jsonData, true); + try{ + $stmt = $this->db->prepare("DELETE FROM $this->table WHERE item=:item;"); + foreach($itemList as $item){ + $stmt->execute(array(':item' => $item['itemTitle'])); + } + return json_encode(array('type' => API_SUCCESS_DELETE, 'content' => 'Multiple items deleted.')); + }catch(PDOException $e){ + return json_encode(array('type' => API_ERROR_DELETE, 'content' => $e->getMessage())); + } + } + + function delete($item){ + try{ + $stmt = $this->db->prepare("DELETE FROM $this->table WHERE item=:item"); + $stmt->execute(array(':item' => $item)); + return json_encode(array('type' => API_SUCCESS_DELETE, 'content' => 'Item deleted.')); + }catch(PDOException $e){ + return json_encode(array('type' => API_ERROR_DELETE, 'content' => $e->getMessage())); + } + } + + function clear(){ + try{ + $stmt = $this->db->exec("TRUNCATE TABLE $this->table;"); + return json_encode(array('type' => API_SUCCESS_CLEAR, 'content' => 'Database cleared.')); + }catch(PDOException $e){ + return json_encode(array('type' => API_ERROR_CLEAR, 'content' => $e->getMessage())); + } + } + } From d1f7d017a0e14593d66671e3266916ec73877a9c Mon Sep 17 00:00:00 2001 From: jklmnn Date: Fri, 25 Sep 2015 13:34:48 +0200 Subject: [PATCH 06/42] replaced old database connectors in api.php with db_connector. --- api.php | 8 +++----- db_connector.php | 33 +++++++++++++++++++++++---------- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/api.php b/api.php index 09e31a4..ab33a50 100644 --- a/api.php +++ b/api.php @@ -29,11 +29,9 @@ function hash_equals($a, $b) { switch($dataBase){ case 'SQLite': - $dbConnector = "sqlite_connector.php"; $dbConfig = $SQLiteConfig; break; case 'MySQL': - $dbConnector = "mysql_connector.php"; $dbConfig = $MySQLConfig; break; default: @@ -43,14 +41,14 @@ function hash_equals($a, $b) { } -include $dbConnector; +include('db_connector.php'); if (!hash_equals($authKey, crypt($auth, $authKey))){ die (json_encode(array('type' => API_ERROR_403, 'content' => 'Authentication failed.'))); } - $db = NEW DataBase($dbConfig); - + $db = NEW DataBase($dataBase, $dbConfig); + $db->init(); //TODO: put this to INSTALL.php switch ($function){ case 'listall': echo $db->listall(); diff --git a/db_connector.php b/db_connector.php index a9f93ca..c00ab53 100644 --- a/db_connector.php +++ b/db_connector.php @@ -42,7 +42,7 @@ function init(){ try{ $this->db->exec($sql); }catch(PDOException $e){ - die(json_encode(array('type' => API_ERROR_UNKNOWN, 'content' => $e->getMessage()))); + //die(json_encode(array('type' => API_ERROR_UNKNOWN, 'content' => $e->getMessage()))); //uncomment after init() has been put to INSTALL.php } } @@ -70,15 +70,19 @@ function listall(){ function exists($item){ $stmt = $this->db->prepare("SELECT * from $this->table WHERE item=:item;"); - $stmt->execute(array(':item'=>$item)); + $stmt->bindParam(':item', $item, PDO::PARAM_STR); + $stmt->execute(); return (bool)count($stmt->fetchAll()); } function save($item, $count){ - $checked = false; try{ + $checked = (int)false; $stmt = $this->db->prepare("INSERT INTO $this->table (item, count, checked) VALUES (:item, :count, :checked);"); - $stmt->execute(array(':item' => $item, ':count' => $count, ':checked' => (int)$checked)); + $stmt->bindParam(':item', $item, PDO::PARAM_STR); + $stmt->bindParam(':count', $count, PDO::PARAM_INT); + $stmt->bindParam(':checked', $checked, PDO::PARAM_INT); + $stmt->execute(); return json_encode(array('type' => API_SUCCESS_SAVE, 'content' => $item.' saved.')); }catch(PDOException $e){ return json_encode(array('type' => API_ERROR_SAVE, 'content' => $e->getMessage())); @@ -89,12 +93,17 @@ function saveMultiple($jsonData){ if(empty($jsonData)){ die(json_encode(array('type' => API_ERROR_MISSING_PARAMETER, 'content' => 'parameter missing for saveMultiple'))); } + echo $jsonData."\n"; + var_dump(json_decode($jsonData)); $itemList = json_decode($jsonData, true); try{ $stmt = $this->db->prepare("INSERT INTO $this->table (item, count, checked) VALUES (:item, :count, :checked);"); - $checked = false; + $checked = (int)false; + $stmt->bindParam(':checked', $checked, PDO::PARAM_INT); foreach($itemList as $item){ - $stmt->execute(array(':item' => $item['itemTitle'], ':count' => $item['itemCount'], ':checked' => (int)$checked)); + $stmt->bindParam(':item', $item['itemTitle'], PDO::PARAM_STR); + $stmt->bindParam(':count', $item['itemCount'], PDO::PARAM_INT); + $stmt->execute(); } return json_encode(array('type' => API_SUCCESS_SAVE, 'content' => 'Multiple items saved.')); }catch(PDOException $e){ @@ -105,7 +114,9 @@ function saveMultiple($jsonData){ function update($item, $count){ try{ $stmt = $this->db->prepare("UPDATE $this->table SET count=:count WHERE item=:item;"); - $stmt->execute(array(':item' => $item, ':count' => $count)); + $stmt->bindParam(':item', $item, PDO::PARAM_STR); + $stmt->bindParam(':count', $count, PDO::PARAM_INT); + $stmt->execute(); return json_encode(array('type' => API_SUCCESS_UPDATE, 'content' => 'Update successfull.')); }catch(PDOException $e){ return json_encode(array('type' => API_ERROR_UPDATE_, 'content' => $e->getMessage())); @@ -114,13 +125,14 @@ function update($item, $count){ function deleteMultiple($jsonData){ if(empty($jsonData)) { - die(json_encode(array('type' => API_ERROR_MISSING_PARAMETER, 'content' => 'parameter missing for deleteMultiple'))); + die(json_encode(array('type' => API_ERROR_MISSING_PARAMETER, 'content' => 'Parameter missing for deleteMultiple.'))); } $itemList = json_decode($jsonData, true); try{ $stmt = $this->db->prepare("DELETE FROM $this->table WHERE item=:item;"); foreach($itemList as $item){ - $stmt->execute(array(':item' => $item['itemTitle'])); + $stmt->bindParam(':item', $item['itemTitle'], PDO::PARAM_STR); + $stmt->execute(); } return json_encode(array('type' => API_SUCCESS_DELETE, 'content' => 'Multiple items deleted.')); }catch(PDOException $e){ @@ -131,7 +143,8 @@ function deleteMultiple($jsonData){ function delete($item){ try{ $stmt = $this->db->prepare("DELETE FROM $this->table WHERE item=:item"); - $stmt->execute(array(':item' => $item)); + $stmt->bindParam(':item', $item, PDO::PARAM_STR); + $stmt->execute(); return json_encode(array('type' => API_SUCCESS_DELETE, 'content' => 'Item deleted.')); }catch(PDOException $e){ return json_encode(array('type' => API_ERROR_DELETE, 'content' => $e->getMessage())); From 7044f871ad1d2efc2deb391d783ca7e91b99aeac Mon Sep 17 00:00:00 2001 From: jklmnn Date: Fri, 25 Sep 2015 16:24:38 +0200 Subject: [PATCH 07/42] Fixed misleading status codes on multiple items. --- db_connector.php | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/db_connector.php b/db_connector.php index c00ab53..4ef7a63 100644 --- a/db_connector.php +++ b/db_connector.php @@ -96,7 +96,12 @@ function saveMultiple($jsonData){ echo $jsonData."\n"; var_dump(json_decode($jsonData)); $itemList = json_decode($jsonData, true); - try{ + $returns = array(); + foreach($itemList as $item){ + array_push($returns, array('itemTitle' => $item['itemTitle'], 'error' => $this->save($item['itemTitle'], $item['itemCount']))); + } + return json_encode($returns); + /*try{ $stmt = $this->db->prepare("INSERT INTO $this->table (item, count, checked) VALUES (:item, :count, :checked);"); $checked = (int)false; $stmt->bindParam(':checked', $checked, PDO::PARAM_INT); @@ -108,7 +113,7 @@ function saveMultiple($jsonData){ return json_encode(array('type' => API_SUCCESS_SAVE, 'content' => 'Multiple items saved.')); }catch(PDOException $e){ return json_encode(array('type' => API_ERROR_SAVE, 'content' => $e->getMessage())); - } + }*/ } function update($item, $count){ @@ -128,7 +133,12 @@ function deleteMultiple($jsonData){ die(json_encode(array('type' => API_ERROR_MISSING_PARAMETER, 'content' => 'Parameter missing for deleteMultiple.'))); } $itemList = json_decode($jsonData, true); - try{ + $returns = array(); + foreach($itemList as $item){ + array_push($returns, array('itemTitle' => $item['itemTitle'], 'error' => $this->delete($item['itemTitle']))); + } + return json_encode($returns); + /*try{ $stmt = $this->db->prepare("DELETE FROM $this->table WHERE item=:item;"); foreach($itemList as $item){ $stmt->bindParam(':item', $item['itemTitle'], PDO::PARAM_STR); @@ -137,7 +147,7 @@ function deleteMultiple($jsonData){ return json_encode(array('type' => API_SUCCESS_DELETE, 'content' => 'Multiple items deleted.')); }catch(PDOException $e){ return json_encode(array('type' => API_ERROR_DELETE, 'content' => $e->getMessage())); - } + }*/ } function delete($item){ From f5bec8cbda58a8ad087e6645c72a0f2ec811e6b0 Mon Sep 17 00:00:00 2001 From: Gerrit Giehl Date: Sat, 26 Sep 2015 09:32:09 +0200 Subject: [PATCH 08/42] generate QR code as base64 string instead as file --- INSTALL.php | 579 +++++++++++++++++++++++++-------------------------- tmp/.gitkeep | 0 2 files changed, 288 insertions(+), 291 deletions(-) delete mode 100644 tmp/.gitkeep diff --git a/INSTALL.php b/INSTALL.php index 5072392..5f4b50e 100644 --- a/INSTALL.php +++ b/INSTALL.php @@ -1,307 +1,304 @@ - - - - - - - Install ShoppingList - - -
-
- =') AND version_compare(phpversion(), '5.5', '<')) - require_once('func.inc.php'); - - //Start on submit form - if(isset($_POST['createConfig'])) - { - function generateRandomPWD($length = 25) { - $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; - $charactersLength = strlen($characters); - $randomString = ''; - for ($i = 0; $i < $length; $i++) { - $randomString .= $characters[rand(0, $charactersLength - 1)]; - } - return $randomString; - } - - //set vars - //hash api key with bcrypt - $apikey = password_hash($_POST['apikey'], PASSWORD_BCRYPT); - $dbtype = $_POST['type']; - $dbhost = $_POST['hostname']; - $dbname = (isset($_POST['database']) ? $_POST['database'] : ""); - $dbuser = $_POST['dbuser']; - $dbpassword = $_POST['dbpassword']; - $createDBUser = (isset($_POST['createDBUser']) ? $_POST['createDBUser'] : ""); - $dbrandom_pwd = generateRandomPWD(); - - $filename = 'shoppinglist.sqlite'; - //set up .htaccess rules for sqlite database if sqlite is used - - $htaccess = "Options ALL -Indexes\nDirectoryIndex api.php"; - if($dbtype === 'SQLite'){ - $htaccess .= "\n\norder allow,deny\ndeny from all\n\n"; - } - - file_put_contents('.htaccess', $htaccess); - //create config file value - - if($createDBUser == "true") { - $config_access = ' + + + + + + +Install ShoppingList + + +
+
"'.$filename.'", - ); - //only for MySQL - $MySQLConfig = array( - \'host\' => "'.$dbhost.'", - \'db\' => "shopping", - \'user\' => "ShoppingListUser", - \'password\' => "'.$dbrandom_pwd.'", - ); -?>'; - - //mysql dump for root access - $dbdump_access = "CREATE USER 'ShoppingListUser'@'".$dbhost."' IDENTIFIED BY '".$dbrandom_pwd."';"; - $dbdump_access2 = "CREATE DATABASE shopping;"; - $dbdump_access3 = "GRANT ALL PRIVILEGES ON shopping.ShoppingList TO 'ShoppingListUser'@'".$dbhost."';"; - - } - else - { - - $config = ' - "'.$filename.'", - ); - //only for MySQL - $MySQLConfig = array( - \'host\' => "'.$dbhost.'", - \'db\' => "'.$dbname.'", - \'user\' => "'.$dbuser.'", - \'password\' => "'.$dbpassword.'", - ); -?>'; - } - - //mysql dump - $dbdump = " - CREATE TABLE ShoppingList ( - item VARCHAR(255), - count VARCHAR(255), - RID int(11) NOT NULL auto_increment, - primary KEY (RID)) - ENGINE=InnoDB DEFAULT COLLATE=utf8_general_ci;"; - - //try to open/create config.php file - //success: write config value - //error: message and get out config.php value - if($handler = fopen('config.php', 'w')) { - if($createDBUser == "true") - fwrite($handler, $config_access); - else - fwrite($handler, $config); - - fclose($handler); - } - else - { - echo <<=') AND version_compare(phpversion(), '5.5', '<')) +require_once('func.inc.php'); + +//Start on submit form +if(isset($_POST['createConfig'])) +{ + function generateRandomPWD($length = 25) { + $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; + $charactersLength = strlen($characters); + $randomString = ''; + for ($i = 0; $i < $length; $i++) { + $randomString .= $characters[rand(0, $charactersLength - 1)]; + } + return $randomString; + } + + function get_qrcode($_apikey) { + + include('lib/phpqrcode-git/lib/full/qrlib.php'); + $url = "http" . (($_SERVER['SERVER_PORT'] == 443) ? "s://" : "://") . $_SERVER['HTTP_HOST']; + $uri = substr($_SERVER['REQUEST_URI'], 0, strrpos($_SERVER['REQUEST_URI'], '/') + 1); + $api_url = $url . $uri . "api.php"; + $ssl = (($_SERVER['SERVER_PORT'] == 443) ? "true" : "false"); + + $qr_code = "{ \"url\": \"$api_url\", \"apikey\": \"$_apikey\", \"ssl\": $ssl}"; + + ob_start(); + QRCode::png($qr_code); + $image_string = base64_encode(ob_get_clean()); + header('Content-Type: text/html'); + + return $image_string; + } + + //set vars + //hash api key with bcrypt + $apikey = password_hash($_POST['apikey'], PASSWORD_BCRYPT); + $dbtype = $_POST['type']; + $dbhost = $_POST['hostname']; + $dbname = (isset($_POST['database']) ? $_POST['database'] : ""); + $dbuser = $_POST['dbuser']; + $dbpassword = $_POST['dbpassword']; + $createDBUser = (isset($_POST['createDBUser']) ? $_POST['createDBUser'] : ""); + $dbrandom_pwd = generateRandomPWD(); + + $filename = 'shoppinglist.sqlite'; + //set up .htaccess rules for sqlite database if sqlite is used + + $htaccess = "Options ALL -Indexes\nDirectoryIndex api.php"; + if($dbtype === 'SQLite'){ + $htaccess .= "\n\norder allow,deny\ndeny from all\n\n"; + } + + file_put_contents('.htaccess', $htaccess); + //create config file value + + if($createDBUser == "true") { + $config_access = ' + "'.$filename.'", + ); + //only for MySQL + $MySQLConfig = array( + \'host\' => "'.$dbhost.'", + \'db\' => "shopping", + \'user\' => "ShoppingListUser", + \'password\' => "'.$dbrandom_pwd.'", + ); + ?>'; + + //mysql dump for root access + $dbdump_access = "CREATE USER 'ShoppingListUser'@'".$dbhost."' IDENTIFIED BY '".$dbrandom_pwd."';"; + $dbdump_access2 = "CREATE DATABASE shopping;"; + $dbdump_access3 = "GRANT ALL PRIVILEGES ON shopping.ShoppingList TO 'ShoppingListUser'@'".$dbhost."';"; + + } else { + + $config = ' + "'.$filename.'", + ); + //only for MySQL + $MySQLConfig = array( + \'host\' => "'.$dbhost.'", + \'db\' => "'.$dbname.'", + \'user\' => "'.$dbuser.'", + \'password\' => "'.$dbpassword.'", + ); + ?>'; + } + + //mysql dump + $dbdump = " + CREATE TABLE ShoppingList ( + item VARCHAR(255), + count VARCHAR(255), + RID int(11) NOT NULL auto_increment, + primary KEY (RID)) + ENGINE=InnoDB DEFAULT COLLATE=utf8_general_ci;"; + + //try to open/create config.php file + //success: write config value + //error: message and get out config.php value + if($handler = fopen('config.php', 'w')) { + if($createDBUser == "true") + fwrite($handler, $config_access); + else + fwrite($handler, $config); + + fclose($handler); + } + else + { + echo <<Config.php -
- - - -EOCONFIG; - } + if($createDBUser == "true") + echo $config_access; + else + echo $config; + + echo ''; + } - //when DB Type MySQL create table - if($dbtype == "MySQL") - { + //when DB Type MySQL create table + if($dbtype == "MySQL") + { if($createDBUser == "true") $handler = new mysqli($dbhost, $dbuser, $dbpassword); else $handler = new mysqli($dbhost, $dbuser, $dbpassword, $dbname); - //check if connection successful - if ($handler->connect_error) - die(''); - - //prepare query - if($createDBUser == "true") { - $stmt1 = $handler->prepare($dbdump_access); - $stmt2 = $handler->prepare($dbdump_access2); - $stmt3 = $handler->prepare($dbdump_access3); - } - else - $stmt = $handler->prepare($dbdump); - - //execute query and check if successful - if($createDBUser == "true") { - if($stmt1->execute() && $stmt2->execute() && $stmt3->execute()) { - $mysql_error = false; - $handler = new mysqli($dbhost, $dbuser, $dbpassword, 'shopping'); - $stmt4 = $handler->prepare($dbdump); - if($stmt4->execute()) - $mysql_error = false; - else - $mysql_error = true; - } - else { - $mysql_error = true; - } - //close connection - $stmt1->close(); - $stmt2->close(); - $stmt3->close(); - $stmt4->close(); - } - else { - if($stmt->execute()) - $mysql_error = false; - else - $mysql_error = true; - - //close connection - $stmt->close(); - } - if (!$mysql_error) - echo ''; - else - echo ''; - - } - else - { - - echo ''; - - if(is_writable(dirname("tmp/config-qr.png"))) { - include('lib/phpqrcode-git/lib/full/qrlib.php'); - $url = "http" . (($_SERVER['SERVER_PORT'] == 443) ? "s://" : "://") . $_SERVER['HTTP_HOST']; - $uri = substr($_SERVER['REQUEST_URI'], 0, strrpos($_SERVER['REQUEST_URI'], '/') + 1); - $api_url = $url . $uri . "api.php"; - $ak = $_POST['apikey']; - $ssl = (($_SERVER['SERVER_PORT'] == 443) ? "true" : "false"); - - $qr_code = "{ \"url\": \"$api_url\", \"apikey\": \"$ak\", \"ssl\": $ssl}"; - - QRcode::png("$qr_code", "tmp/config-qr.png"); - echo <<
-

Open your app and scan code for automatically configuration

-EOQR; - } - } - } - else - { - ?> - -

Install ShoppingList Database

-

API Key

-
-
-
-
- -
-
- -

Database Setup

-
-
-
- -
-

-
-

 Do you want to create a user and database for that App?

+ //check if connection successful + if ($handler->connect_error) + die(''); + + //prepare query + if($createDBUser == "true") { + $stmt1 = $handler->prepare($dbdump_access); + $stmt2 = $handler->prepare($dbdump_access2); + $stmt3 = $handler->prepare($dbdump_access3); + } + else + $stmt = $handler->prepare($dbdump); + + //execute query and check if successful + if($createDBUser == "true") { + if($stmt1->execute() && $stmt2->execute() && $stmt3->execute()) { + $mysql_error = false; + $handler = new mysqli($dbhost, $dbuser, $dbpassword, 'shopping'); + $stmt4 = $handler->prepare($dbdump); + if($stmt4->execute()) + $mysql_error = false; + else + $mysql_error = true; + } + else { + $mysql_error = true; + } + //close connection + $stmt1->close(); + $stmt2->close(); + $stmt3->close(); + $stmt4->close(); + } else { + if($stmt->execute()) + $mysql_error = false; + else + $mysql_error = true; + + //close connection + $stmt->close(); + } + if (!$mysql_error) { + echo ''; + + echo '
+

Open your app and scan code for automatically configuration

'; + + } else { + echo ''; + } + } else { + echo ''; + + echo '
+

Open your app and scan code for automatically configuration

'; + } +} else { + ?> + +

Install ShoppingList Database

+

API Key

+ +
+
+
+ +
+
+ +

Database Setup

+
+
+
+ +
+

+
+

 Do you want to create a user and database for that App?

-
-
-
- -
-


-
-
-
- -
-

-
-
-
-
- -
-


-
-
-
- -
-
-


- Click Create to write the necessary configuration to config.php and in case of MySQL to create a table for the list storage in the given database.

-
-
-
- -
- +
+
+ +
+


+
+
+
+ +
+

+
+
+
+
+ +
+


+
+
+
+ +
+
+


+ Click Create to write the necessary configuration to config.php and in case of MySQL to create a table for the list storage in the given database.

+
+ + + + + - + diff --git a/tmp/.gitkeep b/tmp/.gitkeep deleted file mode 100644 index e69de29..0000000 From c45b4201106210390b66b3c35bbbd62b4dc5522a Mon Sep 17 00:00:00 2001 From: Gerrit Giehl Date: Sat, 26 Sep 2015 10:34:50 +0200 Subject: [PATCH 09/42] fixed indents --- INSTALL.php | 559 ++++++++++++++++++++++++++-------------------------- 1 file changed, 278 insertions(+), 281 deletions(-) diff --git a/INSTALL.php b/INSTALL.php index 5f4b50e..b41322c 100644 --- a/INSTALL.php +++ b/INSTALL.php @@ -10,294 +10,291 @@
-
+
=') AND version_compare(phpversion(), '5.5', '<')) -require_once('func.inc.php'); + require_once('func.inc.php'); //Start on submit form -if(isset($_POST['createConfig'])) -{ - function generateRandomPWD($length = 25) { - $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; - $charactersLength = strlen($characters); - $randomString = ''; - for ($i = 0; $i < $length; $i++) { - $randomString .= $characters[rand(0, $charactersLength - 1)]; - } - return $randomString; - } - - function get_qrcode($_apikey) { - - include('lib/phpqrcode-git/lib/full/qrlib.php'); - $url = "http" . (($_SERVER['SERVER_PORT'] == 443) ? "s://" : "://") . $_SERVER['HTTP_HOST']; - $uri = substr($_SERVER['REQUEST_URI'], 0, strrpos($_SERVER['REQUEST_URI'], '/') + 1); - $api_url = $url . $uri . "api.php"; - $ssl = (($_SERVER['SERVER_PORT'] == 443) ? "true" : "false"); - - $qr_code = "{ \"url\": \"$api_url\", \"apikey\": \"$_apikey\", \"ssl\": $ssl}"; - - ob_start(); - QRCode::png($qr_code); - $image_string = base64_encode(ob_get_clean()); - header('Content-Type: text/html'); - - return $image_string; - } - - //set vars - //hash api key with bcrypt - $apikey = password_hash($_POST['apikey'], PASSWORD_BCRYPT); - $dbtype = $_POST['type']; - $dbhost = $_POST['hostname']; - $dbname = (isset($_POST['database']) ? $_POST['database'] : ""); - $dbuser = $_POST['dbuser']; - $dbpassword = $_POST['dbpassword']; - $createDBUser = (isset($_POST['createDBUser']) ? $_POST['createDBUser'] : ""); - $dbrandom_pwd = generateRandomPWD(); - - $filename = 'shoppinglist.sqlite'; - //set up .htaccess rules for sqlite database if sqlite is used - - $htaccess = "Options ALL -Indexes\nDirectoryIndex api.php"; - if($dbtype === 'SQLite'){ - $htaccess .= "\n\norder allow,deny\ndeny from all\n\n"; - } - - file_put_contents('.htaccess', $htaccess); - //create config file value - - if($createDBUser == "true") { - $config_access = ' - "'.$filename.'", - ); - //only for MySQL - $MySQLConfig = array( - \'host\' => "'.$dbhost.'", - \'db\' => "shopping", - \'user\' => "ShoppingListUser", - \'password\' => "'.$dbrandom_pwd.'", - ); - ?>'; - - //mysql dump for root access - $dbdump_access = "CREATE USER 'ShoppingListUser'@'".$dbhost."' IDENTIFIED BY '".$dbrandom_pwd."';"; - $dbdump_access2 = "CREATE DATABASE shopping;"; - $dbdump_access3 = "GRANT ALL PRIVILEGES ON shopping.ShoppingList TO 'ShoppingListUser'@'".$dbhost."';"; - - } else { - - $config = ' - "'.$filename.'", - ); - //only for MySQL - $MySQLConfig = array( - \'host\' => "'.$dbhost.'", - \'db\' => "'.$dbname.'", - \'user\' => "'.$dbuser.'", - \'password\' => "'.$dbpassword.'", - ); - ?>'; - } - - //mysql dump - $dbdump = " - CREATE TABLE ShoppingList ( - item VARCHAR(255), - count VARCHAR(255), - RID int(11) NOT NULL auto_increment, - primary KEY (RID)) - ENGINE=InnoDB DEFAULT COLLATE=utf8_general_ci;"; - - //try to open/create config.php file - //success: write config value - //error: message and get out config.php value - if($handler = fopen('config.php', 'w')) { - if($createDBUser == "true") - fwrite($handler, $config_access); - else - fwrite($handler, $config); - - fclose($handler); - } - else - { - echo <<Config.php - -
- - '; - } - - //when DB Type MySQL create table - if($dbtype == "MySQL") - { - if($createDBUser == "true") - $handler = new mysqli($dbhost, $dbuser, $dbpassword); - else - $handler = new mysqli($dbhost, $dbuser, $dbpassword, $dbname); - - //check if connection successful - if ($handler->connect_error) - die(''); - - //prepare query - if($createDBUser == "true") { - $stmt1 = $handler->prepare($dbdump_access); - $stmt2 = $handler->prepare($dbdump_access2); - $stmt3 = $handler->prepare($dbdump_access3); - } - else - $stmt = $handler->prepare($dbdump); - - //execute query and check if successful - if($createDBUser == "true") { - if($stmt1->execute() && $stmt2->execute() && $stmt3->execute()) { - $mysql_error = false; - $handler = new mysqli($dbhost, $dbuser, $dbpassword, 'shopping'); - $stmt4 = $handler->prepare($dbdump); - if($stmt4->execute()) - $mysql_error = false; - else - $mysql_error = true; - } - else { - $mysql_error = true; - } - //close connection - $stmt1->close(); - $stmt2->close(); - $stmt3->close(); - $stmt4->close(); - } else { - if($stmt->execute()) - $mysql_error = false; - else - $mysql_error = true; - - //close connection - $stmt->close(); - } - if (!$mysql_error) { - echo ''; - - echo '
-

Open your app and scan code for automatically configuration

'; - - } else { - echo ''; - } - } else { - echo ''; - - echo '
-

Open your app and scan code for automatically configuration

'; - } + if($createDBUser == "true") + echo $config_access; + else + echo $config; + + echo ''; + } + + //when DB Type MySQL create table + if($dbtype == "MySQL") { + if($createDBUser == "true") + $handler = new mysqli($dbhost, $dbuser, $dbpassword); + else + $handler = new mysqli($dbhost, $dbuser, $dbpassword, $dbname); + + //check if connection successful + if ($handler->connect_error) + die(''); + + //prepare query + if($createDBUser == "true") { + $stmt1 = $handler->prepare($dbdump_access); + $stmt2 = $handler->prepare($dbdump_access2); + $stmt3 = $handler->prepare($dbdump_access3); + } + else + $stmt = $handler->prepare($dbdump); + + //execute query and check if successful + if($createDBUser == "true") { + if($stmt1->execute() && $stmt2->execute() && $stmt3->execute()) { + $mysql_error = false; + $handler = new mysqli($dbhost, $dbuser, $dbpassword, 'shopping'); + $stmt4 = $handler->prepare($dbdump); + if($stmt4->execute()) + $mysql_error = false; + else + $mysql_error = true; + } + else { + $mysql_error = true; + } + //close connection + $stmt1->close(); + $stmt2->close(); + $stmt3->close(); + $stmt4->close(); + } else { + if($stmt->execute()) + $mysql_error = false; + else + $mysql_error = true; + + //close connection + $stmt->close(); + } + if (!$mysql_error) { + echo ''; + + echo '
+

Open your app and scan code for automatically configuration

'; + + } else { + echo ''; + } + } else { + echo ''; + + echo '
+

Open your app and scan code for automatically configuration

'; + } } else { - ?> - -

Install ShoppingList Database

-

API Key

-
-
-
-
- -
-
- -

Database Setup

-
-
-
- -
-

-
-

 Do you want to create a user and database for that App?

- -
-
-
- -
-


-
-
-
- -
-

-
-
-
-
- -
-


-
-
-
- -
-
-


- Click Create to write the necessary configuration to config.php and in case of MySQL to create a table for the list storage in the given database.

-
- -
- -
- + +

Install ShoppingList Database

+

API Key

+
+
+
+
+ +
+
+ +

Database Setup

+
+
+
+ +
+

+
+

 Do you want to create a user and database for that App?

+ +
+
+
+ +
+
+

+
+
+
+ +
+

+
+
+
+
+ +
+
+

+
+
+
+ +
+
+
+

+ Click Create to write the necessary configuration to config.php and in case of MySQL to create a table for the list storage in the given database.

+
+ + + + From ab174d94e9b80dd3d102b1baec093570c8104008 Mon Sep 17 00:00:00 2001 From: Gerrit Giehl Date: Sat, 26 Sep 2015 12:10:07 +0200 Subject: [PATCH 10/42] fixed typo --- INSTALL.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/INSTALL.php b/INSTALL.php index b41322c..cad0ead 100644 --- a/INSTALL.php +++ b/INSTALL.php @@ -198,7 +198,7 @@ function get_qrcode($_apikey) { if (!$mysql_error) { echo ''; - echo '
+ echo '

Open your app and scan code for automatically configuration

'; } else { @@ -209,7 +209,7 @@ function get_qrcode($_apikey) { } else { echo ''; - echo '
+ echo '

Open your app and scan code for automatically configuration

'; } } else { From 3e497070c6117c088953157240b21e1ad33567a2 Mon Sep 17 00:00:00 2001 From: Gerrit Giehl Date: Sat, 26 Sep 2015 12:22:08 +0200 Subject: [PATCH 11/42] removed unused entry --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 3b7c92d..0ed1d5b 100644 --- a/.gitignore +++ b/.gitignore @@ -42,4 +42,3 @@ Network Trash Folder Temporary Items .apdisk -qr-config.png From d01606395a292718e1f8a144f72f9ad44d2b0572 Mon Sep 17 00:00:00 2001 From: Jan Buchta Date: Sat, 26 Sep 2015 20:35:33 +0200 Subject: [PATCH 12/42] Create postinst.sh Idea for postinst.sh Not sure how to automatically find config.php, any ideas? --- postinst.sh | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 postinst.sh diff --git a/postinst.sh b/postinst.sh new file mode 100644 index 0000000..6817b67 --- /dev/null +++ b/postinst.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +echo "Press Enter to continue with update for ShoppingList database" +read +HOST=`cat /var/www/sholi_test/config.php | grep host | cut -d\" -f2` +USER=`cat /var/www/sholi_test/config.php | grep user | cut -d\" -f2` +PASS=`cat /var/www/sholi_test/config.php | grep password | cut -d\" -f2` +DB=`cat /var/www/sholi_test/config.php | grep db | cut -d\" -f2` +echo "Updating Database. Please wait..." +echo +SQLSTATUS=`mysql -u $USER -p$PASS < update.sql` + +if [ $? -eq 0 ] +then + echo "Successfully updated database!" +else + echo "Could not update database. See error above." +fi From 3b569d1a5375e3cce196c52b7cf4655725217228 Mon Sep 17 00:00:00 2001 From: Jan Buchta Date: Sat, 26 Sep 2015 20:36:06 +0200 Subject: [PATCH 13/42] Create update.sql update file for postinst.sh --- update.sql | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 update.sql diff --git a/update.sql b/update.sql new file mode 100644 index 0000000..8048cfb --- /dev/null +++ b/update.sql @@ -0,0 +1,2 @@ +use shopping; +alter table ShoppingList ADD COLUMN checked VARCHAR(128); From a26d65c444ea3372004d95023dfd3a0c30a53577 Mon Sep 17 00:00:00 2001 From: Jan Buchta Date: Sat, 26 Sep 2015 21:09:59 +0200 Subject: [PATCH 14/42] Create UPDATE.php Idea for UPDATE.php SQL Update does not work yet To check, set BACKEND_VERSION in CONSTANTS.php < 1 --- UPDATE.php | 357 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 357 insertions(+) create mode 100644 UPDATE.php diff --git a/UPDATE.php b/UPDATE.php new file mode 100644 index 0000000..9c9f8fe --- /dev/null +++ b/UPDATE.php @@ -0,0 +1,357 @@ +DEBUG"; +} +if(ISSET($_POST['update'])){ + update($_POST['zipURL'], $_POST['newVersion']); +} else { + status(); +} + +function echoHead(){ +echo << + + + + +HEAD; +} +function echoStyle(){ +<< +STYLE; +echo << + + + +BOOTSTRAP; +} + +function echoPageStart(){ +echo << +PAGE_START; +} + +function echoPageEnd(){ +echo << +PAGE_END; +} + +function echoUpdateAvailable($curVersion, $newVersion, $updateText, $zipURL, $disabled){ + echo << +
+
+

Update Available - ShoppingList Backend

+
+
+ Release Note: $updateText

+ Download URL: $zipURL +
+
+ + + +
+
+ +STATUS; +} + +function echoAllGood(){ + echo << +
+
+

ShoppingList Backend

+
+
+ Congratulations!
+ Everything is up to date. +
+
+ +ALLGOOD; +} + +function echoChecklist($newVersion, $zipURL){ +echo << +
+
+

Updating backend to version: $newVersion

+
+
+ Downloading from: $zipURL

+ +
+ +
+ +
+ +
+ +
+
+
+ + +CHECKLIST; +} + +function echoJavascript(){ +echo << + var progressDownloadElement = document.getElementById('progressDownload') + var progressElement = document.getElementById('progressDownload') + var downloadElement = document.getElementById('cb_download') + var extractElement = document.getElementById('cb_extract') + var backupElement = document.getElementById('cb_backup') + var filemoveElement = document.getElementById('cb_filemove') + var databaseElement = document.getElementById('cb_databaseupdate') + + function updateProgress(percentage) { + progressElement.innerHTML = 'Download (' + percentage + '%' + ')'; + } + function updateDownload(status) { + downloadElement.checked = status; + } + function updateExtract(status) { + extractElement.checked = status; + } + function updateBackup(status) { + backupElement.checked = status; + } + function updateFilemove(status) { + filemoveElement.checked = status; + } + function updateDatabase(status) { + databaseElement.checked = status; + } + +JS; +} + +function status(){ + $githubData = getGithubData(); + echoHead(); + echoPageStart(); + echoStyle(); + echoJavascript(); + if($githubData['version'] == 0){ + echoError('Could not connect to GitHub API.'); + die(); + } + if($githubData['version'] == BACKEND_VERSION){ + $updateDisabled = permissionCheck(); + echoUpdateAvailable(BACKEND_VERSION, $githubData['version'], $githubData['releasenote'], $githubData['zipurl'], $updateDisabled); + } else { + echoAllGood(); + } + echoPageEnd(); +} + +function update($zipURL, $newVersion){ + echoPageStart(); + echoHead(); + echoStyle(); + echoChecklist($newVersion, $zipURL); + echoJavascript(); + $zipFILE = downloadUpdate($zipURL); + unzipDownloadedUpdate($zipFILE); + backup(); + copyNewFiles(); + updateDatabase(); + echoPageEnd(); +} + +function echoSuccess(){ +echo << +
+ +
+ +SUCCESS; +} + +function echoError($errorMessage){ +echo << +
+ +
+ +ERROR; + +} + +function permissionCheck(){ + if (!is_writable(__DIR__)){ + $user = get_current_user(); + $dir = __DIR__; + echoError('No write permission for user "'.$user.'" for directory "'. $dir.'"'); + return "disabled"; + } +} + +function downloadUpdate($zipurl){ + $ch = curl_init() or die('Sorry cURL is not installed!'); + $zipfile = __DIR__.'/update_'.$tag.'.zip'; + $fp = fopen ($zipfile, 'w+'); + $ch = curl_init(); + curl_setopt($ch, CURLOPT_URL, $zipurl); + curl_setopt($ch, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']); + curl_setopt($ch, CURLOPT_FILE, $fp); + curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); + curl_setopt($ch,CURLOPT_NOPROGRESS,false); + curl_setopt($ch,CURLOPT_PROGRESSFUNCTION,'progress'); + curl_exec($ch); + curl_close($ch); + echo ''; + return $zipfile; + if(!file_exists($zipfile)){ + echoError('Could not download file from GitHub!'); + } +} + +function unzipDownloadedUpdate($zipfile){ + $zip = new ZipArchive; + $res = $zip->open($zipfile); + if ($res === TRUE) { + $zip->extractTo(__DIR__); + $zip->close(); + echo ''; + unlink($zipfile); + } else { + die ('Could not extract file!'); + } +} + +function backup(){ + $backupFolder = __DIR__.'/backup/'; + delDir($backupFolder); + if(!mkdir($backupFolder, 0770)){ + die ('Could not create backup folder!'); + } + $iteratorBackup = new DirectoryIterator(__DIR__); + foreach ($iteratorBackup as $backupFile) { + $backupFileName = $backupFile->getFilename(); + if ($backupFileName != '.' && $backupFileName != '..' && $backupFileName != 'config.php' && $backupFileName != '.htaccess' && !$backupFile->isDir() && $backupFileName[0] != '.'){ + if(!rename($backupFile->getPathname(), $backupFolder.$backupFile->getFilename())){ + die ('Could not backup files!'); + } + } + } + echo ''; +} + +function copyNewFiles(){ + $iterator = new DirectoryIterator(__DIR__); + foreach ($iterator as $fileinfo) { + if ($fileinfo->isDir() && strlen(strstr($fileinfo->getFilename(),"GroundApps-ShoppingList_Backend"))>0) { + $iteratorUpdateDir = new DirectoryIterator($fileinfo->getPathname()); + foreach ($iteratorUpdateDir as $updateFile) { + $updateFileName = $updateFile->getFilename(); + if ($updateFileName != '.' && $updateFileName != '..' && $updateFileName != 'config.php' && $updateFileName != '.htaccess'){ + rename($updateFile->getPathname(), __DIR__ .'/'.$updateFileName); + echo ''; + } + } + delDir($fileinfo->getPathname()); + } + } + echo ''; +} + +function getGithubData(){ + $ch = curl_init() or die('Sorry cURL is not installed!'); + $url = 'https://api.github.com/repos/GroundApps/ShoppingList_Backend/releases/latest'; + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_URL,$url); + $agent = 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0'; + curl_setopt($ch,CURLOPT_HTTPHEADER,array('User-Agent: '.$agent)); + $result = curl_exec($ch); + curl_close($ch); + $obj = json_decode($result); + $tag = $obj->tag_name; + $zipurl = $obj->zipball_url; + $releasenote = $obj->body; + //$tag = "v1.1"; + $zipurl = 'https://api.github.com/repos/GroundApps/ShoppingList_Backend/zipball/v1'; + return array( + "version" => substr($tag,1), + "zipurl" => $zipurl, + "releasenote" => $releasenote, + ); +} + +function updateDatabase(){ + include('config.php'); + switch($dataBase){ + case 'SQLite': + $dbConnector = "sqlite_connector.php"; + $dbConfig = $SQLiteConfig; + break; + case 'MySQL': + $dbConnector = "mysql_connector.php"; + $dbConfig = $MySQLConfig; + break; + default: + $dbConnector = ""; + $dbConfig = ""; + echoError('No database type specified in config.php'); + } + include $dbConnector; + //IMPLEMENT DATABASE ALTERATION + echo ''; +} + +function progress($clientp,$dltotal,$dlnow,$ultotal,$ulnow){ + if ($dltotal > 0){ + echo ''; + } + return(0); + } + +function delDir($dir) { + if (is_dir($dir)) { + $objects = scandir($dir); + foreach ($objects as $object) { + if ($object != "." && $object != "..") { + if (filetype($dir."/".$object) == "dir") delDir($dir."/".$object); else unlink($dir."/".$object); + } + } + reset($objects); + rmdir($dir); + } +} + +?> From 17a1df63d129d92199ff09e5c27fe31e59142f4b Mon Sep 17 00:00:00 2001 From: Jan Buchta Date: Sun, 27 Sep 2015 11:01:39 +0200 Subject: [PATCH 15/42] Update UPDATE.php --- UPDATE.php | 46 +++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/UPDATE.php b/UPDATE.php index 9c9f8fe..0a11b4e 100644 --- a/UPDATE.php +++ b/UPDATE.php @@ -111,7 +111,7 @@ function echoChecklist($newVersion, $zipURL){
-
+
@@ -129,6 +129,7 @@ function echoJavascript(){ var backupElement = document.getElementById('cb_backup') var filemoveElement = document.getElementById('cb_filemove') var databaseElement = document.getElementById('cb_databaseupdate') + var databaseLabel = document.getElementById('databaseupdateLabel') function updateProgress(percentage) { progressElement.innerHTML = 'Download (' + percentage + '%' + ')'; @@ -148,6 +149,10 @@ function updateFilemove(status) { function updateDatabase(status) { databaseElement.checked = status; } + function updateDatabaseNotNecessary() { + databaseLabel.style.setProperty("text-decoration", "line-through"); + } + JS; } @@ -185,13 +190,13 @@ function update($zipURL, $newVersion){ echoPageEnd(); } -function echoSuccess(){ +function echoSuccess($message){ echo <<
@@ -259,12 +264,14 @@ function backup(){ $backupFolder = __DIR__.'/backup/'; delDir($backupFolder); if(!mkdir($backupFolder, 0770)){ - die ('Could not create backup folder!'); - } + echoError('Could not create backup folder!'); + exit; + } $iteratorBackup = new DirectoryIterator(__DIR__); foreach ($iteratorBackup as $backupFile) { $backupFileName = $backupFile->getFilename(); - if ($backupFileName != '.' && $backupFileName != '..' && $backupFileName != 'config.php' && $backupFileName != '.htaccess' && !$backupFile->isDir() && $backupFileName[0] != '.'){ + //REMOVE CHECK FOR update.sql FOR RELEASE, CURRENTLY USED BECAUSE ZIP FROM GITHUB HAS NONE AND IT WOULD BE DELETED + if ($backupFileName != '.' && $backupFileName != '..' && $backupFileName != 'config.php' && $backupFileName != 'update.sql' && $backupFileName != '.htaccess' && !$backupFile->isDir() && $backupFileName[0] != '.'){ if(!rename($backupFile->getPathname(), $backupFolder.$backupFile->getFilename())){ die ('Could not backup files!'); } @@ -315,23 +322,20 @@ function getGithubData(){ function updateDatabase(){ include('config.php'); - switch($dataBase){ - case 'SQLite': - $dbConnector = "sqlite_connector.php"; - $dbConfig = $SQLiteConfig; - break; - case 'MySQL': - $dbConnector = "mysql_connector.php"; - $dbConfig = $MySQLConfig; - break; - default: - $dbConnector = ""; - $dbConfig = ""; - echoError('No database type specified in config.php'); - } - include $dbConnector; //IMPLEMENT DATABASE ALTERATION + // WITH PDO + $updateFILE = __DIR__."/update.sql"; + if(!file_exists($updateFILE)){ + echo ''; + } else { + $lines = file($updateFILE); + foreach ($lines as $line){ + echo $line.'
'; + //SQL FOR EACH LINE IN update.sql + } echo ''; + } + echoSuccess('Update done! Please check if everything works and then delete the backup folder.'); } function progress($clientp,$dltotal,$dlnow,$ultotal,$ulnow){ From 604a7eb9179b4345cbb0b5de22aa15489a89a2d4 Mon Sep 17 00:00:00 2001 From: jklmnn Date: Sun, 27 Sep 2015 22:00:29 +0200 Subject: [PATCH 16/42] Fixed constant already defined notices. --- api.php | 2 +- db_connector.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api.php b/api.php index ab33a50..a947bed 100644 --- a/api.php +++ b/api.php @@ -1,5 +1,5 @@ Date: Wed, 30 Sep 2015 23:54:14 +0200 Subject: [PATCH 17/42] Typo. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4e14a4f..848df7f 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ You can either use MySQL or SQLite. SQLite is easier to set up. ####Installation -If you use Ubuntu you can use the [PPA](https://launchpad.net/~jklmnn/+archive/ubuntu/groundapps) by executing `apt-add repository ppa:jklmnn/groundapps`. +If you use Ubuntu you can use the [PPA](https://launchpad.net/~jklmnn/+archive/ubuntu/groundapps) by executing `add-apt-repository ppa:jklmnn/groundapps`. To install the backend manually , go to http://your.path/. Fill up the form, click on create! That's all. From d75fafee44f70a4444619e1e587887a8ba7b21cb Mon Sep 17 00:00:00 2001 From: Jean Elchinger Date: Thu, 26 Nov 2015 00:53:39 +0100 Subject: [PATCH 18/42] Update README.md correction of installation path and added some info and direct link to the wiki. --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 3bd2e45..a632b5a 100644 --- a/README.md +++ b/README.md @@ -12,10 +12,13 @@ You can either use MySQL or SQLite. SQLite is easier to set up. ####Installation If you use Ubuntu you can use the [PPA](https://launchpad.net/~jklmnn/+archive/ubuntu/groundapps) by executing `add-apt-repository ppa:jklmnn/groundapps`. -To install the backend manually , go to http://your.path/. -Fill up the form, click on create! +To install the backend manually, go to http://your.path/INSTALL.php +Fill up the form, click on create! That's all. +https is currently not supported for self-signed certificates. +See the [wiki](https://github.com/GroundApps/ShoppingList/wiki) for more informations about installation and roadmap. + ## Feedback Please do never hesitate to open an issue!
I know there a some bugs, most likely because I had no idea how to do it otherwise and therefore had to use a workaround. From 2b7db66613913c8f67ed7ccd80fcef54cd1da438 Mon Sep 17 00:00:00 2001 From: Jean Elchinger Date: Thu, 26 Nov 2015 11:47:13 +0100 Subject: [PATCH 19/42] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a632b5a..ca74ea9 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ You can either use MySQL or SQLite. SQLite is easier to set up. ####Installation If you use Ubuntu you can use the [PPA](https://launchpad.net/~jklmnn/+archive/ubuntu/groundapps) by executing `add-apt-repository ppa:jklmnn/groundapps`. -To install the backend manually, go to http://your.path/INSTALL.php +To install the backend manually, go to http://your.path/ Fill up the form, click on create! That's all. From 8499a8b6b7b7e61afdff543ac71910f204b55941 Mon Sep 17 00:00:00 2001 From: Malte Kiefer Date: Thu, 26 Nov 2015 17:48:47 +0100 Subject: [PATCH 20/42] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 645a72b..1f8e1da 100644 --- a/README.md +++ b/README.md @@ -13,13 +13,11 @@ You can either use MySQL or SQLite. SQLite is easier to set up. ####Installation If you use Ubuntu you can use the [PPA](https://launchpad.net/~jklmnn/+archive/ubuntu/groundapps) by executing `add-apt-repository ppa:jklmnn/groundapps`. -<<<<<<< HEAD To install the backend manually , go to http://your.path/. Fill up the form, click on create! ======= To install the backend manually, go to http://your.path/ Fill up the form, click on create! ->>>>>>> 2b7db66613913c8f67ed7ccd80fcef54cd1da438 That's all. https is currently not supported for self-signed certificates. From 85aaca004aba943144b4a80860c6b739a7f0063f Mon Sep 17 00:00:00 2001 From: Lertsenem Date: Mon, 21 Dec 2015 00:47:15 +0100 Subject: [PATCH 21/42] Adding Docker support for easy deploying --- Dockerfile | 26 ++++++++++++++++++ docker/config_sqlite.php | 9 +++++++ docker/entrypoint.sh | 26 ++++++++++++++++++ docker/hash_apikey.php | 6 +++++ docker/nginx.conf | 58 ++++++++++++++++++++++++++++++++++++++++ docker/php-fpm.conf | 24 +++++++++++++++++ 6 files changed, 149 insertions(+) create mode 100644 Dockerfile create mode 100644 docker/config_sqlite.php create mode 100755 docker/entrypoint.sh create mode 100644 docker/hash_apikey.php create mode 100644 docker/nginx.conf create mode 100644 docker/php-fpm.conf diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..21d503f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +FROM alpine + +RUN apk update \ + && apk add \ + nginx \ + php-fpm \ + php-json \ + && rm -rf /var/cache/apk/* + +COPY . /shoppinglist + +RUN chown -R nginx:www-data /shoppinglist/ + +# Copy scripts +COPY docker/nginx.conf /etc/nginx/ +COPY docker/php-fpm.conf /etc/php/ +COPY docker/entrypoint.sh / + + +EXPOSE 80 + +ENV API_KEY="" + +VOLUME [ "/data" ] + +CMD [ "/entrypoint.sh" ] diff --git a/docker/config_sqlite.php b/docker/config_sqlite.php new file mode 100644 index 0000000..5c75bc4 --- /dev/null +++ b/docker/config_sqlite.php @@ -0,0 +1,9 @@ + "shoppinglist.sqlite" + ); +?> diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100755 index 0000000..f891434 --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +SL_ROOT="/shoppinglist" + +if [ -z "$API_KEY" ]; then + echo "You must define an API_KEY when running this container" >&2 + echo "Use for example: -e \"API_KEY=mysecretpassword\"" >&2 + exit 1 +fi + +# Check initialisation state +if [ -e "$SL_ROOT/INSTALL.php" ]; then + hashed_api_key="$( php "$SL_ROOT/docker/hash_apikey.php" "$API_KEY" )" + + sed "s%authKey *= *.*;%authKey = '$hash_apikey';%" "$SL_ROOT/docker/config_sqlite.php" \ + > "$SL_ROOT/config.php" + + rm "$SL_ROOT/INSTALL.php" +fi + + +# Run PHP-FPM +php-fpm + +# Run nginx +nginx diff --git a/docker/hash_apikey.php b/docker/hash_apikey.php new file mode 100644 index 0000000..9c8e5c6 --- /dev/null +++ b/docker/hash_apikey.php @@ -0,0 +1,6 @@ + diff --git a/docker/nginx.conf b/docker/nginx.conf new file mode 100644 index 0000000..3a3c6dd --- /dev/null +++ b/docker/nginx.conf @@ -0,0 +1,58 @@ +# run nginx in foreground +daemon off; + +error_log /dev/stderr; + +pid /var/run/nginx.pid; +worker_processes 5; + +events { + worker_connections 4096; +} + +http { + include /etc/nginx/mime.types; + include /etc/nginx/fastcgi.conf; + + default_type application/octet-stream; + + #tcp_nopush on; + #client_body_temp_path /tmp/nginx/body 1 2; + #fastcgi_temp_path /tmp/nginx/fastcgi_temp 1 2; + + server { + + listen 80; + + access_log /dev/stdout; + + root /shoppinglist; + index index.php index.html index.htm; + + location = /robots.txt { + allow all; + log_not_found off; + access_log off; + } + + location / { + try_files $uri $uri/ /index.html; + } + + # pass the PHP scripts to FastCGI server listening on /tmp/phpfpm.sock + location ~ [^/]\.php(/|$) { + + fastcgi_split_path_info ^(.+?\.php)(/.*)$; + + if (!-f $document_root$fastcgi_script_name) { + return 404; + } + + fastcgi_pass unix:/tmp/phpfpm.sock; + fastcgi_index index.php; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + include fastcgi_params; + } + } + +} diff --git a/docker/php-fpm.conf b/docker/php-fpm.conf new file mode 100644 index 0000000..a82ffed --- /dev/null +++ b/docker/php-fpm.conf @@ -0,0 +1,24 @@ +error_log = /dev/stderr + +[www] +user = nginx +group = www-data +listen = /tmp/phpfpm.sock +listen.owner = nginx +listen.group = www-data + +pm = ondemand +pm.max_children = 75 +pm.process_idle_timeout = 10s +pm.max_requests = 500 + +chdir = /shoppinglist + +php_flag[display_errors] = on +php_admin_value[memory_limit] = 128M +php_admin_value[upload_max_filesize] = 2G +php_admin_value[post_max_size] = 2G +php_admin_value[always_populate_raw_post_data] = -1 +php_admin_value[output_buffering] = 0 +php_admin_value[php_value max_input_time] = 3600 +php_admin_value[php_value max_execution_time] = 3600 From a2c54a8985648d10f5834fa9d486fb599107f185 Mon Sep 17 00:00:00 2001 From: Lertsenem Date: Mon, 21 Dec 2015 01:19:20 +0100 Subject: [PATCH 22/42] Adding php-pdo_sqlite --- Dockerfile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Dockerfile b/Dockerfile index 21d503f..c101983 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,6 +5,8 @@ RUN apk update \ nginx \ php-fpm \ php-json \ + php-pdo \ + php-pdo_sqlite \ && rm -rf /var/cache/apk/* COPY . /shoppinglist From 08e1c1a37c7fdde5cbd806099c6260e07204686b Mon Sep 17 00:00:00 2001 From: Lertsenem Date: Mon, 21 Dec 2015 01:19:43 +0100 Subject: [PATCH 23/42] Typo causes warning --- api.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api.php b/api.php index a947bed..ead60a5 100644 --- a/api.php +++ b/api.php @@ -1,4 +1,4 @@ - Date: Mon, 21 Dec 2015 01:19:51 +0100 Subject: [PATCH 24/42] Correction on entrypoint for hashed key --- docker/entrypoint.sh | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index f891434..605837e 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -10,11 +10,14 @@ fi # Check initialisation state if [ -e "$SL_ROOT/INSTALL.php" ]; then - hashed_api_key="$( php "$SL_ROOT/docker/hash_apikey.php" "$API_KEY" )" - sed "s%authKey *= *.*;%authKey = '$hash_apikey';%" "$SL_ROOT/docker/config_sqlite.php" \ + # Hash the key and add it to config.php + hashed_apikey="$( php "$SL_ROOT/docker/hash_apikey.php" "$API_KEY" )" + + sed "s%authKey *= *.*;%authKey = '$hashed_apikey';%" "$SL_ROOT/docker/config_sqlite.php" \ > "$SL_ROOT/config.php" + # No need for web install rm "$SL_ROOT/INSTALL.php" fi From af431efebbd5c02d8210a10a2c39235ad4a84886 Mon Sep 17 00:00:00 2001 From: Lertsenem Date: Mon, 21 Dec 2015 01:44:49 +0100 Subject: [PATCH 25/42] Docker: persist sqlite file with volume To allow for slite database persistence accross Docker container destruction/creation, we use a Docker volume mounted on '/shoppinglist/data'. Files on the volume (ie 'shoppinglist.sqlite') are of course not served by nginx. --- Dockerfile | 6 ++++-- docker/config_sqlite.php | 2 +- docker/entrypoint.sh | 2 ++ docker/nginx.conf | 5 +++++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index c101983..8a6b7f3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,10 @@ RUN apk update \ COPY . /shoppinglist -RUN chown -R nginx:www-data /shoppinglist/ +VOLUME [ "/shoppinglist/data" ] + +RUN chown -R nginx:www-data /shoppinglist/ \ + && chown -R nginx:www-data /shoppinglist/data/ # Copy scripts COPY docker/nginx.conf /etc/nginx/ @@ -23,6 +26,5 @@ EXPOSE 80 ENV API_KEY="" -VOLUME [ "/data" ] CMD [ "/entrypoint.sh" ] diff --git a/docker/config_sqlite.php b/docker/config_sqlite.php index 5c75bc4..3aa0fce 100644 --- a/docker/config_sqlite.php +++ b/docker/config_sqlite.php @@ -4,6 +4,6 @@ $dataBase = "SQLite"; $SQLiteConfig = array( - 'file' => "shoppinglist.sqlite" + 'file' => "data/shoppinglist.sqlite" ); ?> diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index 605837e..68161ba 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -21,6 +21,8 @@ if [ -e "$SL_ROOT/INSTALL.php" ]; then rm "$SL_ROOT/INSTALL.php" fi +# Reset permissions +chown -R nginx:www-data "$SL_ROOT/data" # Run PHP-FPM php-fpm diff --git a/docker/nginx.conf b/docker/nginx.conf index 3a3c6dd..eccf80c 100644 --- a/docker/nginx.conf +++ b/docker/nginx.conf @@ -38,6 +38,11 @@ http { location / { try_files $uri $uri/ /index.html; } + + # Deny data/ + location /data { + deny all; + } # pass the PHP scripts to FastCGI server listening on /tmp/phpfpm.sock location ~ [^/]\.php(/|$) { From 4df71c5c9fa11dc3e64308cbcb5abf04650208d7 Mon Sep 17 00:00:00 2001 From: Lertsenem Date: Mon, 21 Dec 2015 02:05:35 +0100 Subject: [PATCH 26/42] Update README with Docker instructions --- README.md | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/README.md b/README.md index 1f8e1da..65e2523 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,38 @@ # ShoppingList Backend +##Docker Deployment +For a fast deployment, use Docker! +For now the container is only compatible with an sqlite database. + +###Build from source +You can build the container from sources: + +> git clone https://github.com/GroundApps/ShoppingList_Backend/tree/devel +> docker build -t shoppinglist . + +###Pull an already built container +Or, if you are feeling lazy, just pull an already built container from the +[Docker hub](https://hub.docker.com/r/lertsenem/shoppinglist): + +> docker pull lertsenem/shoppinglist + +###Run the container +Finally, run the container with a command like this: + +> $ docker run \ +> -d \ +> --name shoppinglist \ +> -p 8000:80 \ +> -e "API_KEY=mysecretpassword" \ +> -v /tmp/sl_data:/shoppinglist/data \ +> lertsenem/shoppinglist + +* use the env variable 'API_KEY' to set the app password ; +* mount the volume '/shoppinglist/data' to persist the sqlite database. + +Note that in regard to this last point you can (and should) use a volume-only +container for portability reasons. + ##Installation ###Requirements From 6d491de566b3bc3ff8c7be67683b48ef7454f1f5 Mon Sep 17 00:00:00 2001 From: the0ne Date: Thu, 7 Apr 2016 18:01:21 +0200 Subject: [PATCH 27/42] some fixes and cleanup + bootstrap ui --- .htaccess | 2 +- INSTALL.php | 6 +- api.php | 9 +- config.php | 20 ++- css/main.css | 60 +++++++ db_connector.php | 61 ++++--- index.php | 80 ++++++++++ js/main.js | 373 +++++++++++++++++++++++++++++++++++++++++++ mysql_connector.php | 276 -------------------------------- postinst.sh | 22 +-- sqlite_connector.php | 136 ---------------- update.sql | 3 +- 12 files changed, 574 insertions(+), 474 deletions(-) create mode 100644 css/main.css create mode 100644 index.php create mode 100644 js/main.js delete mode 100644 mysql_connector.php delete mode 100644 sqlite_connector.php diff --git a/.htaccess b/.htaccess index 9862c54..bfe19a4 100644 --- a/.htaccess +++ b/.htaccess @@ -1,2 +1,2 @@ Options All -Indexes -DirectoryIndex api.php +DirectoryIndex index.php diff --git a/INSTALL.php b/INSTALL.php index cad0ead..f69d732 100644 --- a/INSTALL.php +++ b/INSTALL.php @@ -10,7 +10,7 @@
-
+
=') AND version_compare(phpversion(), '5.5', '<')) @@ -29,7 +29,7 @@ function generateRandomPWD($length = 25) { } function get_qrcode($_apikey) { - include('lib/phpqrcode-git/lib/full/qrlib.php'); + include('lib/phpqrcode-git/phpqrcode.php'); $url = "http" . (($_SERVER['SERVER_PORT'] == 443) ? "s://" : "://") . $_SERVER['HTTP_HOST']; $uri = substr($_SERVER['REQUEST_URI'], 0, strrpos($_SERVER['REQUEST_URI'], '/') + 1); $api_url = $url . $uri . "api.php"; @@ -114,7 +114,7 @@ function get_qrcode($_apikey) { //mysql dump $dbdump = " - CREATE TABLE ShoppingList ( + CREATE TABLE IF NOT EXISTS ShoppingList ( item VARCHAR(255), count VARCHAR(255), RID int(11) NOT NULL auto_increment, diff --git a/api.php b/api.php index ead60a5..5bf203e 100644 --- a/api.php +++ b/api.php @@ -42,9 +42,12 @@ function hash_equals($a, $b) { include('db_connector.php'); - - if (!hash_equals($authKey, crypt($auth, $authKey))){ - die (json_encode(array('type' => API_ERROR_403, 'content' => 'Authentication failed.'))); + + session_start(); + if (! isset($_SESSION['user_logged']) || $_SESSION['user_logged'] != 1) { + if (!hash_equals($authKey, crypt($auth, $authKey))){ + die (json_encode(array('type' => API_ERROR_403, 'content' => 'Authentication failed.'))); + } } $db = NEW DataBase($dataBase, $dbConfig); diff --git a/config.php b/config.php index 7278a42..f78e2bd 100644 --- a/config.php +++ b/config.php @@ -5,24 +5,28 @@ ############################################################*/ /*########################################################## - # Auth Key / Password, to get acces to the database # + # Auth Key / Password, to get acces to the database # ############################################################*/ - $authKey = ''; + $authKey = ''; /*########################################################## - # Branch: stable, testing, experimental # + # Branch: stable, testing, experimental # ############################################################*/ $branch = "stable"; /*########################################################## - # Database: MySQL, SQLite # + # Database: MySQL, SQLite # ############################################################*/ $dataBase = "SQLite"; /*########################################################## - # SQLite Config # + # SQLite Config # ############################################################*/ $SQLiteConfig = array('file' => "shoppinglist.sqlite"); /*########################################################## - # MySQL Config # + # MySQL Config # ############################################################*/ - $MySQLConfig = array('host' => "host",'db' => "db",'user' => "user",'password' => "password",); + $MySQLConfig = [ + 'host' => "localhost", + 'db' => "your_database_name", + 'user' => "your_mysql_user", + 'password' => "your_mysql_password", + ]; ?> - diff --git a/css/main.css b/css/main.css new file mode 100644 index 0000000..0d1f4c9 --- /dev/null +++ b/css/main.css @@ -0,0 +1,60 @@ +/****************** + Shopping list css for web frontend + Reference : https://github.com/GroundApps/ShoppingList_Backend + Licence : http://www.gnu.org/licenses/agpl-3.0.fr.html +*******************/ +#shopItems { + list-style-type: none; + margin: 0; + padding: 0; + zoom: 1; +} + +#shopItems li { + margin: 2px 0px 12px 8px; + border-style: solid; + border-width: 0px; +} + +.itemCheck { width: 40px ; } +.itemName { } +.itemQty { width: 36px;} +.itemQty input { width: 30px;} +.itemDelete { } + +.itemCheckedTD { font-family: FontAwesome; color: #a94442; } +.itemCheckedName{ text-decoration:line-through; } +.itemUncheckedTD { font-family: FontAwesome; color: #3c763d; } +.itemCheckedTD i::before { content: '\f273'; } +.itemUncheckedTD i::before { content: '\f274'; } + +body.dragging, body.dragging * { + cursor: move !important; +} + +.dragged { + position: absolute; + opacity: 0.5; + z-index: 2000; +} + +#shopItems li.placeholder { + position: relative; + /** More li styles **/ +} +#shopItems li.placeholder:before { + position: absolute; + /** Define arrowhead **/ +} +.icon::before { + font-family: FontAwesome; + font-style: normal; + font-weight: normal; + text-transform: none !important; +} +button::before, .button::before { + left: -0.5em; + padding: 0 0 0 0.75em; + color: #aaaaaa; + position: relative; +} diff --git a/db_connector.php b/db_connector.php index 35268dd..5144299 100644 --- a/db_connector.php +++ b/db_connector.php @@ -6,7 +6,7 @@ class DataBase{ var $db; var $type; - var $table = "shoppinglist"; + var $table = "ShoppingList"; function __construct($dbtype, $dbargs){ $this->type = $dbtype; @@ -48,7 +48,7 @@ function init(){ function listall(){ try{ - $sql = "SELECT * FROM $this->table;"; + $sql = "SELECT * FROM $this->table ORDER BY item ASC"; $val = $this->db->query($sql); $stack = array(); foreach($val as $row){ @@ -93,27 +93,21 @@ function saveMultiple($jsonData){ if(empty($jsonData)){ die(json_encode(array('type' => API_ERROR_MISSING_PARAMETER, 'content' => 'parameter missing for saveMultiple'))); } - echo $jsonData."\n"; - var_dump(json_decode($jsonData)); $itemList = json_decode($jsonData, true); - $returns = array(); + $success = true; + $errors = array(); foreach($itemList as $item){ - array_push($returns, array('itemTitle' => $item['itemTitle'], 'error' => $this->save($item['itemTitle'], $item['itemCount']))); - } - return json_encode($returns); - /*try{ - $stmt = $this->db->prepare("INSERT INTO $this->table (item, count, checked) VALUES (:item, :count, :checked);"); - $checked = (int)false; - $stmt->bindParam(':checked', $checked, PDO::PARAM_INT); - foreach($itemList as $item){ - $stmt->bindParam(':item', $item['itemTitle'], PDO::PARAM_STR); - $stmt->bindParam(':count', $item['itemCount'], PDO::PARAM_INT); - $stmt->execute(); + $output = json_decode($this->exists($item['itemTitle']) ? $this->update($item['itemTitle'], $item['itemCount']) : $this->save($item['itemTitle'], $item['itemCount']),true); + if($output['type']!=API_SUCCESS_SAVE && $output['type']!=API_SUCCESS_UPDATE) { + $success = false; + array_push($errors, array('itemTitle' => $item['itemTitle'], 'error' => $output['content'])); } - return json_encode(array('type' => API_SUCCESS_SAVE, 'content' => 'Multiple items saved.')); - }catch(PDOException $e){ - return json_encode(array('type' => API_ERROR_SAVE, 'content' => $e->getMessage())); - }*/ + } + if($success) { + return json_encode(array('type' => API_SUCCESS_SAVE, 'content' => count($itemList)>1 ? 'Multiple items saved.':$itemList[0]['itemTitle'].' saved.')); + } else { + return json_encode(array('type' => API_ERROR_SAVE, 'content' => $errors)); + } } function update($item, $count){ @@ -133,21 +127,20 @@ function deleteMultiple($jsonData){ die(json_encode(array('type' => API_ERROR_MISSING_PARAMETER, 'content' => 'Parameter missing for deleteMultiple.'))); } $itemList = json_decode($jsonData, true); - $returns = array(); + $success = true; + $errors = array(); foreach($itemList as $item){ - array_push($returns, array('itemTitle' => $item['itemTitle'], 'error' => $this->delete($item['itemTitle']))); - } - return json_encode($returns); - /*try{ - $stmt = $this->db->prepare("DELETE FROM $this->table WHERE item=:item;"); - foreach($itemList as $item){ - $stmt->bindParam(':item', $item['itemTitle'], PDO::PARAM_STR); - $stmt->execute(); + $output = json_decode($this->delete($item['itemTitle']),true); + if($output['type']!=API_SUCCESS_DELETE) { + $success = false; + array_push($errors, array('itemTitle' => $item['itemTitle'], 'error' => $output['content'])); } - return json_encode(array('type' => API_SUCCESS_DELETE, 'content' => 'Multiple items deleted.')); - }catch(PDOException $e){ - return json_encode(array('type' => API_ERROR_DELETE, 'content' => $e->getMessage())); - }*/ + } + if($success) { + return json_encode(array('type' => API_SUCCESS_DELETE, 'content' => count($itemList)>1 ? 'Multiple items deleted.':$itemList[0]['itemTitle'].' deleted.')); + } else { + return json_encode(array('type' => API_ERROR_DELETE, 'content' => $errors)); + } } function delete($item){ @@ -169,5 +162,5 @@ function clear(){ return json_encode(array('type' => API_ERROR_CLEAR, 'content' => $e->getMessage())); } } - + } diff --git a/index.php b/index.php new file mode 100644 index 0000000..579fd4b --- /dev/null +++ b/index.php @@ -0,0 +1,80 @@ +Invalid API Key

"; + } + } +?> + + + + Shopping List + + + + + + + + + + +
+
+

Shopping List

+ +
" method="post"> + +

API Key

+
+
+
+ +
+
+
+
+ +
+ + + */ ?> + +
+ + +
+
+ + diff --git a/js/main.js b/js/main.js new file mode 100644 index 0000000..20a7232 --- /dev/null +++ b/js/main.js @@ -0,0 +1,373 @@ +/****************** + Shopping list javascript for web frontend + Reference : https://github.com/GroundApps/ShoppingList_Backend + Licence : http://www.gnu.org/licenses/agpl-3.0.fr.html + +*******************/ + +/*******************/ + +var apiurl="api.php"; + +/*******************/ +var BACKEND_VERSION=1.0; + +var API_SUCCESS_LIST=1000; +var API_SUCCESS_LIST_EMPTY=1001; +var API_SUCCESS_UPDATE=1002; +var API_SUCCESS_FAVORITE=1003; +var API_SUCCESS_DELETE=1004; +var API_SUCCESS_SAVE=1005; +var API_SUCCESS_CLEAR=1006; + +var API_ERROR_SERVER=5000; +var API_ERROR_404=5001; +var API_ERROR_403=5002; +var API_ERROR_MISSING_FUNCTION=5003; +var API_ERROR_NO_DATABASE=5004; +var API_ERROR_CONFIG=5005; +var API_ERROR_UNKNOWN=5006; +var API_ERROR_DATABASE_CONNECT=5012; +var API_ERROR_MISSING_PARAMETER=5013; +var API_ERROR_FUNCTION_NOT_SPECIFIED=5014; +var API_ERROR_NOT_CONFIGURED=5015; + +var API_ERROR_UPDATE_=6001; +var API_ERROR_FAVORITE=6002; +var API_ERROR_DELETE=6003; +var API_ERROR_SAVE=6004; +var API_ERROR_CLEAR=6005; +var API_ERROR_LIST=6006; +/*******************/ + +/* web page structure + + + + + +
# accordeon +

+
+

(Item add inputs and buttons)

+
    +
  • + + + + + + + +
    + +
    +
  • + +
+
+ +
+ + + + */ + + var categoryList = new Array ("Uncategorized"); // TODO : make an object here + + $.ajaxSetup({ + url: apiurl, + async: true, + dataType: "json", + type: "POST" + }); + + // add a category ( id: category ref in categoryList, name: display name ) + function addCategory(id,name) { + var content= '

'+name+'


'; + $("#shopcategory").append(content); + var addButtonOBJ=$('#cat_'+id); + addButtonOBJ.find( "#addItemButton" ).button().click(addItemClick); + addButtonOBJ.find( "#shopItems" ).sortable({ + items: "li" + }); + //XXX $("#title_"+id).droppable({drop: function (event,ui) { categoryMove(event, ui); } }); + //XXX $("#title_"+id).droppable({drop: function (event,ui) { categoryMove(event, ui, $("#title_"+id)); } }); + + return addButtonOBJ; + } + // Returns category ref in categoryList, or -1 if not found + function categoryID(name) { + for (index = 0; index < categoryList.length; index++) { + if (name == categoryList[index]) { + return index; + } + } + return -1; + } + /* Returns jquery object of a category identified by display name. + Create categoryList entry and/or object if not found.*/ + function GetCategoryOBJ(name) { + var index=categoryID(name); + if (index ==-1) { + index=categoryList.length; + categoryList[index]=name; + } + var catObject=$('#cat_'+index); + if (catObject.length ) { + return catObject + } + return addCategory(index,name) + } + // delete item in list. Only used by the delete button + function deleteItem(){ + var itemNameOBJ=$(this).parent().find(".itemName"); + var itemName=itemNameOBJ.html(); + //$(this).closest("#shopItemEntry").remove(); + var itemOBJ = $(this).closest("#shopItemEntry"); + + $.ajax({ + data: { + auth: "none", + function: "delete", + item: itemName, + }, + success: function (data) { + if (data.type == API_SUCCESS_DELETE) { + itemOBJ.remove(); + } + } + }); + } + // Add an item in the current category. Only used by "add" button of category + function addItemClick(){ + // Get the input values + var addedItemObj=$(this).parent().parent().find("#addItemName"); + var addedQtyObj=$(this).parent().parent().find("#addItemQty"); + var addedItem=addedItemObj.val(); + var addedQty=addedQtyObj.val(); + // Get category pointer and add item + var category=$(this).closest("div[id*='cat_']"); + + if(addedQty>0) { + } + else { + addedQty = 1; + addedQtyObj.val(addedQty); + } + + if(addedItem.length>0) { + + $.ajax({ + data: { + auth: "none", + function: "save", + item: addedItem, + count: addedQty + }, + success: function (data) { + if (data.type == API_SUCCESS_SAVE) { + addItem(category,addedItem,addedQty,false); + // reset values + addedItemObj.val(""); + addedQtyObj.val("1"); + } + else if (data.type == API_SUCCESS_UPDATE) { + // reset values + addedItemObj.val(""); + addedQtyObj.val("1"); + // easier than finding out on which item to update the QTY + refresh(); + } + } + }); + + $(this).blur(); + } + else addedItemObj.focus(); + } + /* Add an item (name : display name, amount : amount of items, checked : bool ) + to the category object categoryOBJ */ + function addItem(categoryOBJ, name, amount, checked) { + var isChecked,isCheckedName; + if (checked) { isChecked="itemCheckedTD"; isCheckedName=" itemCheckedName";} + else { isChecked="itemUncheckedTD";isCheckedName=""; } + var itemVal=$('
  • ' + + ' '+ + ' '+ + '
    ' + + ' ' + + ' '+name+'
  • '); + $(itemVal).appendTo(categoryOBJ.find("#shopItems")); + // set events + $(itemVal).find( ".itemDelete" ).click(deleteItem); + $(itemVal).find( ".itemCheck" ).click(checkItemToggle); + $(itemVal).find( "#itemQtyValue" ).change(updateItem); + } + + // Toggle check/uncheck of an item. Only used by "add" button of category + function checkItemToggle() { + var itemNameOBJ=$(this).parent().find(".itemName"); + if ($(this).hasClass("itemUncheckedTD") ) + { + $(this).removeClass("itemUncheckedTD"); + $(this).addClass("itemCheckedTD"); + itemNameOBJ.addClass("itemCheckedName"); + } else + { + $(this).removeClass("itemCheckedTD"); + $(this).addClass("itemUncheckedTD"); + itemNameOBJ.removeClass("itemCheckedName"); + } + } + + // Update item linked by itemObject + function updateItem() + { + var itemOBJ = $(this).closest("#shopItemEntry"); + var itemName=itemOBJ.find(".itemName").html(); + var itemQty=itemOBJ.find("#itemQtyValue").val(); + + if(itemQty>0) { + } + else { + itemQty = 1; + itemOBJ.find("#itemQtyValue").val(itemQty); + } + + $.ajax({ + data: { + auth: "none", + function: "save", + item: itemName, + count: itemQty + }, + success: function (data) { + if (data.type != API_SUCCESS_UPDATE) { + refresh(); + } + } + }); + + } + + // Delete all and refresh from DB + function refresh(){ + var addedItem="Error"; + var addedQty="Error"; + var addedchecked="Error"; + var addedcategory="Error"; + + //Remove all + $("#shopcategory").empty(); + categoryList=[]; + var defcategory=GetCategoryOBJ("Uncategorized"); + + $.ajax({ + data: { + auth: "none", + function: "listall" + }, + success: function (data) { + + //alert(data.type); + if (data.type == API_SUCCESS_LIST) { + for (var x = 0; x < data.items.length; x++) { + addedItem = data.items[x].itemTitle; + addedQty = data.items[x].itemCount; + checked = data.items[x].checked; + addedcategory = data.items[x].itemCategory; + if (addedcategory == null) { + addItem(defcategory, addedItem, addedQty, checked); + } else { + var categoryOBJ=GetCategoryOBJ(addedcategory); + addItem(categoryOBJ, addedItem, addedQty, checked); + } + } + //XXX $( "#shopcategory" ).accordion("refresh"); + } else + if (data.type == API_SUCCESS_LIST_EMPTY) { + alert ("Empty list"); + //TODO + } else { + alert ("Error : "+data.content); + } + } //success + + }); //ajax + + if($(this).blur) $(this).blur(); + } + + function removechecked(){ + var checked = $(".itemCheckedTD"); + if (checked.length>0) { + + var data = '['; + for (index = 0; index < checked.length; index++) { + data = data + '{"itemTitle":"'+$(checked[index]).parent().find(".itemName").html()+'"},'; + } + data = data.substr(0,data.length-1) + ']'; + + $.ajax({ + data: { + auth: "none", + function: "deleteMultiple", + jsonArray: data + }, + success: function (data) { + if (data.type == API_SUCCESS_DELETE) { + refresh(); + } + } + }); + + } + $(this).blur(); + } + + function getItemValues(itemOBJ) { + var retVal = new Array(); + retVal['checked']=itemOBJ.find(".itemCheck").hasClass("itemCheckedTD"); + retVal['amount']=itemOBJ.find("#itemQtyValue").val(); + retVal['name']=itemOBJ.find(".itemName").html(); + retVal['category']=itemOBJ.closest("div[id*='cat_']").attr("id"); + retVal['category'] = retVal['category'].substr(4); + return retVal; + } + + // Item is dropped in another category.Only used by drop event of category + function categoryMove(event, ui, category) { + //alert("Dropped"); + var itemValues = new Array (); + itemValues = getItemValues(ui.draggable); + var catID = category.attr("id").substr(6); // Remove this + var newCategory = $("#cat_"+catID); + ui.draggable.remove(); + addItem(newCategory,itemValues['name'],itemValues['amount'],itemValues['checked']); + } + + // Doc ready functions + $(function() { + + // for test cat + $( ".itemDelete" ).click(deleteItem); + $( "#shopItems" ).sortable({ items: "li" }); + $( "#addItemButton" ).button().click(addItemClick); + $( ".itemCheck" ).click(checkItemToggle()); + + // Main page + $( "#refresh" ).button().click(refresh); + $( "#checked" ).button().click(removechecked); + $( "#itemQtyValue" ).change(updateItem); + + /* XXX + // Categories accordeon + $( "#shopcategory" ).accordion({ + heightStyle: content,collapsible: true + }); + */ + + if ($("#shopcategory").length) refresh(); + }); + diff --git a/mysql_connector.php b/mysql_connector.php deleted file mode 100644 index 7894908..0000000 --- a/mysql_connector.php +++ /dev/null @@ -1,276 +0,0 @@ - server = $mysql_config['host']; - $this->database = $mysql_config['db']; - $this->username = $mysql_config['user']; - $this->password = $mysql_config['password']; - } - - function save($itemName, $itemCount) - { - if(empty($itemName)||empty($itemCount)) { - die(json_encode(array('type' => API_ERROR_MISSING_PARAMETER, 'content' => 'parameter missing'))); - } - //connect to db - $handler = new mysqli($this->server, $this->username, $this->password, $this->database); - - //check if connection successful - if ($handler->connect_error) { - die(json_encode(array('type' => API_ERROR_DATABASE_CONNECT, 'content' => $handler->connect_error))); - } - - //prepare query - $stmt = $handler->prepare("INSERT into ShoppingList(item,count) VALUES(?,?)"); - $stmt->bind_param('ss', $itemName, $itemCount); - - //execute query and check if successful - if ($stmt->execute()){ - $result = json_encode(array('type' => API_SUCCESS_SAVE, 'content' => $itemName.' saved')); - } else { - $result = json_encode(array('type' => API_ERROR_SAVE, 'content' => $stmt->error)); - } - - //close connection - $stmt->close(); - - //return result - return $result; - } - - function saveMultiple($jsonData) - { - if(empty($jsonData)) { - die(json_encode(array('type' => API_ERROR_MISSING_PARAMETER, 'content' => 'parameter missing for saveMultiple'))); - } - //connect to db - $handler = new mysqli($this->server, $this->username, $this->password, $this->database); - - //check if connection successful - if ($handler->connect_error) { - die(json_encode(array('type' => API_ERROR_DATABASE_CONNECT, 'content' => $handler->connect_error))); - } - //iterate over all items in json array - $array = json_decode( $jsonData, true ); - foreach($array as $item) - { - //prepare query - $stmt = $handler->prepare("INSERT into ShoppingList(item,count) VALUES(?,?)"); - $stmt->bind_param('ss', $item['itemTitle'], $item['itemCount']); - - //execute query and check if successful - if ($stmt->execute()){ - $result = json_encode(array('type' => API_SUCCESS_SAVE, 'content' => ' Multiple items saved')); - } else { - $result = json_encode(array('type' => API_ERROR_SAVE, 'content' => $stmt->error)); - } - } - - //close connection - $stmt->close(); - - //return result - return $result; - } - - function deleteMultiple($jsonData) - { - if(empty($jsonData)) { - die(json_encode(array('type' => API_ERROR_MISSING_PARAMETER, 'content' => 'parameter missing for deleteMultiple'))); - } - //connect to db - $handler = new mysqli($this->server, $this->username, $this->password, $this->database); - - //check if connection successful - if ($handler->connect_error) { - die(json_encode(array('type' => API_ERROR_DATABASE_CONNECT, 'content' => $handler->connect_error))); - } - //iterate over all items in json array - $array = json_decode( $jsonData, true ); - foreach($array as $item) - { - //prepare query - $stmt = $handler->prepare("DELETE from ShoppingList WHERE item = ?"); - $stmt->bind_param('s', $item['itemTitle']); - - //execute query and check if successful - if (!$stmt->execute()){ - $result = json_encode(array('type' => API_SUCCESS_DELETE, 'content' => ' Multiple items deleted')); - } else { - $result = json_encode(array('type' => API_ERROR_DELETE, 'content' => $stmt->error)); - } - } - - //close connection - $stmt->close(); - - //return result - return $result; - } - - function update($itemName, $itemCount) - { - if(empty($itemName)||empty($itemCount)){ - die(json_encode(array('type' => API_ERROR_MISSING_PARAMETER, 'content' => 'parameter missing'))); - } - //connect to db - $handler = new mysqli($this->server, $this->username, $this->password, $this->database); - - //check if connection successful - if ($handler->connect_error) { - die(json_encode(array('type' => API_ERROR_DATABASE_CONNECT, 'content' => $handler->connect_error))); - } - - //prepare query - $stmt = $handler->prepare("UPDATE ShoppingList SET count = ? WHERE item = ?"); - $stmt->bind_param('ss', $itemCount, $itemName); - - //execute query and check if successful - if ($stmt->execute()){ - $result = json_encode(array('type' => API_SUCCESS_UPDATE, 'content' => $itemName.' updated')); - } else { - $result = json_encode(array('type' => API_ERROR_UPDATE_, 'content' => $stmt->error)); - } - - //close connection - $stmt->close(); - - //return result - return $result; - } - - function delete($itemName) - { - if(empty($itemName)){ - die(json_encode(array('type' => API_ERROR_MISSING_PARAMETER, 'content' => 'parameter missing'))); - } - //connect to db - $handler = new mysqli($this->server, $this->username, $this->password, $this->database); - - //check if connection successful - if ($handler->connect_error) { - die(json_encode(array('type' => API_ERROR_DATABASE_CONNECT, 'content' => $handler->connect_error))); - } - - //prepare query - $stmt = $handler->prepare("DELETE FROM ShoppingList WHERE item = ?"); - $stmt->bind_param('s', $itemName); - - //execute query and check if successful - if ($stmt->execute()){ - $result = json_encode(array('type' => API_SUCCESS_DELETE, 'content' => $itemName.' deleted')); - } else { - $result = json_encode(array('type' => API_ERROR_DELETE, 'content' => $stmt->error)); - } - - //close connection - $stmt->close(); - - //return result - return $result; - } - - function exists($itemName) - { - if(empty($itemName)){ - die(json_encode(array('type' => API_ERROR_MISSING_PARAMETER, 'content' => 'parameter missing'))); - } - //connect to db - $handler = new mysqli($this->server, $this->username, $this->password, $this->database); - - //check if connection successful - if ($handler->connect_error) { - die(json_encode(array('type' => API_ERROR_DATABASE_CONNECT, 'content' => $handler->connect_error))); - } - - //prepare query - $stmt = $handler->prepare("SELECT item FROM ShoppingList WHERE item = ?"); - $stmt->bind_param('s', $itemName); - //execute query - $stmt->execute(); - - //bind the result - $stmt->store_result(); - if ($stmt->num_rows > 0){ - $itemExists = true; - } else { - $itemExists = false; - } - - //close connection - $stmt->close(); - return $itemExists; - - } - - function listall() - { - //connect to db - $handler = new mysqli($this->server, $this->username, $this->password, $this->database); - - //check if connection successful - if ($handler->connect_error) { - die(json_encode(array('type' => API_ERROR_DATABASE_CONNECT, 'content' => $handler->connect_error))); - } - - //prepare query - $stmt = $handler->prepare("SELECT item, count FROM ShoppingList ORDER BY item ASC"); - //execute query - $stmt->execute(); - $stmt->store_result(); - //bind the result - $stmt->bind_result($item_name, $item_count); - - //create array - $stack = array(); - - if($stmt->num_rows > 0) { - //put all rows into array - while ($stmt->fetch()) { - $listdata = array('itemTitle' => $item_name, 'itemCount' => $item_count, 'checked' => false); - array_push($stack, $listdata); - } - return json_encode(array('type' => API_SUCCESS_LIST, 'items' => $stack)); - } else { - return json_encode(array('type' => API_SUCCESS_LIST_EMPTY)); - } - - //close connection - $stmt->close(); - } - - function clear() - { - //connect to db - $handler = new mysqli($this->server, $this->username, $this->password, $this->database); - - //check if connection successful - if ($handler->connect_error) { - die(json_encode(array('type' => API_ERROR_DATABASE_CONNECT, 'content' => $handler->connect_error))); - } - - //prepare query - $stmt = $handler->prepare("TRUNCATE ShoppingList"); - - //execute query and check if successful - if ($stmt->execute()){ - $result = json_encode(array('type' => API_SUCCESS_CLEAR, 'content' => 'list cleared')); - } else { - $result = json_encode(array('type' => API_ERROR_CLEAR, 'content' => $stmt->error)); - } - - //close connection - $stmt->close(); - - //return result - return $result; - } - - } - -?> diff --git a/postinst.sh b/postinst.sh index 6817b67..c7b386e 100644 --- a/postinst.sh +++ b/postinst.sh @@ -1,18 +1,18 @@ #!/bin/bash - -echo "Press Enter to continue with update for ShoppingList database" +echo -n "Press Enter to update the ShoppingList database" read -HOST=`cat /var/www/sholi_test/config.php | grep host | cut -d\" -f2` -USER=`cat /var/www/sholi_test/config.php | grep user | cut -d\" -f2` -PASS=`cat /var/www/sholi_test/config.php | grep password | cut -d\" -f2` -DB=`cat /var/www/sholi_test/config.php | grep db | cut -d\" -f2` -echo "Updating Database. Please wait..." -echo -SQLSTATUS=`mysql -u $USER -p$PASS < update.sql` +BASE=`dirname $BASH_SOURCE` +HOST=`cat $BASE/config.php | grep host | cut -d\" -f2` +USER=`cat $BASE/config.php | grep user | cut -d\" -f2` +PASS=`cat $BASE/config.php | grep password | cut -d\" -f2` +DB=`cat $BASE/config.php | grep db | cut -d\" -f2` +echo "Starting the update. Please wait..." +SQLSTATUS=`mysql -h $HOST -u $USER -p$PASS $DB < $BASE/update.sql 2>&1` if [ $? -eq 0 ] then - echo "Successfully updated database!" + echo "The database has been updated successfully!" else - echo "Could not update database. See error above." + echo "Error updating the database:" + echo "$SQLSTATUS" fi diff --git a/sqlite_connector.php b/sqlite_connector.php deleted file mode 100644 index 6ba6cce..0000000 --- a/sqlite_connector.php +++ /dev/null @@ -1,136 +0,0 @@ -db = new SQLite3($dbfile, SQLITE3_OPEN_READWRITE | SQLITE3_OPEN_CREATE); - }catch(Exception $e){ - die(json_encode(array('type' => API_ERROR_DATABASE_CONNECT, 'content' => $e->getMessage()))); - } - $resultQuery = $this->db->query("SELECT COUNT(*) as count FROM sqlite_master WHERE type='table' AND name='itemlist'"); - $row = $resultQuery->fetchArray(); - if($row['count'] == 0){ - $this->db->exec("CREATE TABLE itemlist(ITEM TEXT PRIMARY KEY NOT NULL, COUNT INT NOT NULL);"); - } - } - - function __destructor(){ - $this->db->close(); - } - - function listall(){ - $resultQuery = $this->db->query("SELECT ITEM, COUNT FROM itemlist ORDER BY ITEM ASC;"); - $stack = array(); - if(!$resultQuery){ - return json_encode(array('type' => API_SUCCESS_LIST_EMPTY)); - } - while($item = $resultQuery->fetchArray()){ - $itemData = array( - 'itemTitle' => $item['ITEM'], - 'itemCount' => $item['COUNT'], - 'checked' => false - ); - array_push($stack, $itemData); - } - if(count($stack) == 0){ - return json_encode(array('type' => API_SUCCESS_LIST_EMPTY)); - }else{ - return json_encode(array('type' => API_SUCCESS_LIST, 'items' => $stack)); - } - } - - function exists($item){ - $resultQuery = $this->db->query("SELECT COUNT(*) as count FROM itemlist WHERE ITEM = '".$item."';"); - $row = $resultQuery->fetchArray(); - if($row['count'] > 0){ - return True; - }else{ - return False; - } - } - - function save($item, $count){ - $resultQuery = $this->db->query("INSERT INTO itemlist (ITEM, COUNT) VALUES('".$item."', ".$count.");"); - if($resultQuery){ - $result = json_encode(array('type' => API_SUCCESS_SAVE, 'content' => $item.' saved.')); - }else{ - $result = json_encode(array('type' => API_ERROR_SAVE, 'content' => 'Saving failed')); - } - return $result; - } - - function saveMultiple($jsonData){ - if(empty($jsonData)) { - die(json_encode(array('type' => API_ERROR_MISSING_PARAMETER, 'content' => 'parameter missing for saveMultiple'))); - } - //iterate over all items in json array - $array = json_decode( $jsonData, true ); - foreach($array as $item) - { - $resultQuery = $this->db->query("INSERT INTO itemlist (ITEM, COUNT) VALUES('".$item['itemTitle']."', ".$item['itemCount'].");"); - } - if($resultQuery){ - $result = json_encode(array('type' => API_SUCCESS_SAVE, 'content' => 'Multiple items saved')); - }else{ - $result = json_encode(array('type' => API_ERROR_SAVE, 'content' => 'Saving failed')); - } - return $result; - } - - function update($item, $count){ - $resultQuery = $this->db->query("UPDATE itemlist SET COUNT = ".$count." WHERE ITEM = '".$item."';"); - if($resultQuery){ - $result = json_encode(array('type' => API_SUCCESS_UPDATE, 'content' => $item.' updated.')); - }else{ - $result = json_encode(array('type' => API_ERROR_UPDATE_, 'content' => 'Updating failed')); - } - return $result; - } - - function deleteMultiple($jsonData){ - if(empty($jsonData)) { - die(json_encode(array('type' => API_ERROR_MISSING_PARAMETER, 'content' => 'parameter missing for deleteMultiple'))); - } - //iterate over all items in json array - $array = json_decode( $jsonData, true ); - foreach($array as $item) - { - $resultQuery = $this->db->query("DELETE FROM itemlist WHERE ITEM = '".$item['itemTitle']."';"); - } - if($resultQuery){ - $result = json_encode(array('type' => API_SUCCESS_DELETE, 'content' => 'Multiple items deleted')); - }else{ - $result = json_encode(array('type' => API_ERROR_DELETE, 'content' => 'Deleting failed')); - } - return $result; - } - - function delete($item){ - $resultQuery = $this->db->query("DELETE FROM itemlist WHERE ITEM = '".$item."';"); - if($resultQuery){ - $result = json_encode(array('type' => API_SUCCESS_DELETE, 'content' => $item.' deleted.')); - }else{ - $result = json_encode(array('type' => API_ERROR_DELETE, 'content' => 'Deleting failed')); - } - return $result; - } - - function clear(){ - $resultQuery = $this->db->query("DELETE FROM itemlist;"); - $this->db->exec("VACUUM;"); - if($resultQuery){ - $result = json_encode(array('type' => API_SUCCESS_CLEAR, 'content' => 'List cleared')); - }else{ - $result = json_encode(array('type' => API_ERROR_CLEAR, 'content' => 'Clearing failed')); - } - return $result; - } - - - } - -?> diff --git a/update.sql b/update.sql index 8048cfb..4a362e1 100644 --- a/update.sql +++ b/update.sql @@ -1,2 +1 @@ -use shopping; -alter table ShoppingList ADD COLUMN checked VARCHAR(128); +ALTER TABLE ShoppingList ADD COLUMN checked VARCHAR(128); From ac5ef9d33553fbf7023bbdc3539db584a331c5a9 Mon Sep 17 00:00:00 2001 From: the0ne Date: Fri, 8 Apr 2016 20:12:12 +0200 Subject: [PATCH 28/42] added viewport directive to support mobile device browsers --- index.php | 1 + 1 file changed, 1 insertion(+) diff --git a/index.php b/index.php index 579fd4b..a0555f3 100644 --- a/index.php +++ b/index.php @@ -33,6 +33,7 @@ function hash_equals($a, $b) { + Shopping List From f5b0bfbf2e3780ff6e62344c48ec9d257ef1cb9b Mon Sep 17 00:00:00 2001 From: the0ne Date: Fri, 8 Apr 2016 20:33:03 +0200 Subject: [PATCH 29/42] improving rwd compliance --- css/main.css | 4 ++++ index.php | 3 +++ 2 files changed, 7 insertions(+) diff --git a/css/main.css b/css/main.css index 0d1f4c9..ee585bb 100644 --- a/css/main.css +++ b/css/main.css @@ -58,3 +58,7 @@ button::before, .button::before { color: #aaaaaa; position: relative; } +.row { + margin-left: unset; + margin-right: unset; +} diff --git a/index.php b/index.php index a0555f3..66bc716 100644 --- a/index.php +++ b/index.php @@ -45,6 +45,7 @@ function hash_equals($a, $b) { +

    Shopping List

    @@ -71,11 +72,13 @@ function hash_equals($a, $b) { */ ?>
    +
    +
    From a3209faf15f5bf1f8e35304bde4dd6f470cf3767 Mon Sep 17 00:00:00 2001 From: the0ne Date: Mon, 11 Apr 2016 13:07:50 +0200 Subject: [PATCH 30/42] updated css for improved layout and tap handling --- css/main.css | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++ index.php | 20 +++++++--- js/main.js | 2 +- 3 files changed, 117 insertions(+), 7 deletions(-) diff --git a/css/main.css b/css/main.css index ee585bb..ab2dd15 100644 --- a/css/main.css +++ b/css/main.css @@ -62,3 +62,105 @@ button::before, .button::before { margin-left: unset; margin-right: unset; } +.row { + border-bottom: 1px solid transparent; + box-sizing: border-box; +} +.row > * { + box-sizing: border-box; + float: left; +} +.row::after, .row::before { + clear: both; + content: ""; + display: block; + height: 0; +} +.row.uniform > * > *:first-child { + margin-top: 0; +} +.row.uniform > * > *:last-child { + margin-bottom: 0; +} +.row > * { + padding: 0 0 0 1em; +} +.row { + margin: 0 0 -1px -1em; +} +.row.uniform > * { + padding: 1em 0 0 1em; +} +.row.uniform { + margin: -1em 0 -1px -1em; +} +@media screen and (max-width: 1680px) { +.row > * { + padding: 0 0 0 1em; +} +.row { + margin: 0 0 -1px -1em; +} +.row.uniform > * { + padding: 1em 0 0 1em; +} +.row.uniform { + margin: -1em 0 -1px -1em; +} +} +@media screen and (max-width: 1280px) { +.row > * { + padding: 0 0 0 1em; +} +.row { + margin: 0 0 -1px -1em; +} +.row.uniform > * { + padding: 1em 0 0 1em; +} +.row.uniform { + margin: -1em 0 -1px -1em; +} +} +@media screen and (max-width: 980px) { +.row > * { + padding: 0 0 0 1em; +} +.row { + margin: 0 0 -1px -1em; +} +.row.uniform > * { + padding: 1em 0 0 1em; +} +.row.uniform { + margin: -1em 0 -1px -1em; +} +} +@media screen and (max-width: 736px) { +.row > * { + padding: 0 0 0 1em; +} +.row { + margin: 0 0 -1px -1em; +} +.row.uniform > * { + padding: 1em 0 0 1em; +} +.row.uniform { + margin: -1em 0 -1px -1em; +} +} +@media screen and (max-width: 480px) { +.row > * { + padding: 0 0 0 1em; +} +.row { + margin: 0 0 -1px -1em; +} +.row.uniform > * { + padding: 1em 0 0 1em; +} +.row.uniform { + margin: -1em 0 -1px -1em; +} +} diff --git a/index.php b/index.php index 66bc716..2443463 100644 --- a/index.php +++ b/index.php @@ -55,15 +55,23 @@ function hash_equals($a, $b) {
    " method="post">

    API Key

    -
    -
    -
    +
    +
    +
    +
    +
    +
    -
    -
    - +
    +
    +
    +
    + +
    +
    +
    '+name+'

    '; + var content= '

    '+name+'


    '; $("#shopcategory").append(content); var addButtonOBJ=$('#cat_'+id); addButtonOBJ.find( "#addItemButton" ).button().click(addItemClick); From d1ef74318b3d31ec80d7690ded6b4706ce4efbe8 Mon Sep 17 00:00:00 2001 From: the0ne Date: Thu, 21 Apr 2016 16:39:23 +0200 Subject: [PATCH 31/42] webgui: add new element on enter button --- js/main.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/js/main.js b/js/main.js index 196068c..e2f80b4 100644 --- a/js/main.js +++ b/js/main.js @@ -91,6 +91,12 @@ var API_ERROR_LIST=6006; addButtonOBJ.find( "#shopItems" ).sortable({ items: "li" }); + addButtonOBJ.find( "#addItemName" ).keypress(function( event ) { + if ( event.which == 13 ) { + event.preventDefault(); + addButtonOBJ.find( "#addItemButton" ).click(); + } + }); //XXX $("#title_"+id).droppable({drop: function (event,ui) { categoryMove(event, ui); } }); //XXX $("#title_"+id).droppable({drop: function (event,ui) { categoryMove(event, ui, $("#title_"+id)); } }); From b4204d04c9882b1930320bc3e470bdc25b5789e0 Mon Sep 17 00:00:00 2001 From: the0ne Date: Thu, 23 Feb 2017 13:17:56 +0100 Subject: [PATCH 32/42] added read-only mode --- api.php | 9 ++++++--- index.php | 4 +++- js/main.js | 25 ++++++++++++++++++++++--- view.php | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 83 insertions(+), 7 deletions(-) create mode 100644 view.php diff --git a/api.php b/api.php index 5bf203e..e0ab6ef 100644 --- a/api.php +++ b/api.php @@ -42,16 +42,19 @@ function hash_equals($a, $b) { include('db_connector.php'); + $db = NEW DataBase($dataBase, $dbConfig); + $db->init(); //TODO: put this to INSTALL.php session_start(); - if (! isset($_SESSION['user_logged']) || $_SESSION['user_logged'] != 1) { + if (isset($_SESSION['user_logged']) && $_SESSION['user_logged'] == 0 && $_SESSION['user_read'] == 1) { + echo $db->listall(); + exit(); + } else if (! isset($_SESSION['user_logged']) || $_SESSION['user_logged'] != 1) { if (!hash_equals($authKey, crypt($auth, $authKey))){ die (json_encode(array('type' => API_ERROR_403, 'content' => 'Authentication failed.'))); } } - $db = NEW DataBase($dataBase, $dbConfig); - $db->init(); //TODO: put this to INSTALL.php switch ($function){ case 'listall': echo $db->listall(); diff --git a/index.php b/index.php index 2443463..4b97905 100644 --- a/index.php +++ b/index.php @@ -23,8 +23,9 @@ function hash_equals($a, $b) { return !$ret; } } - if (hash_equals($authKey, crypt($_POST['password'], $authKey))){ + if (hash_equals($authKey, crypt($_POST['password'], $authKey))) { $_SESSION['user_logged']=1; + $_SESSION['user_read']=0; } else { $login_error.="

    Invalid API Key

    "; } @@ -79,6 +80,7 @@ function hash_equals($a, $b) { */ ?> +

    '+name+'

    '; + if (read!=null && read.length) { + content= '

    '+name+'

    '; + } $("#shopcategory").append(content); var addButtonOBJ=$('#cat_'+id); addButtonOBJ.find( "#addItemButton" ).button().click(addItemClick); @@ -196,6 +200,7 @@ var API_ERROR_LIST=6006; to the category object categoryOBJ */ function addItem(categoryOBJ, name, amount, checked) { var isChecked,isCheckedName; + var read = $("#shopcategory").attr("data"); if (checked) { isChecked="itemCheckedTD"; isCheckedName=" itemCheckedName";} else { isChecked="itemUncheckedTD";isCheckedName=""; } var itemVal=$('
  • ' + @@ -204,11 +209,20 @@ var API_ERROR_LIST=6006; ' '+ ' '+ '
    '+name+'
  • '); + if (read!=null && read.length) { + itemVal=$('
  • ' + + ' '+ + '
    ' + + ' '+amount+'' + + ' '+name+'
  • '); + } $(itemVal).appendTo(categoryOBJ.find("#shopItems")); // set events - $(itemVal).find( ".itemDelete" ).click(deleteItem); - $(itemVal).find( ".itemCheck" ).click(checkItemToggle); - $(itemVal).find( "#itemQtyValue" ).change(updateItem); + if (read==null || read.length==0) { + $(itemVal).find( ".itemDelete" ).click(deleteItem); + $(itemVal).find( ".itemCheck" ).click(checkItemToggle); + $(itemVal).find( "#itemQtyValue" ).change(updateItem); + } } // Toggle check/uncheck of an item. Only used by "add" button of category @@ -332,6 +346,10 @@ var API_ERROR_LIST=6006; $(this).blur(); } + function share(){ + alert('Share the following URL: '+"\n"+$(this).attr("data")); + } + function getItemValues(itemOBJ) { var retVal = new Array(); retVal['checked']=itemOBJ.find(".itemCheck").hasClass("itemCheckedTD"); @@ -365,6 +383,7 @@ var API_ERROR_LIST=6006; // Main page $( "#refresh" ).button().click(refresh); $( "#checked" ).button().click(removechecked); + $( "#share" ).button().click(share); $( "#itemQtyValue" ).change(updateItem); /* XXX diff --git a/view.php b/view.php new file mode 100644 index 0000000..8645f1c --- /dev/null +++ b/view.php @@ -0,0 +1,52 @@ + + + + + + Shopping List View + + + + + + + + + + +
    +
    +
    +

    Shopping List View

    + + +
    +
    + + +
    +
    +
    + + From 2a8ada07db49775969297f7cc765759efa2faf97 Mon Sep 17 00:00:00 2001 From: Matthias Goebl Date: Mon, 6 Mar 2017 23:17:51 +0100 Subject: [PATCH 33/42] Added save and update of checked attribute --- api.php | 5 +++-- db_connector.php | 10 ++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/api.php b/api.php index 5bf203e..b4e7965 100644 --- a/api.php +++ b/api.php @@ -12,6 +12,7 @@ function hash_equals($a, $b) { $itemName = array_key_exists('item', $_POST) ? $_POST['item'] : null; $itemCount = array_key_exists('count', $_POST) ? $_POST['count'] : null; +$itemChecked = array_key_exists('checked', $_POST) ? $_POST['checked'] : "false"; $jsonData = array_key_exists('jsonArray', $_POST) ? $_POST['jsonArray'] : null; $function = array_key_exists('function', $_POST) ? $_POST['function'] : null; $auth = array_key_exists('auth', $_POST) ? $_POST['auth'] : null; @@ -58,9 +59,9 @@ function hash_equals($a, $b) { break; case 'save': if($db->exists($itemName)){ - echo $db->update($itemName, $itemCount); + echo $db->update($itemName, $itemCount, $itemChecked); } else { - echo $db->save($itemName, $itemCount); + echo $db->save($itemName, $itemCount, $itemChecked); } break; case 'saveMultiple': diff --git a/db_connector.php b/db_connector.php index 5144299..3e70403 100644 --- a/db_connector.php +++ b/db_connector.php @@ -75,9 +75,9 @@ function exists($item){ return (bool)count($stmt->fetchAll()); } - function save($item, $count){ + function save($item, $count, $checked){ try{ - $checked = (int)false; + $checked = $checked == "true" ? 1 : 0; $stmt = $this->db->prepare("INSERT INTO $this->table (item, count, checked) VALUES (:item, :count, :checked);"); $stmt->bindParam(':item', $item, PDO::PARAM_STR); $stmt->bindParam(':count', $count, PDO::PARAM_INT); @@ -110,11 +110,13 @@ function saveMultiple($jsonData){ } } - function update($item, $count){ + function update($item, $count, $checked){ try{ - $stmt = $this->db->prepare("UPDATE $this->table SET count=:count WHERE item=:item;"); + $checked = $checked == "true" ? 1 : 0; + $stmt = $this->db->prepare("UPDATE $this->table SET count=:count, checked=:checked WHERE item=:item;"); $stmt->bindParam(':item', $item, PDO::PARAM_STR); $stmt->bindParam(':count', $count, PDO::PARAM_INT); + $stmt->bindParam(':checked', $checked, PDO::PARAM_INT); $stmt->execute(); return json_encode(array('type' => API_SUCCESS_UPDATE, 'content' => 'Update successfull.')); }catch(PDOException $e){ From b50b0254de80ec4b11c10b9e67a39ef44a35d0ae Mon Sep 17 00:00:00 2001 From: Matthias Goebl Date: Sat, 11 Mar 2017 21:57:26 +0100 Subject: [PATCH 34/42] Added lookup of qr code --- CONSTANTS.php | 1 + api.php | 17 +++++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/CONSTANTS.php b/CONSTANTS.php index 287d56f..8727b82 100644 --- a/CONSTANTS.php +++ b/CONSTANTS.php @@ -27,4 +27,5 @@ define('API_ERROR_SAVE', 6004); define('API_ERROR_CLEAR', 6005); define('API_ERROR_LIST', 6006); + define('API_ERROR_QRCODE', 6007); ?> diff --git a/api.php b/api.php index b4e7965..b0ae85d 100644 --- a/api.php +++ b/api.php @@ -18,6 +18,7 @@ function hash_equals($a, $b) { $auth = array_key_exists('auth', $_POST) ? $_POST['auth'] : null; include('config.php'); +$outpanApiKey='1a74a95c40a331e50d4b2c7fe311328c'; // taken from https://github.com/johncipponeri/outpan-api-java if($authKey == ''){ if ($_SERVER['HTTP_USER_AGENT'] != "ShoLiApp"){ @@ -79,6 +80,22 @@ function hash_equals($a, $b) { case 'clear': echo $db->clear(); break; + case 'addQRcodeItem': + $response = file_get_contents("https://api.outpan.com/v2/products/" . $itemName . "/?apikey=" . $outpanApiKey); + $name = json_decode($response)->{'name'}; + if ( $name != "" ) { + $itemName = $name; + $itemCount = 1; + $itemChecked = "false"; + if( $db->exists($itemName) ) { + echo $db->update($itemName, $itemCount, $itemChecked); + } else { + echo $db->save($itemName, $itemCount, $itemChecked); + } + } else { + die (json_encode(array('type' => API_ERROR_QRCODE, 'content' => "Code not found: " . $itemName))); + } + break; default: die (json_encode(array('type' => API_ERROR_FUNCTION_NOT_SPECIFIED, 'content' => 'function not specified'))); From 1357ee74f0a063cae8d242bfb009015b804e70b8 Mon Sep 17 00:00:00 2001 From: "Thomas E. Horner" Date: Fri, 10 Aug 2018 11:01:06 +0200 Subject: [PATCH 35/42] added support for the checked flag --- api.php | 9 +++++---- db_connector.php | 13 ++++++++----- update.sql | 2 +- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/api.php b/api.php index e0ab6ef..9f69fc2 100644 --- a/api.php +++ b/api.php @@ -9,9 +9,10 @@ function hash_equals($a, $b) { return !$ret; } } - + $itemName = array_key_exists('item', $_POST) ? $_POST['item'] : null; $itemCount = array_key_exists('count', $_POST) ? $_POST['count'] : null; +$itemChecked = array_key_exists('checked', $_POST) ? $_POST['checked'] : null; $jsonData = array_key_exists('jsonArray', $_POST) ? $_POST['jsonArray'] : null; $function = array_key_exists('function', $_POST) ? $_POST['function'] : null; $auth = array_key_exists('auth', $_POST) ? $_POST['auth'] : null; @@ -61,9 +62,9 @@ function hash_equals($a, $b) { break; case 'save': if($db->exists($itemName)){ - echo $db->update($itemName, $itemCount); + echo $db->update($itemName, $itemCount, $itemChecked); } else { - echo $db->save($itemName, $itemCount); + echo $db->save($itemName, $itemCount, $itemChecked); } break; case 'saveMultiple': @@ -73,7 +74,7 @@ function hash_equals($a, $b) { echo $db->deleteMultiple($jsonData); break; case 'update': - echo $db->update($itemName, $itemCount); + echo $db->update($itemName, $itemCount, $itemChecked); break; case 'delete': echo $db->delete($itemName); diff --git a/db_connector.php b/db_connector.php index 5144299..783491b 100644 --- a/db_connector.php +++ b/db_connector.php @@ -31,6 +31,7 @@ function __construct($dbtype, $dbargs){ die(json_encode(array('type' => API_ERROR_MISSING_PARAMETER, 'content' => "Missing database parameters."))); } $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + //SQLITE $this->db->query('PRAGMA journal_mode=OFF;'); } function init(){ @@ -75,9 +76,9 @@ function exists($item){ return (bool)count($stmt->fetchAll()); } - function save($item, $count){ + function save($item, $count, $checked = false){ try{ - $checked = (int)false; + $checked = (int)$checked; $stmt = $this->db->prepare("INSERT INTO $this->table (item, count, checked) VALUES (:item, :count, :checked);"); $stmt->bindParam(':item', $item, PDO::PARAM_STR); $stmt->bindParam(':count', $count, PDO::PARAM_INT); @@ -110,13 +111,15 @@ function saveMultiple($jsonData){ } } - function update($item, $count){ + function update($item, $count, $checked){ try{ - $stmt = $this->db->prepare("UPDATE $this->table SET count=:count WHERE item=:item;"); + $checked = ($checked=='true' ? 1:0); + $stmt = $this->db->prepare("UPDATE $this->table SET count=:count, checked=:checked WHERE item=:item;"); $stmt->bindParam(':item', $item, PDO::PARAM_STR); $stmt->bindParam(':count', $count, PDO::PARAM_INT); + $stmt->bindParam(':checked', $checked, PDO::PARAM_INT); $stmt->execute(); - return json_encode(array('type' => API_SUCCESS_UPDATE, 'content' => 'Update successfull.')); + return json_encode(array('type' => API_SUCCESS_UPDATE, 'content' => 'Update successful.')); }catch(PDOException $e){ return json_encode(array('type' => API_ERROR_UPDATE_, 'content' => $e->getMessage())); } diff --git a/update.sql b/update.sql index 4a362e1..c3a1a7a 100644 --- a/update.sql +++ b/update.sql @@ -1 +1 @@ -ALTER TABLE ShoppingList ADD COLUMN checked VARCHAR(128); +ALTER TABLE ShoppingList ADD COLUMN checked INT NOT NULL; From 31929ef8d4f4edf49e6d32032a71783de8bf2d0f Mon Sep 17 00:00:00 2001 From: "Thomas E. Horner" Date: Fri, 10 Aug 2018 13:15:40 +0200 Subject: [PATCH 36/42] added local configuration to avoid accidentally checking-in the api-key or db-configuration and to enable upgrading without losing the configuration data --- .gitignore | 3 +++ config.php | 13 ++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 0ed1d5b..5baa208 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,6 @@ Network Trash Folder Temporary Items .apdisk +# Local configuration +config.local.php + diff --git a/config.php b/config.php index f78e2bd..e6243ec 100644 --- a/config.php +++ b/config.php @@ -10,15 +10,15 @@ $authKey = ''; /*########################################################## # Branch: stable, testing, experimental # - ############################################################*/ + ############################################################*/ $branch = "stable"; /*########################################################## # Database: MySQL, SQLite # - ############################################################*/ + ############################################################*/ $dataBase = "SQLite"; /*########################################################## # SQLite Config # - ############################################################*/ + ############################################################*/ $SQLiteConfig = array('file' => "shoppinglist.sqlite"); /*########################################################## # MySQL Config # @@ -29,4 +29,11 @@ 'user' => "your_mysql_user", 'password' => "your_mysql_password", ]; + + /*########################################################## + # if you put your config into a new file config.local.php # + # instead of changing the values above then in case of an # + # update of config.php you will not lose all your settings # + ############################################################*/ + @include('config.local.php'); ?> From 0b85afea2bc381e42078ef70f239395fd477bc12 Mon Sep 17 00:00:00 2001 From: "Thomas E. Horner" Date: Fri, 10 Aug 2018 13:48:33 +0200 Subject: [PATCH 37/42] added checked flag update support to web ui --- js/main.js | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/js/main.js b/js/main.js index 5bb3a01..47d7b96 100644 --- a/js/main.js +++ b/js/main.js @@ -228,7 +228,8 @@ var API_ERROR_LIST=6006; // Toggle check/uncheck of an item. Only used by "add" button of category function checkItemToggle() { var itemNameOBJ=$(this).parent().find(".itemName"); - if ($(this).hasClass("itemUncheckedTD") ) + var itemUnchecked=$(this).hasClass("itemUncheckedTD"); + if (itemUnchecked) { $(this).removeClass("itemUncheckedTD"); $(this).addClass("itemCheckedTD"); @@ -239,6 +240,26 @@ var API_ERROR_LIST=6006; $(this).addClass("itemUncheckedTD"); itemNameOBJ.removeClass("itemCheckedName"); } + + var itemOBJ = $(this).closest("#shopItemEntry"); + var itemName=itemOBJ.find(".itemName").html(); + var itemQty=itemOBJ.find("#itemQtyValue").val(); + + $.ajax({ + data: { + auth: "none", + function: "update", + item: itemName, + count: itemQty, + checked: itemUnchecked ? "true" : "false" + }, + success: function (data) { + if (data.type != API_SUCCESS_UPDATE) { + //refresh(); not required for toggling + } + } + }); + } // Update item linked by itemObject @@ -378,7 +399,7 @@ var API_ERROR_LIST=6006; $( ".itemDelete" ).click(deleteItem); $( "#shopItems" ).sortable({ items: "li" }); $( "#addItemButton" ).button().click(addItemClick); - $( ".itemCheck" ).click(checkItemToggle()); + $( ".itemCheck" ).click(checkItemToggle); // Main page $( "#refresh" ).button().click(refresh); From 5cd6026c61b54ac18cd576eb2ce20c45ee901376 Mon Sep 17 00:00:00 2001 From: "Thomas E. Horner" Date: Fri, 10 Aug 2018 14:07:23 +0200 Subject: [PATCH 38/42] additional check for addQRcodeItem --- api.php | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/api.php b/api.php index 12eb863..734167a 100644 --- a/api.php +++ b/api.php @@ -85,19 +85,23 @@ function hash_equals($a, $b) { break; case 'addQRcodeItem': $response = file_get_contents("https://api.outpan.com/v2/products/" . $itemName . "/?apikey=" . $outpanApiKey); - $name = json_decode($response)->{'name'}; - if ( $name != "" ) { - $itemName = $name; - $itemCount = 1; - $itemChecked = "false"; - if( $db->exists($itemName) ) { - echo $db->update($itemName, $itemCount, $itemChecked); + if($response!==false) { + $name = json_decode($response)->{'name'}; + if ( $name != "" ) { + $itemName = $name; + $itemCount = 1; + $itemChecked = "false"; + if( $db->exists($itemName) ) { + echo $db->update($itemName, $itemCount, $itemChecked); + } else { + echo $db->save($itemName, $itemCount, $itemChecked); + } } else { - echo $db->save($itemName, $itemCount, $itemChecked); + die (json_encode(array('type' => API_ERROR_QRCODE, 'content' => "Code not found: " . $itemName))); } } else { - die (json_encode(array('type' => API_ERROR_QRCODE, 'content' => "Code not found: " . $itemName))); - } + die (json_encode(array('type' => API_ERROR_QRCODE, 'content' => "Error querying outpan.com"))); + } break; default: die (json_encode(array('type' => API_ERROR_FUNCTION_NOT_SPECIFIED, 'content' => 'function not specified'))); From 589630d1117767595886a78e6cf81a549716548a Mon Sep 17 00:00:00 2001 From: "Thomas E. Horner" Date: Fri, 10 Aug 2018 14:44:43 +0200 Subject: [PATCH 39/42] changed from outpan.com to opengtindb.org --- api.php | 126 +++++++++++------ db_connector.php | 343 ++++++++++++++++++++++++----------------------- 2 files changed, 259 insertions(+), 210 deletions(-) diff --git a/api.php b/api.php index 734167a..65ec0c6 100644 --- a/api.php +++ b/api.php @@ -1,14 +1,14 @@ init(); //TODO: put this to INSTALL.php +$db = NEW DataBase($dataBase, $dbConfig); +$db->init(); //TODO: put this to INSTALL.php - session_start(); - if (isset($_SESSION['user_logged']) && $_SESSION['user_logged'] == 0 && $_SESSION['user_read'] == 1) { - echo $db->listall(); - exit(); - } else if (! isset($_SESSION['user_logged']) || $_SESSION['user_logged'] != 1) { - if (!hash_equals($authKey, crypt($auth, $authKey))){ - die (json_encode(array('type' => API_ERROR_403, 'content' => 'Authentication failed.'))); - } +session_start(); +if (isset($_SESSION['user_logged']) && $_SESSION['user_logged'] == 0 && $_SESSION['user_read'] == 1) { + echo $db->listall(); + exit(); +} else if (! isset($_SESSION['user_logged']) || $_SESSION['user_logged'] != 1) { + if (!hash_equals($authKey, crypt($auth, $authKey))){ + die (json_encode(array('type' => API_ERROR_403, 'content' => 'Authentication failed.'))); } - - switch ($function){ - case 'listall': +} + + +switch ($function){ + + case 'listall': echo $db->listall(); break; - case 'save': - if($db->exists($itemName)){ - echo $db->update($itemName, $itemCount, $itemChecked); + + case 'save': + if($db->exists($itemName)){ + echo $db->update($itemName, $itemCount, $itemChecked); } else { echo $db->save($itemName, $itemCount, $itemChecked); } break; - case 'saveMultiple': + + case 'saveMultiple': echo $db->saveMultiple($jsonData); break; - case 'deleteMultiple': + + case 'deleteMultiple': echo $db->deleteMultiple($jsonData); break; - case 'update': + + case 'update': echo $db->update($itemName, $itemCount, $itemChecked); break; - case 'delete': + + case 'delete': echo $db->delete($itemName); break; - case 'clear': + + case 'clear': echo $db->clear(); break; - case 'addQRcodeItem': - $response = file_get_contents("https://api.outpan.com/v2/products/" . $itemName . "/?apikey=" . $outpanApiKey); + + case 'addQRcodeItem': + /* OUTPAN + $outpanApiKey='1a74a95c40a331e50d4b2c7fe311328c'; // taken from https://github.com/johncipponeri/outpan-api-java + $response = file_get_contents('https://api.outpan.com/v2/products/' . $itemName . '/?apikey=' . $outpanApiKey); if($response!==false) { $name = json_decode($response)->{'name'}; - if ( $name != "" ) { + if ( $name != '' ) { $itemName = $name; $itemCount = 1; - $itemChecked = "false"; + $itemChecked = 'false'; if( $db->exists($itemName) ) { echo $db->update($itemName, $itemCount, $itemChecked); } else { echo $db->save($itemName, $itemCount, $itemChecked); } } else { - die (json_encode(array('type' => API_ERROR_QRCODE, 'content' => "Code not found: " . $itemName))); + die (json_encode(array('type' => API_ERROR_QRCODE, 'content' => 'Code not found: ' . $itemName))); } } else { - die (json_encode(array('type' => API_ERROR_QRCODE, 'content' => "Error querying outpan.com"))); + die (json_encode(array('type' => API_ERROR_QRCODE, 'content' => 'Error querying outpan.com'))); + } + //*/ + $opengtindbApiKey='400000000'; // taken from https://opengtindb.org/api.php + $response = file_get_contents('https://opengtindb.org/?ean=' . $itemName . '&cmd=query&queryid=' . $opengtindbApiKey); + if($response!==false) { + if(strpos($response,'error=0')!==false) { + $values = parse_ini_string(utf8_encode($response)); + //file_put_contents('/tmp/sholi',print_r($values, true)); + $name = trim($values['name']); + if ($name == '') $name = trim($values['detailname']); + if ($name == '') $name = trim($values['name_en']); + if ($name == '') $name = trim($values['detailname_en']); + if ($name == '') $name = trim($values['descr']); + + if ( $name != '' ) { + $itemName = $name; + $itemCount = 1; + $itemChecked = 'false'; + if( $db->exists($itemName) ) { + echo $db->update($itemName, $itemCount, $itemChecked); + } else { + echo $db->save($itemName, $itemCount, $itemChecked); + } + } else { + die (json_encode(array('type' => API_ERROR_QRCODE, 'content' => 'Code not found: ' . $itemName))); + } + } else { + die (json_encode(array('type' => API_ERROR_QRCODE, 'content' => 'Code not found: ' . $itemName . ', '.$response))); + } + } else { + die (json_encode(array('type' => API_ERROR_QRCODE, 'content' => 'Error querying opengtindb.org'))); } break; - default: + + default: die (json_encode(array('type' => API_ERROR_FUNCTION_NOT_SPECIFIED, 'content' => 'function not specified'))); - } - - -?> +} +?> diff --git a/db_connector.php b/db_connector.php index 8d3e72c..092f9a1 100644 --- a/db_connector.php +++ b/db_connector.php @@ -1,169 +1,178 @@ type = $dbtype; - switch($dbtype){ - case 'SQLite': - $db_pdo="sqlite:".$dbargs['file']; - try{ - $this->db = new PDO($db_pdo); - }catch(PDOException $e){ - die(json_encode(array('type' => API_ERROR_DATABASE_CONNECT, 'content' => $e->getMessage()))); - } - break; - case 'MySQL': - $db_pdo="mysql:host=".$dbargs['host'].";dbname=".$dbargs['db']; - try{ - $this->db = new PDO($db_pdo, $dbargs['user'], $dbargs['password']); - }catch(PDOException $e){ - die(json_encode(array('type' => API_ERROR_DATABASE_CONNECT, 'content' => $e->getMessage()))); - } - break; - default: - die(json_encode(array('type' => API_ERROR_MISSING_PARAMETER, 'content' => "Missing database parameters."))); - } - $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); - //SQLITE $this->db->query('PRAGMA journal_mode=OFF;'); - } - - function init(){ - $sql = "CREATE table $this->table( - item STRING PRIMARY KEY, - count INT NOT NULL, - checked INT NOT NULL, - category STRING);"; - try{ - $this->db->exec($sql); - }catch(PDOException $e){ - //die(json_encode(array('type' => API_ERROR_UNKNOWN, 'content' => $e->getMessage()))); //uncomment after init() has been put to INSTALL.php - } - } - - function listall(){ - try{ - $sql = "SELECT * FROM $this->table ORDER BY item ASC"; - $val = $this->db->query($sql); - $stack = array(); - foreach($val as $row){ - array_push($stack, array( - 'itemTitle' => $row['item'], - 'itemCount' => $row['count'], - 'checked' => (bool)$row['checked'], - 'itemCategory' => $row['category'])); - } - if(count($stack) == 0){ - return json_encode(array('type' => API_SUCCESS_LIST_EMPTY)); - }else{ - return json_encode(array('type' => API_SUCCESS_LIST, 'items' => $stack)); - } - }catch(PDOException $e){ - die(json_encode(array('type' => API_ERROR_LIST, 'content' => $e->getMessage()))); - } - } - - function exists($item){ - $stmt = $this->db->prepare("SELECT * from $this->table WHERE item=:item;"); - $stmt->bindParam(':item', $item, PDO::PARAM_STR); - $stmt->execute(); - return (bool)count($stmt->fetchAll()); - } - - function save($item, $count, $checked = false){ - try{ - $checked = ($checked=='true' ? 1:0); - $stmt = $this->db->prepare("INSERT INTO $this->table (item, count, checked) VALUES (:item, :count, :checked);"); - $stmt->bindParam(':item', $item, PDO::PARAM_STR); - $stmt->bindParam(':count', $count, PDO::PARAM_INT); - $stmt->bindParam(':checked', $checked, PDO::PARAM_INT); - $stmt->execute(); - return json_encode(array('type' => API_SUCCESS_SAVE, 'content' => $item.' saved.')); - }catch(PDOException $e){ - return json_encode(array('type' => API_ERROR_SAVE, 'content' => $e->getMessage())); - } - } - - function saveMultiple($jsonData){ - if(empty($jsonData)){ - die(json_encode(array('type' => API_ERROR_MISSING_PARAMETER, 'content' => 'parameter missing for saveMultiple'))); - } - $itemList = json_decode($jsonData, true); - $success = true; - $errors = array(); - foreach($itemList as $item){ - $output = json_decode($this->exists($item['itemTitle']) ? $this->update($item['itemTitle'], $item['itemCount']) : $this->save($item['itemTitle'], $item['itemCount']),true); - if($output['type']!=API_SUCCESS_SAVE && $output['type']!=API_SUCCESS_UPDATE) { - $success = false; - array_push($errors, array('itemTitle' => $item['itemTitle'], 'error' => $output['content'])); - } - } - if($success) { - return json_encode(array('type' => API_SUCCESS_SAVE, 'content' => count($itemList)>1 ? 'Multiple items saved.':$itemList[0]['itemTitle'].' saved.')); - } else { - return json_encode(array('type' => API_ERROR_SAVE, 'content' => $errors)); - } - } - - function update($item, $count, $checked = false){ - try{ - $checked = ($checked=='true' ? 1:0); - $stmt = $this->db->prepare("UPDATE $this->table SET count=:count, checked=:checked WHERE item=:item;"); - $stmt->bindParam(':item', $item, PDO::PARAM_STR); - $stmt->bindParam(':count', $count, PDO::PARAM_INT); - $stmt->bindParam(':checked', $checked, PDO::PARAM_INT); - $stmt->execute(); - return json_encode(array('type' => API_SUCCESS_UPDATE, 'content' => 'Update successful.')); - }catch(PDOException $e){ - return json_encode(array('type' => API_ERROR_UPDATE_, 'content' => $e->getMessage())); - } - } - - function deleteMultiple($jsonData){ - if(empty($jsonData)) { - die(json_encode(array('type' => API_ERROR_MISSING_PARAMETER, 'content' => 'Parameter missing for deleteMultiple.'))); - } - $itemList = json_decode($jsonData, true); - $success = true; - $errors = array(); - foreach($itemList as $item){ - $output = json_decode($this->delete($item['itemTitle']),true); - if($output['type']!=API_SUCCESS_DELETE) { - $success = false; - array_push($errors, array('itemTitle' => $item['itemTitle'], 'error' => $output['content'])); - } - } - if($success) { - return json_encode(array('type' => API_SUCCESS_DELETE, 'content' => count($itemList)>1 ? 'Multiple items deleted.':$itemList[0]['itemTitle'].' deleted.')); - } else { - return json_encode(array('type' => API_ERROR_DELETE, 'content' => $errors)); - } - } - - function delete($item){ - try{ - $stmt = $this->db->prepare("DELETE FROM $this->table WHERE item=:item"); - $stmt->bindParam(':item', $item, PDO::PARAM_STR); - $stmt->execute(); - return json_encode(array('type' => API_SUCCESS_DELETE, 'content' => 'Item deleted.')); - }catch(PDOException $e){ - return json_encode(array('type' => API_ERROR_DELETE, 'content' => $e->getMessage())); - } - } - - function clear(){ - try{ - $stmt = $this->db->exec("TRUNCATE TABLE $this->table;"); - return json_encode(array('type' => API_SUCCESS_CLEAR, 'content' => 'Database cleared.')); - }catch(PDOException $e){ - return json_encode(array('type' => API_ERROR_CLEAR, 'content' => $e->getMessage())); - } - } - - } + class DataBase{ + + var $db; + var $type; + var $table = "ShoppingList"; + + function __construct($dbtype, $dbargs){ + $this->type = $dbtype; + switch($dbtype){ + case 'SQLite': + $db_pdo="sqlite:".$dbargs['file']; + try{ + $this->db = new PDO($db_pdo); + }catch(PDOException $e){ + die(json_encode(array('type' => API_ERROR_DATABASE_CONNECT, 'content' => $e->getMessage()))); + } + break; + case 'MySQL': + $db_pdo="mysql:host=".$dbargs['host'].";dbname=".$dbargs['db']; + try{ + $this->db = new PDO($db_pdo, $dbargs['user'], $dbargs['password']); + }catch(PDOException $e){ + die(json_encode(array('type' => API_ERROR_DATABASE_CONNECT, 'content' => $e->getMessage()))); + } + break; + default: + die(json_encode(array('type' => API_ERROR_MISSING_PARAMETER, 'content' => "Missing database parameters."))); + } + $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + //SQLITE $this->db->query('PRAGMA journal_mode=OFF;'); + } + + function init(){ + $sql = "CREATE table $this->table( + item STRING PRIMARY KEY, + count INT NOT NULL, + checked INT NOT NULL, + category STRING);"; + try{ + $this->db->exec($sql); + }catch(PDOException $e){ + //die(json_encode(array('type' => API_ERROR_UNKNOWN, 'content' => $e->getMessage()))); //uncomment after init() has been put to INSTALL.php + } + } + + function listall(){ + try{ + $sql = "SELECT * FROM $this->table ORDER BY item ASC"; + $val = $this->db->query($sql); + $stack = array(); + foreach($val as $row){ + array_push($stack, array( + 'itemTitle' => $row['item'], + 'itemCount' => $row['count'], + 'checked' => (bool)$row['checked'], + 'itemCategory' => $row['category'])); + } + if(count($stack) == 0){ + return json_encode(array('type' => API_SUCCESS_LIST_EMPTY)); + }else{ + return json_encode(array('type' => API_SUCCESS_LIST, 'items' => $stack)); + } + }catch(PDOException $e){ + die(json_encode(array('type' => API_ERROR_LIST, 'content' => $e->getMessage()))); + } + } + + function exists($item){ + $stmt = $this->db->prepare("SELECT * from $this->table WHERE item=:item;"); + $stmt->bindParam(':item', $item, PDO::PARAM_STR); + $stmt->execute(); + return (bool)count($stmt->fetchAll()); + } + + function save($item, $count, $checked = false){ + try{ + $checked = ($checked=='true' ? 1:0); + $stmt = $this->db->prepare("INSERT INTO $this->table (item, count, checked) VALUES (:item, :count, :checked);"); + $stmt->bindParam(':item', $item, PDO::PARAM_STR); + $stmt->bindParam(':count', $count, PDO::PARAM_INT); + $stmt->bindParam(':checked', $checked, PDO::PARAM_INT); + $stmt->execute(); + return json_encode(array('type' => API_SUCCESS_SAVE, 'content' => $item.' saved.')); + }catch(PDOException $e){ + return json_encode(array('type' => API_ERROR_SAVE, 'content' => $e->getMessage())); + } + } + + function saveMultiple($jsonData){ + if(empty($jsonData)){ + die(json_encode(array('type' => API_ERROR_MISSING_PARAMETER, 'content' => 'parameter missing for saveMultiple'))); + } + $itemList = json_decode($jsonData, true); + $success = true; + $errors = array(); + foreach($itemList as $item){ + $output = json_decode($this->exists($item['itemTitle']) ? $this->update($item['itemTitle'], $item['itemCount']) : $this->save($item['itemTitle'], $item['itemCount']),true); + if($output['type']!=API_SUCCESS_SAVE && $output['type']!=API_SUCCESS_UPDATE) { + $success = false; + array_push($errors, array('itemTitle' => $item['itemTitle'], 'error' => $output['content'])); + } + } + if($success) { + return json_encode(array('type' => API_SUCCESS_SAVE, 'content' => count($itemList)>1 ? 'Multiple items saved.':$itemList[0]['itemTitle'].' saved.')); + } else { + return json_encode(array('type' => API_ERROR_SAVE, 'content' => $errors)); + } + } + + function update($item, $count, $checked = false){ + try{ + $stmt = $this->db->prepare("SELECT * from $this->table WHERE item=:item;"); + $stmt->bindParam(':item', $item, PDO::PARAM_STR); + $stmt->execute(); + $rows = $stmt->fetchAll(); + $prev = $rows[0]; + + $checked = ($checked=='true' ? 1:0); + $stmt = $this->db->prepare("UPDATE $this->table SET count=:count, checked=:checked WHERE item=:item;"); + $stmt->bindParam(':item', $item, PDO::PARAM_STR); + $stmt->bindParam(':count', $count, PDO::PARAM_INT); + $stmt->bindParam(':checked', $checked, PDO::PARAM_INT); + $stmt->execute(); + + if ($prev['count'] != $count) + return json_encode(array('type' => API_SUCCESS_UPDATE, 'content' => 'Update successful.')); + + return json_encode(array('type' => API_SUCCESS_UPDATE, 'content' => ($checked ? 'done':'unchecked'))); + }catch(PDOException $e){ + return json_encode(array('type' => API_ERROR_UPDATE_, 'content' => $e->getMessage())); + } + } + + function deleteMultiple($jsonData){ + if(empty($jsonData)) { + die(json_encode(array('type' => API_ERROR_MISSING_PARAMETER, 'content' => 'Parameter missing for deleteMultiple.'))); + } + $itemList = json_decode($jsonData, true); + $success = true; + $errors = array(); + foreach($itemList as $item){ + $output = json_decode($this->delete($item['itemTitle']),true); + if($output['type']!=API_SUCCESS_DELETE) { + $success = false; + array_push($errors, array('itemTitle' => $item['itemTitle'], 'error' => $output['content'])); + } + } + if($success) { + return json_encode(array('type' => API_SUCCESS_DELETE, 'content' => count($itemList)>1 ? 'Multiple items deleted.':$itemList[0]['itemTitle'].' deleted.')); + } else { + return json_encode(array('type' => API_ERROR_DELETE, 'content' => $errors)); + } + } + + function delete($item){ + try{ + $stmt = $this->db->prepare("DELETE FROM $this->table WHERE item=:item"); + $stmt->bindParam(':item', $item, PDO::PARAM_STR); + $stmt->execute(); + return json_encode(array('type' => API_SUCCESS_DELETE, 'content' => 'Item deleted.')); + }catch(PDOException $e){ + return json_encode(array('type' => API_ERROR_DELETE, 'content' => $e->getMessage())); + } + } + + function clear(){ + try{ + $stmt = $this->db->exec("TRUNCATE TABLE $this->table;"); + return json_encode(array('type' => API_SUCCESS_CLEAR, 'content' => 'Database cleared.')); + }catch(PDOException $e){ + return json_encode(array('type' => API_ERROR_CLEAR, 'content' => $e->getMessage())); + } + } + + } From da5aab535b9c0fdf0be39ad6b64988c1a1cf30e7 Mon Sep 17 00:00:00 2001 From: "Thomas E. Horner" Date: Fri, 10 Aug 2018 15:00:37 +0200 Subject: [PATCH 40/42] changed checked confirmation text to symbols --- db_connector.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db_connector.php b/db_connector.php index 092f9a1..ce4332e 100644 --- a/db_connector.php +++ b/db_connector.php @@ -128,7 +128,7 @@ function update($item, $count, $checked = false){ if ($prev['count'] != $count) return json_encode(array('type' => API_SUCCESS_UPDATE, 'content' => 'Update successful.')); - return json_encode(array('type' => API_SUCCESS_UPDATE, 'content' => ($checked ? 'done':'unchecked'))); + return json_encode(array('type' => API_SUCCESS_UPDATE, 'content' => ($checked ? '✔':'♻'))); }catch(PDOException $e){ return json_encode(array('type' => API_ERROR_UPDATE_, 'content' => $e->getMessage())); } From cc37534a2c4cedd14a2d7ae820d840e8bb9606b8 Mon Sep 17 00:00:00 2001 From: "Thomas E. Horner" Date: Wed, 9 Jan 2019 15:33:41 +0100 Subject: [PATCH 41/42] improved mysql-pdo utf8 support --- api.php | 8 ++++---- db_connector.php | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/api.php b/api.php index 65ec0c6..7e5c8f8 100644 --- a/api.php +++ b/api.php @@ -8,7 +8,7 @@ function hash_equals($a, $b) { $ret |= array_sum(unpack("C*", $a^$b)); return !$ret; } -} +} $itemName = array_key_exists('item', $_POST) ? $_POST['item'] : null; $itemCount = array_key_exists('count', $_POST) ? $_POST['count'] : null; @@ -66,9 +66,9 @@ function hash_equals($a, $b) { case 'save': if($db->exists($itemName)){ echo $db->update($itemName, $itemCount, $itemChecked); - } else { - echo $db->save($itemName, $itemCount, $itemChecked); - } + } else { + echo $db->save($itemName, $itemCount, $itemChecked); + } break; case 'saveMultiple': diff --git a/db_connector.php b/db_connector.php index ce4332e..cb2edcd 100644 --- a/db_connector.php +++ b/db_connector.php @@ -5,13 +5,13 @@ class DataBase{ var $db; var $type; - var $table = "ShoppingList"; + var $table = 'ShoppingList'; function __construct($dbtype, $dbargs){ $this->type = $dbtype; switch($dbtype){ case 'SQLite': - $db_pdo="sqlite:".$dbargs['file']; + $db_pdo='sqlite:'.$dbargs['file']; try{ $this->db = new PDO($db_pdo); }catch(PDOException $e){ @@ -19,15 +19,15 @@ function __construct($dbtype, $dbargs){ } break; case 'MySQL': - $db_pdo="mysql:host=".$dbargs['host'].";dbname=".$dbargs['db']; + $db_pdo='mysql:host='.$dbargs['host'].';dbname='.$dbargs['db'].';charset=utf8'; try{ - $this->db = new PDO($db_pdo, $dbargs['user'], $dbargs['password']); + $this->db = new PDO($db_pdo, $dbargs['user'], $dbargs['password'], array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'")); }catch(PDOException $e){ die(json_encode(array('type' => API_ERROR_DATABASE_CONNECT, 'content' => $e->getMessage()))); } break; default: - die(json_encode(array('type' => API_ERROR_MISSING_PARAMETER, 'content' => "Missing database parameters."))); + die(json_encode(array('type' => API_ERROR_MISSING_PARAMETER, 'content' => 'Missing database parameters.'))); } $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); //SQLITE $this->db->query('PRAGMA journal_mode=OFF;'); From 644e8ba0dd5f4b762883d962ea49456020cc20da Mon Sep 17 00:00:00 2001 From: "Thomas E. Horner" Date: Thu, 10 Jan 2019 20:14:28 +0100 Subject: [PATCH 42/42] finally utf8 characters are handled and shown correctly =) --- db_connector.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/db_connector.php b/db_connector.php index cb2edcd..f0584a8 100644 --- a/db_connector.php +++ b/db_connector.php @@ -19,9 +19,9 @@ function __construct($dbtype, $dbargs){ } break; case 'MySQL': - $db_pdo='mysql:host='.$dbargs['host'].';dbname='.$dbargs['db'].';charset=utf8'; + $db_pdo='mysql:host='.$dbargs['host'].';dbname='.$dbargs['db'].';charset=utf8mb4'; try{ - $this->db = new PDO($db_pdo, $dbargs['user'], $dbargs['password'], array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'")); + $this->db = new PDO($db_pdo, $dbargs['user'], $dbargs['password'], array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8mb4' COLLATE 'utf8mb4_bin'")); }catch(PDOException $e){ die(json_encode(array('type' => API_ERROR_DATABASE_CONNECT, 'content' => $e->getMessage()))); }