summaryrefslogtreecommitdiff
path: root/public/app/models
diff options
context:
space:
mode:
authorGeorge Halkiadakis <gchalkiadakis@sklavenitis.co.gr>2023-04-27 03:47:30 +0300
committerGeorge Halkiadakis <gchalkiadakis@sklavenitis.co.gr>2023-04-27 03:47:30 +0300
commit059e0d95d0c28bc5060e87e146eaf7411f51bf90 (patch)
tree7c21ac432f16dde2329a0d5954d70711eceb48e6 /public/app/models
parent26cd8ee99659ef1926c96a049c93645ffc9b169d (diff)
downloadgyraf1gov-059e0d95d0c28bc5060e87e146eaf7411f51bf90.tar.gz
gyraf1gov-059e0d95d0c28bc5060e87e146eaf7411f51bf90.tar.bz2
gyraf1gov-059e0d95d0c28bc5060e87e146eaf7411f51bf90.zip
skeleton commit; based on an anom project
Diffstat (limited to 'public/app/models')
-rw-r--r--public/app/models/.gitkeep0
-rw-r--r--public/app/models/Jorge.php172
-rw-r--r--public/app/models/_info.md150
-rw-r--r--public/app/models/admin/History_model.php34
-rw-r--r--public/app/models/admin/Privilege_model.php42
-rw-r--r--public/app/models/admin/User_model.php299
-rw-r--r--public/app/models/cms/Course_model.php314
-rw-r--r--public/app/models/cms/Media_model.php36
-rw-r--r--public/app/models/cms/Page_model.php18
-rw-r--r--public/app/models/market/Market_repository.php235
-rw-r--r--public/app/models/market/Payment_template.txt98
-rw-r--r--public/app/models/market/ProductCategories_model.php199
-rw-r--r--public/app/models/market/Product_model.php49
-rw-r--r--public/app/models/todo.md132
14 files changed, 1778 insertions, 0 deletions
diff --git a/public/app/models/.gitkeep b/public/app/models/.gitkeep
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/public/app/models/.gitkeep
diff --git a/public/app/models/Jorge.php b/public/app/models/Jorge.php
new file mode 100644
index 0000000..5d79007
--- /dev/null
+++ b/public/app/models/Jorge.php
@@ -0,0 +1,172 @@
+<?php
+
+namespace app\models;
+
+use \Registry;
+
+/** Jorge
+ * a database reference and adminitration toolkit
+ * ---
+ * The name Jorge comes from Uberto Eco's book: 'il nome della rosa',
+ * (Jorge is the blind monk who supervises the monastery's library ;)
+ */
+class Jorge
+{
+
+ /** tables
+ * array to keep all database-tables' critical information.
+ * @key create (array) includes all 'CREATE TABLE' statements
+ * @key relate (array) includes relations between tables
+ * @key fields (array) includes all important fields (when no select*)
+ * @key filter (array) includes all fields used to filter results
+ */
+ private $tables = [
+ 'create' => [],
+ 'relate' => [],
+ 'fields' => []
+ ];
+
+ public function __construct()
+ {
+ $db = Registry::use('database');
+
+ // get all tables
+ $tableList = $db->runQuery("SHOW TABLES", []);
+
+ // get all CREATE TABLE statements
+ foreach($tableList as $tableArray) {
+ $table = array_shift($tableArray);
+ $create = $db->runQuery("SHOW CREATE TABLE {$table}", []);
+ $this->tables['create'][$table] = $create[0]['Create Table'];
+ }
+
+ // define relations
+ $this->tables['relate'] = [
+
+ 'lesson' => [
+ 'course' => 'course.id = lesson.course_id',
+ 'lesson_media' => 'lesson_media.lesson_id = lesson.id',
+ 'lesson_privilege' => 'lesson_privilege.lesson_id = lesson.id'
+ ],
+
+ 'page' => [
+ 'page_media' => 'page_media.page_id = page.id'
+ ],
+
+ 'user' => [
+ 'user_privilege' => 'user_privilege.user_id = user.id'
+ ],
+
+ 'privilege' => [
+ 'user_privilege' => 'user_privilege.privilege_id = privilege.id'
+ ],
+
+ ];
+
+ $this->extractTableFields();
+
+ // return true;
+ }
+
+
+ /** Create...
+ * one or more database tables
+ * ---
+ * @param $datasource (string|void) : table name or none
+ * if none then all tables will be created
+ * @return (boolean)
+ *
+ * NOTE:
+ * for safety reasons this method is not executing SQL;
+ * it just echoes the SQL that needs to be executed in
+ * order to crate all database tables.
+ */
+ public function create($datasource = '')
+ {
+ // if no datasource passed then datasource is all tables
+ if ($datasource == '') {
+ $datasource = array_keys(self::$tables['create']);
+
+ } else {
+ // if datasource exist, make it an array
+ if (array_key_exists($datasource, self::$tables['create'])) {
+ $datasource = [ $datasource ];
+
+ } else {
+ // table does not exist
+ return fasle;
+ }
+ }
+
+ $sql = "";
+ foreach( $datasource as $table ) {
+ $sql .= "-- CREATE ". $table .";\n". self::$tables['create'][$table] . "\n\n";
+ }
+
+ return ['sql' => $sql];
+ }
+
+
+ /** Calculate fields of every table
+ * ---
+ * Parse every CREATE SQL statement and extract list of fields;
+ * Algorithm implements linear-parsing (faster than a recursive one)
+ */
+ private function extractTableFields()
+ {
+ // words used by SQL that can not be field names
+ $nonFields = ['PRIMARY', 'KEY', '(', ')', 'CONSTRAINT', 'UNIQUE']; // reduced list (used on CREATE)
+
+ foreach( $this->tables['create'] as $table => $sqlCreate ) {
+
+ $fields = []; // variable to hold the extracted fields
+
+ // get text between first open-parentesis and last close-parentesis
+ // this is waht lies between 'CREATE TABLE table(' and ') [ENGINE whatever]'
+ // (including the patenteses)
+ preg_match_all(
+ "/\((((?>[^()]+)|(?R))*)\)/",
+ str_replace("\n", '', $sqlCreate),
+ $match
+ );
+ // remove 1st+last parentesis
+ $mainDefinitions = substr($match[0][0], 1, -1);
+
+ // replace comma (,) on sub-parenthesis
+ // ex: 'decimal(18, 2)' turns to 'decimal(18: 2)'
+ // or: 'PRIMARY KEY (id1, id2)' turns to 'PRIMARY KEY (id1: id2)'
+ $sentences = preg_replace(
+ '/\\(([0-9a-zA-Z_`]*)[ ,]([0-9a-zA-Z_` ]*)\\)/',
+ '($1:$2)',
+ $mainDefinitions,
+ -1
+ );
+
+ // devide the banth of sentences to field definitions
+ $defines = explode(',', $sentences);
+
+ // now each denine holds a full field definition
+ // like: `ID` int(11) NOT NULL AUTO_INCREMENT
+
+ foreach($defines as $def) {
+ // check the first term of each sentence
+ $terms = explode(' ', trim($def,));
+ $term = array_shift($terms);
+
+ if (!in_array($term, $nonFields)) {
+ $fields[ str_replace('`', '', $term) ] = implode(' ', $terms);
+ }
+ }
+
+ $this->tables['fields'][$table] = $fields;
+ }
+
+ return;
+ }
+
+ public function databaseDocumentation()
+ {
+ return $this->tables['fields'];
+ }
+
+} \ No newline at end of file
diff --git a/public/app/models/_info.md b/public/app/models/_info.md
new file mode 100644
index 0000000..5504523
--- /dev/null
+++ b/public/app/models/_info.md
@@ -0,0 +1,150 @@
+
+# info about the structrure of the models
+
+According to the former impementation (atcom) the project will need to handle 200+ tables.
+The former project includes 209 tables with 5 or more rows (and 325 tables totaly)
+
+
+
+## Database schema
+
+Main tables and recomended constraints:
+
+```
+SET NAMES utf8;
+SET time_zone = '+00:00';
+SET foreign_key_checks = 0;
+SET sql_mode = 'NO_AUTO_VALUE_ON_ZERO';
+
+SET NAMES utf8mb4;
+
+DROP TABLE IF EXISTS `course`;
+CREATE TABLE `course` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `parent_id` int(11) NOT NULL COMMENT 'if 0 then this is a root course',
+ `label` varchar(140) COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+
+DROP TABLE IF EXISTS `lesson`;
+CREATE TABLE `lesson` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `course_id` int(11) NOT NULL,
+ `title` varchar(140) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `body` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `published` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0 = unpublished, 1 = published',
+ PRIMARY KEY (`id`),
+ KEY `course_id` (`course_id`),
+ CONSTRAINT `lesson_ibfk_1` FOREIGN KEY (`course_id`) REFERENCES `course` (`id`),
+ CONSTRAINT `lesson_ibfk_2` FOREIGN KEY (`course_id`) REFERENCES `course` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+
+DROP TABLE IF EXISTS `lesson_media`;
+CREATE TABLE `lesson_media` (
+ `lesson_id` int(11) NOT NULL,
+ `media_id` int(11) NOT NULL,
+ PRIMARY KEY (`lesson_id`,`media_id`),
+ KEY `media_id` (`media_id`),
+ CONSTRAINT `lesson_media_ibfk_1` FOREIGN KEY (`lesson_id`) REFERENCES `lesson` (`id`),
+ CONSTRAINT `lesson_media_ibfk_2` FOREIGN KEY (`lesson_id`) REFERENCES `lesson` (`id`),
+ CONSTRAINT `lesson_media_ibfk_3` FOREIGN KEY (`lesson_id`) REFERENCES `lesson` (`id`),
+ CONSTRAINT `lesson_media_ibfk_4` FOREIGN KEY (`media_id`) REFERENCES `media` (`id`),
+ CONSTRAINT `lesson_media_ibfk_5` FOREIGN KEY (`lesson_id`) REFERENCES `lesson` (`id`) ON DELETE NO ACTION,
+ CONSTRAINT `lesson_media_ibfk_6` FOREIGN KEY (`media_id`) REFERENCES `media` (`id`) ON DELETE NO ACTION,
+ CONSTRAINT `lesson_media_ibfk_7` FOREIGN KEY (`lesson_id`) REFERENCES `lesson` (`id`) ON DELETE NO ACTION,
+ CONSTRAINT `lesson_media_ibfk_8` FOREIGN KEY (`media_id`) REFERENCES `media` (`id`) ON DELETE NO ACTION
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+
+DROP TABLE IF EXISTS `lesson_privilege`;
+CREATE TABLE `lesson_privilege` (
+ `lesson_id` int(11) NOT NULL,
+ `privilege_id` int(11) NOT NULL COMMENT 'minimum privilege required to access the lesson',
+ KEY `lesson_id` (`lesson_id`),
+ KEY `privilege_id` (`privilege_id`),
+ CONSTRAINT `lesson_privilege_ibfk_1` FOREIGN KEY (`lesson_id`) REFERENCES `lesson` (`id`),
+ CONSTRAINT `lesson_privilege_ibfk_2` FOREIGN KEY (`privilege_id`) REFERENCES `privilege` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+
+DROP TABLE IF EXISTS `media`;
+CREATE TABLE `media` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `label` varchar(140) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `type` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `path` varchar(320) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `referable` tinyint(4) NOT NULL DEFAULT '1' COMMENT '0 = hidden, 1 = referable',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+
+DROP TABLE IF EXISTS `page`;
+CREATE TABLE `page` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `title` varchar(140) COLLATE utf8mb4_unicode_ci NOT NULL,
+ `body` text COLLATE utf8mb4_unicode_ci NOT NULL,
+ `published` tinyint(4) NOT NULL DEFAULT '0' COMMENT '0 = unpublished; 1 = published',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+
+DROP TABLE IF EXISTS `page_media`;
+CREATE TABLE `page_media` (
+ `page_id` int(11) NOT NULL,
+ `media_id` int(11) NOT NULL,
+ KEY `page_id` (`page_id`),
+ KEY `media_id` (`media_id`),
+ CONSTRAINT `page_media_ibfk_1` FOREIGN KEY (`page_id`) REFERENCES `page` (`id`) ON DELETE NO ACTION,
+ CONSTRAINT `page_media_ibfk_2` FOREIGN KEY (`media_id`) REFERENCES `media` (`id`) ON DELETE NO ACTION
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+
+DROP TABLE IF EXISTS `privilege`;
+CREATE TABLE `privilege` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `alias` varchar(8) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT 'keep it simple; use latin',
+ `label` varchar(140) COLLATE utf8mb4_unicode_ci NOT NULL,
+ PRIMARY KEY (`id`),
+ UNIQUE KEY `alias` (`alias`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+
+DROP TABLE IF EXISTS `privilege_inherit`;
+CREATE TABLE `privilege_inherit` (
+ `higher_id` int(11) NOT NULL COMMENT 'higher priviledges inherit (include) lower ones',
+ `lower_id` int(11) NOT NULL,
+ PRIMARY KEY (`higher_id`,`lower_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+
+DROP TABLE IF EXISTS `user`;
+CREATE TABLE `user` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `first_name` int(11) NOT NULL,
+ `last_name` int(11) NOT NULL,
+ `email` int(11) NOT NULL,
+ `expiration` int(11) NOT NULL COMMENT 'account expiration date; 0 = never',
+ `password` int(11) NOT NULL,
+ `salt` int(11) NOT NULL,
+ `otp` int(11) NOT NULL COMMENT 'one time password for reset password',
+ `otp_expiration` int(11) NOT NULL,
+ `active` int(11) NOT NULL COMMENT 'account flag; 1=active, 0=inactive',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+
+DROP TABLE IF EXISTS `user_privilege`;
+CREATE TABLE `user_privilege` (
+ `user_id` int(11) NOT NULL,
+ `privilege_id` int(11) NOT NULL,
+ PRIMARY KEY (`user_id`,`privilege_id`),
+ KEY `privilege_id` (`privilege_id`),
+ CONSTRAINT `user_privilege_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`),
+ CONSTRAINT `user_privilege_ibfk_2` FOREIGN KEY (`privilege_id`) REFERENCES `privilege` (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+```
+
+
diff --git a/public/app/models/admin/History_model.php b/public/app/models/admin/History_model.php
new file mode 100644
index 0000000..04ce630
--- /dev/null
+++ b/public/app/models/admin/History_model.php
@@ -0,0 +1,34 @@
+<?php
+namespace app\models\admin;
+
+use \Registry;
+
+
+class History_model
+{
+
+ /** track user access
+ *
+ */
+ public static function trackUserAccess($user_id, $type, $message, $note='_')
+ {
+
+ Registry::use('database')->query(
+ "INSERT INTO history
+ (user_id, `type`, `message`, `note`, `ip`)
+ VALUES
+ (:uid, :type, :msg, :note, :ip)",
+ [
+ ':uid' => $user_id,
+ ':type' => $type,
+ ':msg' => $message,
+ ':note' => $note,
+ ':ip' => Registry::get('REQUEST')->IP
+ ]
+ )->lastInsertID();
+
+ }
+
+
+
+} \ No newline at end of file
diff --git a/public/app/models/admin/Privilege_model.php b/public/app/models/admin/Privilege_model.php
new file mode 100644
index 0000000..c385484
--- /dev/null
+++ b/public/app/models/admin/Privilege_model.php
@@ -0,0 +1,42 @@
+<?php
+
+namespace app\models\admin;
+
+use Registry;
+use Render;
+
+
+class Privilege_model {
+
+ /** privileges
+ * ---
+ * get all privileges
+ *
+ * @param void
+ * @return privileges (array)
+ */
+ public static function privileges()
+ {
+ return Registry::use('database')->runQuery(
+ "SELECT * FROM privilege",[]
+ );
+ }
+
+ /** edit privileges
+ * ---
+ */
+ public static function edit_privileges()
+ {
+ // TODO: ...
+ }
+
+
+ /** update course
+ * ---
+ */
+ public static function update_course()
+ {
+ // TODO: ...
+ }
+
+} \ No newline at end of file
diff --git a/public/app/models/admin/User_model.php b/public/app/models/admin/User_model.php
new file mode 100644
index 0000000..2885dc1
--- /dev/null
+++ b/public/app/models/admin/User_model.php
@@ -0,0 +1,299 @@
+<?php
+
+namespace app\models\admin;
+
+use \Registry;
+use app\models\admin\History_model as History;
+
+class User_model
+{
+
+ /** check user (by index key)
+ *
+ * @param $indexKey (string) : key for user identification
+ * @param $value (string)
+ *
+ * @param $email (string): user's email
+ */
+ public static function checkUser($email)
+ {
+ $user = Registry::use('database')->query(
+ "SELECT * FROM user WHERE email = :email AND active = 1",
+ [ ':email' => $email ]
+ )->getFirst();
+
+ // if no user, return false
+ if ($user === false) return false;
+
+ return $user;
+ }
+
+
+ /** get user (by index key)
+ *
+ * user detailed array
+ * includes all user properties + granted roles + privileges
+ *
+ * @param $id (int) : user id
+ * @param $value (string)
+ */
+ public static function getUser($id)
+ {
+ $user = Registry::use('database')->query(
+ "SELECT user.*,
+ ( -- construct array (json) of roles granted to user
+ SELECT CONCAT(
+ '[',
+ GROUP_CONCAT(role.id),
+ ']'
+ )
+ FROM `role`
+ WHERE role.id IN (
+ SELECT user_role.role_id
+ FROM user_role
+ WHERE user_role.user_id = :id
+ )
+ ) AS Roles_json,
+ ( -- construct array of (root-)privileges granted to user
+ SELECT CONCAT(
+ '[',
+ GROUP_CONCAT(privilege.id),
+ ']'
+ )
+ FROM privilege
+ WHERE privilege.id IN (
+ SELECT user_privilege.privilege_id
+ FROM user_privilege
+ WHERE user_privilege.user_id = :id
+ )
+ ) AS RootPrivileges_json,
+ ( -- construct array of (root-)privileges granted to user
+ SELECT CONCAT(
+ '[',
+ GROUP_CONCAT(privilege.includes),
+ ']'
+ )
+ FROM privilege
+ WHERE privilege.id IN (
+ SELECT user_privilege.privilege_id
+ FROM user_privilege
+ WHERE user_privilege.user_id = :id
+ )
+ ) AS SubPrivileges_json
+ FROM user
+ WHERE id = :id",
+ [ ':id' => $id ]
+ )->getFirst();
+
+
+ // if no user, return false
+ if ($user === false) return false;
+
+
+ // TODO:
+ // * merge root+sub privilede lists
+ // * convert json strings to php arrays
+
+
+ // TODO:
+ // cache user super array
+
+ return $user;
+ }
+
+
+ /** create user
+ *
+ * creates user record;
+ * assigns privileged (usualy defaults);
+ * creates activation_code
+ *
+ * @param $data (array): Request->POST array
+ * @param $password (string): secure hashed password
+ *
+ * @return $activation_code
+ *
+ */
+ public static function registerUser($data, $password, $privileges = DEFAULT_PRIVILEGES)
+ {
+ $required_fields = [
+ 'name',
+ 'surname',
+ 'email',
+ 'password'
+ ];
+
+ // check required fields
+ $isOK = true;
+ foreach($required_fields as $fi) {
+ if (empty($data[$fi])) $isOK = false;
+ }
+ // if empty required fields exists ... return false
+ if (!$isOK) {
+ return [ "success" => false, 'error' => EMPTY_REQUIRED_FIELDS ];
+ }
+
+
+ // TODO:
+ // check if email exists
+ // ...
+
+ // create an activation code
+ $activation_code = md5($data['email'].time().rand(0, 10000));
+
+ // if isOK go on and...
+ // create user record
+ $new_user_id = Registry::use('database')->query(
+ "INSERT INTO user
+ (`first_name`, `last_name`, `email`, `password`, `active`, `activation`)
+ VALUES
+ (:nam, :surname, :email, :pass, :act, :actcode)",
+ [
+ ':nam' => $data['name'],
+ ':surname' => $data['surname'],
+ ':email' => $data['email'],
+ ':pass' => $password,
+ ':act' => 0, // needs email confirmation to be activated ...
+ ':actcode' => $activation_code // ... with the activation code
+ ]
+ )->lastInsertID();
+
+ // set default privileges
+ self::set_user_privileges($new_user_id, $privileges);
+
+ // set reader role
+ self::set_user_role($new_user_id, 5);
+
+ // update history
+ History::trackUserAccess($new_user_id, TRACK_ACCOUNT, 'Create User Account');
+
+ // return success and user id
+ return [
+ "success" => true,
+ 'id' => $new_user_id ,
+ 'activation' => $activation_code
+ ];
+ }
+
+
+ /** set_user_privileges
+ *
+ * @param $privileges (array)
+ */
+ public static function set_user_privileges($user, $privileges)
+ {
+ $db = Registry::use('database'); // database connection
+ foreach($privileges as $pri) { // pri = privilege id
+ $db->runQuery(
+ "INSERT INTO user_privilege (user_id, privilege_id) VALUES (:user, :pri)",
+ [ ':user' => $user, ":pri" => $pri ]
+ );
+ }
+ return true;
+ }
+
+
+ /** set_user_role
+ *
+ * user, role are (int) IDs
+ */
+ public static function set_user_role($user, $role)
+ {
+
+ Registry::use('database')->runQuery(
+ "INSERT INTO user_role (user_id, role_id) VALUES (:user, :role)",
+ [ ':user' => $user, ":role" => $role ]
+ );
+
+ return true;
+ }
+
+
+ /** activate
+ *
+ * check if activation code is valid;
+ * if valid, set account active;
+ *
+ * @param $ticket (hex/MD5): activation code;
+ *
+ */
+ public static function activate($ticket)
+ {
+ $user = Registry::use('database')->query(
+ "SELECT * FROM user WHERE activation = :ticket",
+ [ 'ticket' => $ticket ]
+ )->getFirst();
+
+ // if no user with this activation code, return false
+ if ($user === false) return false;
+
+ // remove activation code from user record
+ Registry::use('database')->runQuery(
+ "UPDATE user
+ SET active = 1, `activation` = NULL
+ WHERE activation = :ticket",
+ [ 'ticket' => $ticket ]
+ );
+
+ // update history
+ History::trackUserAccess($user['id'], TRACK_ACCOUNT, 'User Account Activated');
+
+ return true;
+
+ }
+
+
+}
+
+/* example query getUser (super-array)
+--- -- -- - - -
+
+SELECT user.*,
+( -- array (json) of roles granted to user
+ SELECT CONCAT('[', GROUP_CONCAT(role.id), ']')
+ FROM `role`
+ WHERE role.id IN (
+ SELECT user_role.role_id
+ FROM user_role
+ WHERE user_role.user_id = 1
+ )
+) AS Roles_json,
+(
+ SELECT CONCAT(
+ '[',
+ GROUP_CONCAT(privilege.id),
+ ']'
+ )
+ FROM privilege
+ WHERE privilege.id IN (
+ SELECT user_privilege.privilege_id
+ FROM user_privilege
+ WHERE user_privilege.user_id = 1
+ )
+) AS RootPrivileges_json,
+(
+ SELECT CONCAT( -- array of array of sub-privileges
+ '[',
+ GROUP_CONCAT( -- array (json) of subprivileges
+ (
+ SELECT CONCAT(
+ '[',
+ GROUP_CONCAT(included_id),
+ ']'
+ )
+ FROM privilege_includes
+ WHERE privilege_id = privilege.id
+ )
+ ),
+ ']'
+ )
+ FROM privilege
+ WHERE privilege.id IN (
+ SELECT user_privilege.privilege_id
+ FROM user_privilege
+ WHERE user_id = 1
+ )
+) AS SubPrivileges_json
+FROM user
+WHERE id = 1
+--- */ \ No newline at end of file
diff --git a/public/app/models/cms/Course_model.php b/public/app/models/cms/Course_model.php
new file mode 100644
index 0000000..6614993
--- /dev/null
+++ b/public/app/models/cms/Course_model.php
@@ -0,0 +1,314 @@
+<?php
+namespace app\models\cms;
+
+use \Registry;
+
+
+class Course_model {
+
+ /** COURSES
+ * -------------------------------------------------------------------------
+ */
+
+ /** get all categories
+ *
+ * raw table data (simplest SELECT)
+ *
+ */
+ public static function get_categories()
+ {
+ return Registry::use('database')->runQuery(
+ "SELECT * FROM course ORDER BY label",
+ []
+ );
+ }
+
+ /** courses struct
+ *
+ * a super array with almost any info needed about courses
+ *
+ * @return (array) ['tree' => ..., 'breadcrumbs' => ... ]
+ */
+ public static function courses_struct()
+ {
+ $tree = self::category_tree();
+ return [
+ 'tree' => $tree,
+ 'breadcrumbs' => self::breadcrumbs($tree)
+ ];
+ }
+
+
+ /** constuct a category_tree
+ *
+ * returns a tree representation of the categories
+ *
+ * NOTE:
+ * category_tree() is an expensive method;
+ * it calls 2 other methods implementing recursive algorithms
+ * thus it uses many sources to run (particularly RAM).
+ * Caching the result is strogly recommended.
+ *
+ */
+ public static function category_tree()
+ {
+ $categories = self::get_categories(); // get all categories
+
+ $tree = self::to_tree($categories); // format to a tree
+
+ $tree_wParents = self::tree_parents($tree); // add parents section for each tree-node
+
+ return $tree_wParents;
+ }
+
+
+ /** to_tree
+ *
+ * constructs a tree from raw-table data;
+ * this is a private method and uses a recursive algorithm
+ *
+ * @param $dataset (array): flar array of records with id/parent-id pairs
+ * @return $root (array): id of root category
+ *
+ * (**) each node has 2 parts:
+ * .... .. rec : all record attributes/data as passed into $dataset
+ * .... .. childs : array of (children) nodes
+ */
+ private static function to_tree($dataset, $root = 0)
+ {
+ $return = [];
+
+ // loop data ; search for direct children of root
+ foreach($dataset as $key => $rec) {
+
+ $child = $rec['id'];
+ $parent = $rec['parent_id'];
+
+ if ($parent == $root) { // a direct child is found
+
+ unset($dataset[$key]); // remove item (no need to traverse again)
+
+ // Append the child into result array ; parse its children
+ $return[] = [
+ 'rec' => [
+ 'id' => $rec['id'],
+ 'label' => $rec['label'],
+ 'order' => $rec['order'],
+ 'parent' => $rec['parent_id']
+ ],
+ 'childs' => self::to_tree($dataset, $child) // recursively
+ ];
+ }
+ }
+ return empty($return) ? [] : $return;
+ }
+
+
+ /** tree_parents
+ *
+ * adds a section to each tree node with all parents of each node
+ *
+ * @param $tree (array) : nodes array (each node has `rec` and `childs` sections )
+ * @param $parents (array); DO NOT SET IT (takes values automaticaly)
+ * @return array of nodes with an extra node[parents] section
+ *
+ */
+ private static function tree_parents($tree, $parents = [])
+ {
+ $tree_with_parents = [];
+
+ foreach($tree as $key => $node) {
+ // parents to be pushed for node's children
+ $push_parents = $parents; // parents so far
+ $push_parents[] = $node['rec']; // this record will be a new parent
+
+ $tree_with_parents[$key] = [
+ 'rec' => $node['rec'],
+ 'parents' => $parents,
+ 'childs' => ($node['childs'] == [])
+ ? []
+ : self::tree_parents($node['childs'], $push_parents)
+ ];
+ }
+
+ return $tree_with_parents;
+ }
+
+
+ /** all_breadcrumbs
+ * -------------------------------------------------------------------------
+ *
+ * returns an array of all breadcrumbs
+ * where array-key of each record is category[id]
+ *
+ * NOTE:
+ * ---
+ * Course_model::all_breadcrumbs returns an indexed super-array;
+ * each array item includes a banch of information: [
+ * breadcrumb,
+ * rec: [ id , title ],
+ * parents: [ [id, title] , ... ]
+ * childs: [ [id, title] , ... ],
+ * level
+ * ]
+ *
+ * Use Cases:
+ * ---
+ * as a super-array, the output can be used in many cases
+ * for example...
+ * into <select-option> form elements
+ * .. while selecting category for a post
+ * .. or editing a category
+ * or directry referring to category's parents/childs
+ *
+ * Arguments:
+ * ---
+ * @param $tree (array) : category tree (with childs and parents parts)
+ * @param $detimiter (string, optional) : string to split breadcrumb's path-nodes
+ * @param $exception (int, optional) : id of category to exclude (subcategories shall be excluded too)
+ * @param $l (int, not-pass) : depth level of the node; DO NOT SET (takes values automaticaly)
+ * @return array of breadcrumbs
+ * -------------------------------------------------------------------------
+ */
+ static public function breadcrumbs($tree, $delimiter = " / ", $exception = 0, $l = 0)
+ {
+ $all = []; // results array
+
+ foreach($tree as $node) { // loop through all nodes
+
+ if (intval($node['rec']['id']) != $exception) { // if node is not exception
+
+ // construct breadcrumb html of node
+ // --- -- -- - - -
+ $breadcrumb = "";
+ foreach($node['parents'] as $par) { // first: join path titles
+ $breadcrumb .= $par['label'] . $delimiter;
+ }
+ $breadcrumb .= $node['rec']['label']; // last: append title
+
+ // make a new super record
+ // --- -- -- - - -
+ $all[$node['rec']['id']] = [ // set record is as key
+ 'breadcrumb' => $breadcrumb, // add breadcrump to results
+ 'rec' => $node['rec'], // + node info
+ 'parents' => $node['parents'], // + parents array
+ 'childs' => self::first_level_childs($node), // + direct childs
+ 'level' => $l // + level
+ ];
+
+ // recursively traverse children nodes
+ // --- -- -- - - -
+ if (isset($node['childs']) && $node['childs'] != []) {
+ $child_breadcrumbs = self::breadcrumbs(
+ $node['childs'],
+ $delimiter,
+ $exception,
+ $l+1
+ );
+
+ $all = $all + $child_breadcrumbs; // concatenate arrays (keep array-keys)
+ }
+ }
+
+ }
+ return $all;
+
+ }
+
+ /** first_level_childs
+ * --- -- -- - - -
+ * used by all_breadcrumbs()
+ */
+ static private function first_level_childs($node)
+ {
+ $childs = [];
+ if ($node['childs'] == []) {
+ return [];
+ }
+ foreach($node['childs'] as $key => $kid) {
+ $childs[] = [
+ 'id' => $kid['rec']['id'],
+ 'label' => $kid['rec']['label']
+ ];
+ }
+ return $childs;
+ }
+
+
+
+
+ /** LESSONS
+ * -------------------------------------------------------------------------
+ */
+
+
+ /** lessons of course
+ *
+ * @param $id (int) : course_id
+ */
+ public static function lessons_of_course($id)
+ {
+ // TODO: order results in some way
+
+ return Registry::use('database')->runQuery(
+ "SELECT lesson.*, lesson_privilege.privilege_id FROM lesson
+ LEFT JOIN lesson_privilege ON lesson_privilege.lesson_id = lesson.id
+ WHERE lesson.status = 1 AND lesson.course_id = :id
+ ORDER BY lesson_privilege.privilege_id ASC",
+ [':id' => $id]
+ );
+ }
+
+ /** lesson
+ *
+ * @param $id (int) : lesson id
+ */
+ public static function lesson($id)
+ {
+ // TODO: order results in some way
+
+ return Registry::use('database')->query(
+ "SELECT lesson.*, lesson_privilege.privilege_id FROM lesson
+ LEFT JOIN lesson_privilege ON lesson_privilege.lesson_id = lesson.id
+ WHERE lesson.status = 1 AND lesson.id = :id",
+ [':id' => $id]
+ )->getFirst();
+ }
+
+
+
+ /** files
+ * return all files
+ * @param void
+ * @return array
+ */
+ public static function files()
+ {
+ return Registry::use('database')->runQuery("SELECT * from media", []);
+ }
+
+
+ /** pages
+ *
+ * return all pages as array;
+ * array key of each item shall be the page-id
+ *
+ * @param void
+ * @return array
+ */
+ public static function pages()
+ {
+ $result = [];
+ $pages = Registry::use('database')->runQuery("SELECT * FROM page", []);
+ foreach($pages as $key => $page) {
+ $result[ $page['id'] ] = [
+ 'title' => $page['title'],
+ 'body' => $page['body'],
+ 'status' => $page['status']
+ ];
+ }
+ // print_r($result); die();
+ return $result;
+ }
+
+}
diff --git a/public/app/models/cms/Media_model.php b/public/app/models/cms/Media_model.php
new file mode 100644
index 0000000..6a4a681
--- /dev/null
+++ b/public/app/models/cms/Media_model.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace app\models\cms;
+
+use \Registry;
+
+
+class Media_model
+{
+ public static function serve($type, $id, $ticket)
+ {
+
+ // get media data
+ // [ type =>, rights =>, path => ]
+
+ // validate ticket
+ // ValidateAccess::for($ticket)
+
+ // serve
+ // header("Content-type: type");
+ // passthru('cat $media_path');
+
+
+ return Registry::use('database')->query(
+ "SELECT * FROM pages
+ WHERE FullFriendlyUrl = :url AND IsActive = 1",
+ [ ':url' => $url ]
+ )->getFirst();
+ }
+
+}
+
+
+
+// check:
+// https://stackoverflow.com/questions/1353850/serve-image-with-php-script-vs-direct-loading-an-image \ No newline at end of file
diff --git a/public/app/models/cms/Page_model.php b/public/app/models/cms/Page_model.php
new file mode 100644
index 0000000..fda5fb1
--- /dev/null
+++ b/public/app/models/cms/Page_model.php
@@ -0,0 +1,18 @@
+<?php
+
+namespace app\models\cms;
+
+use \Registry;
+
+class Page_model
+{
+ public static function fromUrl($url)
+ {
+ return Registry::use('database')->query(
+ "SELECT * FROM pages
+ WHERE FullFriendlyUrl = :url AND IsActive = 1",
+ [ ':url' => $url ]
+ )->getFirst();
+ }
+
+} \ No newline at end of file
diff --git a/public/app/models/market/Market_repository.php b/public/app/models/market/Market_repository.php
new file mode 100644
index 0000000..1338426
--- /dev/null
+++ b/public/app/models/market/Market_repository.php
@@ -0,0 +1,235 @@
+<?php
+
+namespace app\models\market;
+
+use \Repository;
+
+/** Market_repository
+ * (thematic-entity repository)
+ * ---
+ * SQL entity templates for Market
+ *
+ * Every '_base' is a repository of common 'prepare' SQL-queries;
+ * They usually include holders for attaching/binding data safely;
+ * These queries are expensive in syntax but fast on run time and
+ * are used to collect multi-joined amounts ot data.
+ *
+ * Organize yous repositories in logical/thematic groups (partitioning).
+ * This way your repository is managable and uses very few sources.
+ *
+ * This class acts like a namespaced repository for geting your code readable
+ * Avoid pushing very simple queries into repository classes. A record like...
+ * 'CARS_by:Age' => "SELECT * FROM cars WHERE age = :Age"
+ * ... won't make your code sexier nor easier to read; it will just increase
+ * your label's entropy and make trickier to pick your next sensable label;
+ *
+ * repository classes include two methods (inherited by the abstract, so
+ * you do not need to implement something more than just construct
+ * the $repository array)
+ * ** pull: for pulling a query by a friendly name
+ * ** echo: optional method used by some devolopment tools
+ *
+ * TODO: development tool using echo is still in progress
+ */
+class Market_repository extends Repository
+{
+ //// use Repository; // repository trait includes
+ //// // a method to pull an entity of the gathering
+ //// // and method to (TODO:) ...
+
+ /** repository is an array of Prepare SWL queries;
+ * each query is attached to the array via a friendly label;
+ *
+ * NOTE: (PROPOSAL)
+ * ---
+ * about the label:
+ * 1. keep names short descriptive but not too long
+ * 2. main entity/entities shall be UPPERCASED
+ * 3. determinands/adjectives shall be Cappitalized
+ * 4. words are connected with underscore (_)
+ * 5. if SQL inludes bind-holderd, label shall be suffixed with '_by:'
+ * followed by holder names connected with undercores (case-sensitively)
+ *
+ * about the data-bind holders (if present):
+ * 1. all holders are prefixed with ':'
+ * 2. holder name shall begin with a letter and contain one word.
+ *
+ * example:
+ * 'Active_EMPLOYEES_by:Age_CityID' => "SELECT Em.*, c.*
+ * FROM employee Em LEFT JOIN city c ON c.id = Em.city_id
+ * WHERE c.id = :CityID AND Em.age = :Age"
+ *
+ * It is recommended to leave a descriptive comment befor each declaration
+ * and you may also include sql-comments inside the query.
+ */
+ public static $repository = [
+
+ // PRODUCTS by: storeID, ProductUrl
+ // picks all product properties
+ // + default image + price for certain store
+ 'Active_PRODUCT_by:storeID_ProductUrl' =>
+ "SELECT p.*,
+ pc.ID AS ProductCategory,
+ pc.Hierarchy,
+ pc.FullFriendlyUrl AS `Path`,
+ prices.ProductPrice_OriginalPrice AS Price,
+ prices.ProductPrice_DiscountedPrice AS DiscountPrice,
+ prices.UnitMeasurePrice_OriginalPrice AS PerUnitPrice,
+ prices.UnitMeasurePrice_DiscountedPrice AS PerUnitDiscountPrice,
+ b.Title BrandName
+ FROM products p
+ LEFT JOIN products_to_product_categories p2pc ON p.ID = p2pc.SimpleProductID
+ LEFT JOIN product_categories pc ON p2pc.ProductCategoryID = pc.ID
+ LEFT JOIN prices ON p.SKU = prices.SKU
+ LEFT JOIN products_to_images p2i ON p2i.SimpleProductID = p.ID
+ LEFT JOIN assets ON assets.ID = p2i.ImageID
+ LEFT JOIN brands b ON b.ID = p.BrandID
+ WHERE pc.IsActive = 1 AND pc.IsCurrentlyActive = 1
+ AND p2i.Order = 1
+ AND p.IsActive = 1
+ AND p.Published = 1
+ AND prices.PriceListID = :storeID
+ AND p.FriendlyUrl = :ProductUrl"
+ ,
+
+ // PRODUCTS by: storeID, ProductID
+ // picks all product properties
+ // + default image + price for certain store
+ 'Active_PRODUCT_by:storeID_ProductID' =>
+ "SELECT p.*,
+ pc.ID AS ProductCategory,
+ pc.Hierarchy,
+ pc.FullFriendlyUrl AS `Path`,
+ prices.ProductPrice_OriginalPrice AS Price,
+ prices.ProductPrice_DiscountedPrice AS DiscountPrice,
+ prices.UnitMeasurePrice_OriginalPrice AS PerUnitPrice,
+ prices.UnitMeasurePrice_DiscountedPrice AS PerUnitDiscountPrice,
+ b.Title BrandName
+ FROM products p
+ LEFT JOIN products_to_product_categories p2pc ON p.ID = p2pc.SimpleProductID
+ LEFT JOIN product_categories pc ON p2pc.ProductCategoryID = pc.ID
+ LEFT JOIN prices ON p.SKU = prices.SKU
+ LEFT JOIN products_to_images p2i ON p2i.SimpleProductID = p.ID
+ LEFT JOIN assets ON assets.ID = p2i.ImageID
+ LEFT JOIN brands b ON b.ID = p.BrandID
+ WHERE pc.IsActive = 1 AND pc.IsCurrentlyActive = 1
+ AND p2i.Order = 1
+ AND p.IsActive = 1
+ AND p.Published = 1
+ AND prices.PriceListID = :storeID
+ AND p.ID = :ProductID"
+ ,
+
+ // CATEGORY-PRODUCTS by: storeID, likeHierarchy
+ // traverses all products from category and subcategories
+ // picke price and default image
+ 'CATEGORY-PRODUCTS_by:storeID_likeHierarchy' =>
+ "SELECT p.*,
+ assets.Url AS ImageUrl,
+ pc.FullFriendlyUrl AS `Path`,
+ prices.ProductPrice_OriginalPrice AS Price,
+ prices.ProductPrice_DiscountedPrice AS DiscountPrice,
+ prices.UnitMeasurePrice_OriginalPrice AS PerUnitPrice,
+ prices.UnitMeasurePrice_DiscountedPrice AS PerUnitDiscountPrice
+ FROM products p
+ LEFT JOIN prices ON p.SKU = prices.SKU
+ LEFT JOIN products_to_product_categories p2pc ON p2pc.SimpleProductID = p.ID
+ LEFT JOIN product_categories pc ON pc.ID = p2pc.ProductCategoryID
+ LEFT JOIN products_to_images p2i ON p2i.SimpleProductID = p.ID
+ LEFT JOIN assets ON assets.ID = p2i.ImageID
+ WHERE p2pc.ProductCategoryID IN (
+ SELECT `ID` FROM product_categories
+ WHERE Hierarchy LIKE :likeHierarchy
+ )
+ AND p2i.Order = 1
+ AND p.IsActive = 1
+ AND p.Published = 1
+ AND prices.PriceListID = :storeID"
+ ,
+
+ // Count Products
+ // of all last-lever Product categories
+ // by StoreID
+ // ---
+ // used on creating product_categories tree
+ // ProductCategories_model::tree()
+ 'Count_Last_Level_PRODUCTS_by:storeID' =>
+ "SELECT pc.ID, COUNT(p.ID) as `Counter`
+ FROM product_categories pc
+ LEFT JOIN products_to_product_categories p2pc ON pc.ID = p2pc.ProductCategoryID
+ LEFT JOIN products p ON p2pc.SimpleProductID = p.ID
+ LEFT JOIN prices ON p.SKU = prices.SKU
+ LEFT JOIN products_to_images p2i ON p2i.SimpleProductID = p.ID
+ LEFT JOIN assets ON assets.ID = p2i.ImageID
+ WHERE pc.IsActive = 1 AND pc.IsCurrentlyActive = 1
+ AND p2i.Order = 1
+ AND pc.Level = 2
+ AND p.IsActive = 1
+ AND p.Published = 1
+ AND prices.PriceListID = :storeID
+ GROUP BY pc.ID"
+ ,
+
+ // PRODUCT by: ProductID
+ // Join images array (json string)
+ // Join prices for all stores (json string)
+ 'Active_PRODUCT_complete_by:ProductID' =>
+ "SELECT p.*, pc.ID AS ProductCategory,
+ pc.Hierarchy, pc.FullFriendlyUrl AS `Path`,
+ ( -- get all store prices as Json
+ SELECT JSON_ARRAYAGG(JSON_OBJECT(
+ 'Store', prices.PriceListID,
+ 'Price', prices.ProductPrice_OriginalPrice,
+ 'DiscountPrice', prices.ProductPrice_DiscountedPrice,
+ 'PerUnitPrice', prices.UnitMeasurePrice_OriginalPrice,
+ 'PerUnitDiscountPrice', prices.UnitMeasurePrice_DiscountedPrice
+ ))
+ FROM prices
+ WHERE prices.SKU = p.SKU
+ ) as pricesJson,
+ ( -- get all images as Json
+ SELECT JSON_ARRAYAGG(JSON_OBJECT(
+ 'Order', p2i.Order,
+ 'Url', assets.Url,
+ 'Title', assets.Title,
+ 'Description', assets.Description
+ ))
+ FROM products_to_images p2i
+ LEFT JOIN assets ON assets.ID = p2i.ImageID
+ WHERE p2i.SimpleProductID = p.ID
+ ) AS imagesJson,
+ b.Title BrandName
+ FROM products p
+ LEFT JOIN products_to_product_categories p2pc ON p.ID = p2pc.SimpleProductID
+ LEFT JOIN product_categories pc ON p2pc.ProductCategoryID = pc.ID
+ LEFT JOIN products_to_images p2i ON p2i.SimpleProductID = p.ID
+ LEFT JOIN assets ON assets.ID = p2i.ImageID
+ LEFT JOIN brands b ON b.ID = p.BrandID
+ WHERE pc.IsActive = 1 AND pc.IsCurrentlyActive = 1
+ AND p2i.Order = 1
+ AND p.IsActive = 1
+ AND p.Published = 1
+ AND p.ID = :ProductID"
+ ,
+
+ // Product images by: ProductID
+ // (sorted by 'order' field)
+ 'PRODUCT_IMAGES_by:productID' =>
+ "SELECT a.Url, a.Title, a.Description
+ FROM products p
+ LEFT JOIN products_to_images p2i ON p2i.SimpleProductID = p.ID
+ LEFT JOIN assets a ON a.ID = p2i.ImageID
+ WHERE p.IsActive = 1
+ AND p.Published = 1
+ AND p.ID = :productID
+ ORDER BY p2i.Order"
+
+ ];
+
+ /* no need to impement anything else;
+ * you can overide if needed the default methods:
+ * + public static function pull($entity){ }
+ * + public static function echo($content =false){ }
+ */
+
+}
diff --git a/public/app/models/market/Payment_template.txt b/public/app/models/market/Payment_template.txt
new file mode 100644
index 0000000..4a66a1a
--- /dev/null
+++ b/public/app/models/market/Payment_template.txt
@@ -0,0 +1,98 @@
+class WC_Piraeusbank_Gateway extends WC_Payment_Gateway {
+
+ public function __construct()
+
+ function pb_get_pages($title = false, $indent = true)
+
+ function pb_get_installments($title = false, $indent = true)
+
+ function generate_piraeusbank_form($order_id)
+
+ function process_payment($order_id)
+
+ function receipt_page($order)
+
+ function check_piraeusbank_response()
+
+ function piraeusbank_message()
+
+ function woocommerce_add_piraeusbank_gateway($methods)
+
+ function piraeusbank_plugin_action_links($links, $file)
+
+}
+
+
+
+class WC_NBG_Gateway extends WC_Payment_Gateway {
+
+ public function __construct()
+
+ public function admin_options()
+
+ function init_form_fields()
+
+ function nbg_get_pages($title = false, $indent = true)
+
+ function nbg_get_installments($title = false, $indent = true)
+
+ function generate_nbg_form($order_id)
+
+ function process_payment($order_id)
+
+ function receipt_page($order) {
+
+ function check_nbg_response()
+
+ function nbg_message()
+
+ function woocommerce_add_nbg_gateway($methods)
+
+ function nbg_plugin_action_links($links, $file)
+
+}
+
+
+
+global $wc;
+
+ $this->id = 'nbg_gateway';
+ $this->icon = apply_filters('nbg_icon', plugins_url('assets/nbg.png', __FILE__));
+ $this->has_fields = false;
+ $this->notify_url = WC()->api_request_url('WC_NBG_Gateway');
+ $this->method_description = __('National Bank Greece Payment Gateway allows you to accept payment through various channels such as Maestro, Mastercard and Visa cards On your Woocommerce Powered Site.', 'woocommerce-nbg-payment-gateway');
+ $this->redirect_page_id = $this->get_option('redirect_page_id');
+ $this->method_title = 'National Bank of Greece Gateway';
+
+ // Load the form fields.
+ $this->init_form_fields();
+
+ //dhmioyrgia vashs
+
+ global $wpdb;
+
+ if ($wpdb->get_var("SHOW TABLES LIKE '" . $wpdb->prefix . "nbg_transactions'") === $wpdb->prefix . 'nbg_transactions') {
+ // The database table exist
+ } else {
+ // Table does not exist
+ $query = 'CREATE TABLE IF NOT EXISTS ' . $wpdb->prefix . 'nbg_transactions (id int(11) unsigned NOT NULL AUTO_INCREMENT,merchantreference varchar(30) not null, reference varchar(100) not null, orderid varchar(100) not null , timestamp datetime default null, PRIMARY KEY (id))';
+ $wpdb->query($query);
+ }
+
+
+ // Load the settings.
+ $this->init_settings();
+
+
+ // Define user set variables
+ $this->title = $this->get_option('title');
+ $this->description = $this->get_option('description');
+ $this->nbg_Username = $this->get_option('nbg_Username');
+ $this->nbg_Password = $this->get_option('nbg_Password');
+
+ $this->nbg_description= $this->get_option('nbg_description');
+ $this->mode = $this->get_option('mode');
+ $this->nbg_installments= $this->get_option('nbg_installments');
+ //Actions
+ add_action('woocommerce_receipt_nbg_gateway', array($this, 'receipt_page'));
+ add_action('woocommerce_update_options_payment_gateways_' . $this->id, array($this
diff --git a/public/app/models/market/ProductCategories_model.php b/public/app/models/market/ProductCategories_model.php
new file mode 100644
index 0000000..d8c274e
--- /dev/null
+++ b/public/app/models/market/ProductCategories_model.php
@@ -0,0 +1,199 @@
+<?php
+
+namespace app\models\market;
+
+use \Registry;
+use app\models\market\Market_repository;
+
+
+/** ProductCategories_model
+ * ---
+ * undertakes to collect any product_categories data
+ * from the database and prepare needed data structures.
+ *
+ */
+class ProductCategories_model
+{
+
+ /** Get (product_) category from (full-friendly_) URL
+ * --- (self explanatory)
+ * @param $url (string): Full-Friendly-URL
+ * @return $category (array); also includes all category products
+ */
+ public static function categoryFromUrl($url)
+ {
+ $db = Registry::use('database');
+ $category = $db->query( "SELECT * FROM product_categories
+ WHERE FullFriendlyUrl = :url AND IsActive = 1",
+ [ ':url' => $url ]
+ )->getFirst();
+
+ if ($category === false) { return false; } // no category? -> false
+
+ // GET PRODUCTS of category
+ $category['products'] = self::categoryProductsByHierarchy($category['Hierarchy']);
+ return $category;
+ }
+
+
+ /** Get (product_) category from ID
+ * --- (self explanatory)
+ * @param $id (int): Catgory ID (usualy from products)
+ * @return $category (array); also includes all category products
+ */
+ public static function categoryFromID($id)
+ {
+ return Registry::use('database')->query( "SELECT * FROM product_categories
+ WHERE `ID` = :url AND IsActive = 1",
+ [':id' => $id]
+ )->getFirst();
+
+ }
+
+
+ /** categoryProductsByHierarchy
+ * ---
+ * This method returns the products belonging to a certain
+ * category (and all it's the children/sub-categories);
+ * It uses category.Hierarchy in a WHERE LIKE condition to
+ * make it fast.
+ *
+ * The method is category.Level agnostic
+ *
+ * @param $category (array)
+ * @return $products (array)
+ */
+ public static function categoryProductsByHierarchy($hierarchy, $store = 904)
+ {
+ // GET PRODUCTS of category
+ // NOTE:
+ // category.Hierarchy is used
+
+ return Registry::use('database')->runQuery(
+ Market_repository::pull('CATEGORY-PRODUCTS_by:storeID_likeHierarchy'),
+ [
+ ':likeHierarchy' => $hierarchy.'%',
+ ':storeID' => 904
+ ]
+ );
+ }
+
+
+ # depricated:
+ # now Proxy has the responsibility to cache whatever
+ # ---
+ # /** GET tree of product_categories
+ # * ---
+ # * get from cache or cache it after creation
+ # */
+ # public static function tree($store = 904)
+ # {
+ # $cacheKey = get_called_class() . $store .'/tree';
+ #
+ # // IF category CACHED
+ # $cache = Registry::use('cache');
+ # $tree = $cache->get($cacheKey);
+ # if ($tree !== false) {
+ # return $tree;
+ # }
+ #
+ # // (not cached) Create and Cache it
+ # $tree = self::createCategoriesTree();
+ # $cache->set($cacheKey, $tree, CACHE_ROOT_TTL);
+ #
+ # return $tree;
+ # }
+
+
+ /** CREATE product_categories tree
+ * NOTE: includes active categories only;
+ *
+ * Algorith uses 'Hierarchy' for category path-positioning,
+ * implementing a linear conctruction of the requested tree;
+ * (faster and less source-consuming than a recursive algo)
+ */
+ public static function tree($store = 904)
+ {
+ $tree = []; // variable to hold results
+
+ $db = Registry::use('database');
+
+ // get raw categories data ---------------------------------------------
+
+ $raw = $db->runQuery( "SELECT `ID`, Title, `Level`, Hierarchy,
+ ParentID, `Order`, FullFriendlyUrl
+ FROM product_categories pc
+ WHERE IsActive = 1 AND IsCurrentlyActive = 1
+ ORDER BY pc.Level asc, pc.Order asc, pc.Hierarchy asc",
+ []
+ );
+
+ // count products per 3rd level category -------------------------------
+
+ $countProducts = $db->runQuery(
+ Market_repository::pull('Count_Last_Level_PRODUCTS_by:storeID'),
+ [ ':storeID' => $store ]
+ );
+ // map categoryID -> Counter
+ $mapCounter = [];
+ foreach($countProducts as $rec) $mapCounter[$rec['ID']] = $rec['Counter'];
+
+ // parse raw data to hierarchical tree (2 pass) ------------------------
+
+ // 1st pass: parse raw data, construct and fill the $tree array ........
+ foreach($raw as $rec) {
+
+ // create category path from Hierarchy (format: .10.10100. )
+ // so remove 1st and last dots (.) from Hierarchy string
+ // then explode via dot-character (.)
+ $categoryPath = explode('.', substr($rec['Hierarchy'], 1, -1));
+
+ switch ($rec['Level']) {
+ case 0:
+ $tree[$categoryPath[0]] = [
+ 'info' => $rec,
+ 'childs' => []
+ ]; break;
+
+ case 1:
+ $tree[$categoryPath[0]]['childs'][$categoryPath[1]] = [
+ 'info' => $rec,
+ 'childs' => []
+ ]; break;
+
+ case 2:
+ $tree[$categoryPath[0]]['childs'][$categoryPath[1]]['childs'][$categoryPath[2]] = [
+ 'info' => $rec,
+ 'count' => isset($mapCounter[$categoryPath[2]]) ? $mapCounter[$categoryPath[2]] : 0
+ ]; break;
+
+ default:
+ // nothing...
+ }
+
+ }
+
+ // 2nd pass: remove categories with no products ........................
+ foreach($tree as $id0 => $rec0) {
+ $count0 = 0;
+
+ foreach($rec0['childs'] as $id1 => $rec1) {
+ $count1 = 0;
+
+ foreach($rec1['childs'] as $id2 => $rec2) {
+ if ($rec2['count']>0) $count1++;
+ else unset($tree[$id0]['childs'][$id1]['childs'][$id2]);
+ }
+
+ if ($count1 > 0) $count0++;
+ else unset($tree[$id0]['childs'][$id1]);
+ }
+
+ if (!$count0) unset($tree[$id0]);
+ }
+
+ return $tree;
+ }
+
+
+} \ No newline at end of file
diff --git a/public/app/models/market/Product_model.php b/public/app/models/market/Product_model.php
new file mode 100644
index 0000000..aa554a1
--- /dev/null
+++ b/public/app/models/market/Product_model.php
@@ -0,0 +1,49 @@
+<?php
+
+namespace app\models\market;
+
+use \Registry;
+use app\models\market\Market_repository as Market;
+
+class Product_model
+{
+ public static function fromUrl($url, $store = 904)
+ {
+ $product = Registry::use('database')->query(
+ Market::pull('Active_PRODUCT_by:storeID_ProductUrl'),
+ [
+ ':storeID' => $store,
+ ':ProductUrl' => $url
+ ]
+ )->getFirst();
+
+ // if no product, return false
+ if ($product === false) return false;
+
+ // inject product's images
+ $product['Images'] = self::getProductImages($product['ID']);
+
+ return $product;
+ }
+
+
+ public static function getProductImages($id)
+ {
+ ## return Registry::use('database')->runQuery(
+ ## "SELECT a.Url, a.Title, a.Description
+ ## FROM products p
+ ## LEFT JOIN products_to_images p2i ON p2i.SimpleProductID = p.ID
+ ## LEFT JOIN assets a ON a.ID = p2i.ImageID
+ ## WHERE p.IsActive = 1 AND p.Published = 1
+ ## AND p.ID = :id
+ ## ORDER BY p2i.Order",
+ ## [ ':id' => $id ]
+ ## );
+
+ return Registry::use('database')->runQuery(
+ Market::pull('PRODUCT_IMAGES_by:productID'),
+ ['productID' => $id ]
+ );
+ }
+
+} \ No newline at end of file
diff --git a/public/app/models/todo.md b/public/app/models/todo.md
new file mode 100644
index 0000000..defd21e
--- /dev/null
+++ b/public/app/models/todo.md
@@ -0,0 +1,132 @@
+
+
+Privilege
+---
+
+Privilege::list_of_privileges( privilege_id )
+
+Privilege::privileges_tree()
+
+
+
+
+
+User
+---
+
+User::get_privileges
+
+
+User::has_privilege( privilege_id )
+
+
+User::reset_password()
+ -> create otp
+ -> send email
+
+
+User::set_privilege( privilege_id )
+
+
+User::track_action()
+ -> user_id
+ -> action : Contoller::method(args)
+ -> timestamp
+
+
+
+Lesson:
+---
+
+Lesson::create_lesson( POST )
+
+Lesson::get_lesson( lesson_id )
+
+
+
+Course
+---
+
+Course::create_course( POST )
+
+Course::courses_tree()
+
+
+
+Upgrades (??)
+---
+
+
+A1
+
+A2 : includes A1
+
+A3 : includes A1, A2
+
+A4 : includes A1, A2, A3
+
+
+A1 100
+A2 200
+A3 350
+A4 500
+
+B1 100
+B2 200
+B3 400
+B4 800
+
+
+:1 -> 0%
+:2 -> 15%
+:3 -> 20%
+
+
+
+
+A1 -> A2
+A1 -> A3
+A1 -> A4
+
+A2 -> A3
+A2 -> A4
+
+A3 -> A4
+
+A1 < A2 < A3 < A4
+
+
+
+B1 -> B2
+B1 -> B3
+B1 -> B4
+
+B2 -> B3
+B2 -> B4
+
+B3 -> B4
+
+
+A1+B1
+
+A2+B2
+
+A3+B3
+
+
+A1+B1 -> A2+B2
+A1+B1 -> A3+B3
+A1+B1 -> A4+B4
+
+A2+B2 -> A3+B3
+A2+B2 -> A4+B4
+
+A3+B3 -> A4+B4
+
+
+
+UPGRADE PRICE = upper_priv_price - own_priv_price
+
+buy 1 item : -0%
+buy 2 items : -10%
+buy 3 items : -15%