summaryrefslogtreecommitdiffstats
path: root/app/class
diff options
context:
space:
mode:
authorMalf Furious <m@lfurio.us>2015-12-18 23:18:33 -0500
committerMalf Furious <m@lfurio.us>2015-12-18 23:18:33 -0500
commit9068e6916ad68194fce2518ab5841af1c8949f3d (patch)
tree2f91e9e3be00c1492cdf4ce88d8e77a909c5c287 /app/class
parent2ebdbaa48f10d6a6f5a1b78f4ef2c5433e50c8cf (diff)
parentb21251ef971d262dc414869fed83f52d0098bfe6 (diff)
downloadscrott-9068e6916ad68194fce2518ab5841af1c8949f3d.tar.gz
scrott-9068e6916ad68194fce2518ab5841af1c8949f3d.zip
Merge branch 'framework' into dev
Diffstat (limited to 'app/class')
-rw-r--r--app/class/controller.class.php37
-rw-r--r--app/class/database.iface.php13
-rw-r--r--app/class/form.class.php186
-rw-r--r--app/class/framework.class.php79
-rw-r--r--app/class/model.class.php77
-rw-r--r--app/class/mysql.class.php63
-rw-r--r--app/class/object.class.php227
7 files changed, 682 insertions, 0 deletions
diff --git a/app/class/controller.class.php b/app/class/controller.class.php
new file mode 100644
index 0000000..fabd7e7
--- /dev/null
+++ b/app/class/controller.class.php
@@ -0,0 +1,37 @@
+<?php
+
+require_once "class/framework.class.php";
+
+/*
+ * Abstract controller -- Contains app security constraints and provides access to
+ * framework internals from concrete controllers
+ */
+abstract class Controller extends Framework
+{
+ /*
+ * Abstract function for concrete controller to handle the page request
+ */
+ abstract function handle($argv);
+
+ /*
+ * Security check
+ * Assert that the current connection to this server is secure. Redirects if not.
+ */
+ function sec_require_https()
+ {
+ if (!isset($_SERVER['HTTPS']))
+ $this->redirectTo("https://" . $_SERVER['SERVER_NAME'] . $this->ap());
+ }
+
+ /*
+ * Security check
+ * Assert that the current connection to this server is NOT secure. Redirects if not.
+ */
+ function sec_forbid_https()
+ {
+ if (isset($_SERVER['HTTPS']))
+ $this->redirectTo("http://" . $_SERVER['SERVER_NAME'] . $this->ap());
+ }
+}
+
+?>
diff --git a/app/class/database.iface.php b/app/class/database.iface.php
new file mode 100644
index 0000000..dcd64ba
--- /dev/null
+++ b/app/class/database.iface.php
@@ -0,0 +1,13 @@
+<?php
+
+/*
+ * Generic interface for the various database drivers Scrott may implement support for
+ */
+interface Database
+{
+ function close();
+ function query($query);
+ function esc($string);
+}
+
+?>
diff --git a/app/class/form.class.php b/app/class/form.class.php
new file mode 100644
index 0000000..808de27
--- /dev/null
+++ b/app/class/form.class.php
@@ -0,0 +1,186 @@
+<?php
+
+/*
+ * Model web-forms and simplify the process of accepting, validating, and sanitizing input
+ */
+class Form
+{
+ /*
+ * Constructor
+ */
+ function __construct()
+ {
+ $this->textFields = array();
+ $this->numbFields = array();
+ $this->enumFields = array();
+
+ $this->errorlist = array();
+ }
+
+ /*
+ * Log an error
+ */
+ function logError($str)
+ {
+ $this->errorlist[] = $str;
+ }
+
+ /*
+ * Add new text field to the form
+ */
+ function field_text($name, $deflt = null, $req = true)
+ {
+ if ($req !== true)
+ $req = false;
+
+ $this->textFields[] = array(
+ 'name' => $name,
+ 'deflt' => $deflt,
+ 'req' => $req
+ );
+ }
+
+ /*
+ * Add new numeric field to the form
+ */
+ function field_numeric($name, $min = null, $max = null, $deflt = null, $integer = true, $req = true)
+ {
+ if ($req !== true)
+ $req = false;
+
+ if ($integer !== true)
+ $integer = false;
+
+ $this->numbFields[] = array(
+ 'name' => $name,
+ 'min' => $min,
+ 'max' => $max,
+ 'deflt' => $deflt,
+ 'int' => $integer,
+ 'req' => $req
+ );
+ }
+
+ /*
+ * Add new enumeration field to the form
+ */
+ function field_enum($name, $values, $deflt = null, $req = true)
+ {
+ if ($req !== true)
+ $req = false;
+
+ $this->enumFields[] = array(
+ 'name' => $name,
+ 'vals' => $values,
+ 'deflt' => $deflt,
+ 'req' => $req
+ );
+ }
+
+ /*
+ * Add new boolean field to the form
+ */
+ function field_bool($name)
+ {
+ $this->field_enum($name, array("true", "false"), "false");
+ }
+
+ /*
+ * Populate the form with input data from web page
+ */
+ function populate($input)
+ {
+ /* detect duplicate names */
+ $names = array();
+ foreach ($this->textFields as $fld)
+ $names[] = $fld['name'];
+ foreach ($this->numbFields as $fld)
+ $names[] = $fld['name'];
+ foreach ($this->enumFields as $fld)
+ $names[] = $fld['name'];
+
+ if (count(array_unique($names)) != count($names))
+ {
+ $this->logError("Internal error: Duplicate field names defined in form");
+ return false;
+ }
+
+ /* init text fields */
+ foreach ($this->textFields as $fld)
+ {
+ if (isset($input[$fld['name']]) && $input[$fld['name']] != "")
+ $this->$fld['name'] = htmlEntities($input[$fld['name']], ENT_QUOTES);
+
+ else if (!is_null($fld['deflt']))
+ $this->$fld['name'] = $fld['deflt'];
+
+ else if ($fld['req'])
+ $this->logError($fld['name'] . " is required");
+ }
+
+ /* init numeric fields */
+ foreach ($this->numbFields as $fld)
+ {
+ if (isset($input[$fld['name']]) && $input[$fld['name']] != "")
+ {
+ if (!is_numeric($input[$fld['name']]))
+ {
+ $this->logError($fld['name'] . " must be numeric");
+ continue;
+ }
+
+ if ($fld['int'] && (floor($input[$fld['name']]) != $input[$fld['name']]))
+ {
+ $this->logError($fld['name'] . " must be an integer");
+ continue;
+ }
+
+ if (!is_null($fld['min']) && ($input[$fld['name']] < $fld['min']))
+ {
+ $this->logError($fld['name'] . " must be no less than " . $fld['min']);
+ continue;
+ }
+
+ if (!is_null($fld['max']) && ($input[$fld['name']] > $fld['max']))
+ {
+ $this->logError($fld['name'] . " must be no more than " . $fld['max']);
+ continue;
+ }
+
+ $this->$fld['name'] = $input[$fld['name']];
+ }
+
+ else if (!is_null($fld['deflt']))
+ $this->$fld['name'] = $fld['deflt'];
+
+ else if ($fld['req'])
+ $this->logError($fld['name'] . " is required");
+ }
+
+ /* init enum fields */
+ foreach ($this->enumFields as $fld)
+ {
+ if (isset($input[$fld['name']]) && $input[$fld['name']] != "")
+ {
+ if (array_search($input[$fld['name']], $fld['vals']) === false)
+ {
+ $this->logError($fld['name'] . " is not an appropriate value");
+ continue;
+ }
+
+ $this->$fld['name'] = $input[$fld['name']];
+ }
+
+ else if (!is_null($fld['deflt']))
+ $this->$fld['name'] = $fld['deflt'];
+
+ else if ($fld['req'])
+ $this->logError($fld['name'] . " is required");
+ }
+
+ /* return */
+ return count($this->errorlist) == 0;
+ }
+}
+
+?>
diff --git a/app/class/framework.class.php b/app/class/framework.class.php
new file mode 100644
index 0000000..eea6c25
--- /dev/null
+++ b/app/class/framework.class.php
@@ -0,0 +1,79 @@
+<?php
+
+/* Include the Scrott system-level configuration file if it exists */
+is_file("scrott.conf.php") &&
+ require_once "scrott.conf.php";
+
+require_once "class/mysql.class.php";
+
+/*
+ * Global functions / operations and access to contextual or session-based information
+ */
+abstract class Framework
+{
+ static $dbobj = null;
+
+ /*
+ * Check for the existence of Scrott's system-level config
+ */
+ function scrottConfExists()
+ {
+ global $_SCROTT;
+ return isset($_SCROTT['conf']);
+ }
+
+ /*
+ * Get the absolute path on this server for the root of this app
+ */
+ function ar()
+ {
+ return substr($_SERVER['PHP_SELF'], 0, -10); // 10 = length of "/index.php"
+ }
+
+ /*
+ * Get the absolute path to the current page
+ */
+ function ap()
+ {
+ return $this->ar() . $_REQUEST['path'];
+ }
+
+ /*
+ * Redirect to the given URL and die
+ */
+ function redirectTo($url)
+ {
+ header("Location: " . $url);
+ exit;
+ }
+
+ /*
+ * Get or create the app's database connection object (this is a singleton object and dependent on system-level config)
+ */
+ function getDbConnection()
+ {
+ global $_SCROTT;
+
+ if (self::$dbobj != null)
+ return self::$dbobj;
+
+ switch ($_SCROTT['dbEngine'])
+ {
+ case "mysql":
+ $host = $_SCROTT['dbAddress'];
+ $username = $_SCROTT['dbUser'];
+ $password = $_SCROTT['dbPass'];
+ $dbName = $_SCROTT['dbName'];
+ self::$dbobj = new Mysql($host, $username, $password, $dbName);
+ break;
+
+ default:
+ throw new Exception("Problem with Scrott Configuration. Invalid database engine specified.");
+ break;
+ }
+
+ return self::$dbobj;
+ }
+}
+
+?>
diff --git a/app/class/model.class.php b/app/class/model.class.php
new file mode 100644
index 0000000..85bcf54
--- /dev/null
+++ b/app/class/model.class.php
@@ -0,0 +1,77 @@
+<?php
+
+require_once "class/framework.class.php";
+
+/*
+ * Abstract model class - defines logic common to all app MVC models
+ */
+abstract class Model extends Framework
+{
+ /*
+ * Constructor
+ */
+ function __construct()
+ {
+ $this->errorlist = array();
+ $this->warninglist = array();
+ $this->noticelist = array();
+ }
+
+ /*
+ * Check for error
+ */
+ function isError()
+ {
+ return count($this->errorlist) > 0;
+ }
+
+ /*
+ * Check for warning
+ */
+ function isWarning()
+ {
+ return count($this->warninglist) > 0;
+ }
+
+ /*
+ * Check for notice
+ */
+ function isNotice()
+ {
+ return count($this->noticelist) > 0;
+ }
+
+ /*
+ * Log an error
+ */
+ function logError($str)
+ {
+ $this->errorlist[] = $str;
+ }
+
+ /*
+ * Log a warning
+ */
+ function logWarning($str)
+ {
+ $this->warninglist[] = $str;
+ }
+
+ /*
+ * Log a notice
+ */
+ function logNotice($str)
+ {
+ $this->noticelist[] = $str;
+ }
+
+ /*
+ * Log errors from a Form
+ */
+ function logFormErrors($obj)
+ {
+ $this->errorlist = array_merge($this->errorlist, $obj->errorlist);
+ }
+}
+
+?>
diff --git a/app/class/mysql.class.php b/app/class/mysql.class.php
new file mode 100644
index 0000000..317468c
--- /dev/null
+++ b/app/class/mysql.class.php
@@ -0,0 +1,63 @@
+<?php
+
+require_once "class/database.iface.php";
+
+/*
+ * MySQL support for Scrott
+ */
+class Mysql implements Database
+{
+ /*
+ * Constructor
+ */
+ function __construct($host, $username, $password, $dbName)
+ {
+ $this->db = new mysqli($host, $username, $password, $dbName);
+
+ if ($this->db->connect_error)
+ throw new Exception("Can not connect to Mysql database. Please check your Scrott configuration.");
+ }
+
+ /*
+ * Destructor
+ */
+ function __destruct()
+ {
+ $this->close();
+ }
+
+ /*
+ * Close connection to DB
+ */
+ function close()
+ {
+ $this->db->close();
+ }
+
+ /*
+ * Make a query of the database. Return data as an array of arrays
+ */
+ function query($query)
+ {
+ $arr = array();
+ $res = $this->db->query($query);
+
+ if ($res === true || $res === false)
+ return $arr;
+
+ while ($r = $res->fetch_assoc())
+ $arr[] = $r;
+
+ return $arr;
+ }
+
+ /*
+ * Escape a string for use in a query
+ */
+ function esc($string)
+ {
+ return $this->db->real_escape_string($string);
+ }
+}
+
+?>
diff --git a/app/class/object.class.php b/app/class/object.class.php
new file mode 100644
index 0000000..bcd8dfa
--- /dev/null
+++ b/app/class/object.class.php
@@ -0,0 +1,227 @@
+<?php
+
+require_once "class/framework.class.php";
+
+/*
+ * Base class for Scrott database objects
+ */
+abstract class Object extends Framework
+{
+ /*
+ * Constructor
+ */
+ function __construct($childTable = "object", $childCols = null)
+ {
+ $this->db = $this->getDbConnection();
+
+ $this->table = "object";
+ $this->cols = array(
+ "guid",
+ "perms",
+ "owner",
+ "parent",
+ "name",
+ "timeCreated",
+ "timeUpdated",
+ "type"
+ );
+
+ $this->childTable = $this->db->esc($childTable);
+ $this->childCols = array();
+
+ if (is_array($childCols))
+ {
+ foreach ($childCols as $col)
+ $this->childCols[] = $this->db->esc($col);
+ }
+ }
+
+ /*
+ * Populate this object with data from the DB with a given GUID
+ */
+ function loadObj($guid = null)
+ {
+ if (is_null($guid))
+ return;
+
+ if (!$this->isGUID($guid))
+ return;
+
+ $escdGuid = $this->db->esc($guid);
+
+ /* Base fields */
+ $query = "SELECT * FROM `" . $this->table . "` WHERE `guid` = '" . $escdGuid . "'";
+ $result = $this->db->query($query)[0];
+
+ foreach ($this->cols as $col)
+ {
+ if (isset($result[$col]))
+ $this->$col = $result[$col];
+ }
+
+ /* Child Table fields */
+ $query = "SELECT * FROM `" . $this->childTable . "` WHERE `guid` = '" . $escdGuid . "'";
+ $result = $this->db->query($query)[0];
+
+ foreach ($this->childCols as $col)
+ {
+ if (isset($result[$col]))
+ $this->$col = $result[$col];
+ }
+ }
+
+ /*
+ * Write this object to the database
+ */
+ function saveObj()
+ {
+ if (isset($this->guid))
+ {
+ /* Update Base */
+ $updateStr = "";
+
+ foreach ($this->cols as $col)
+ {
+ if (!isset($this->$col))
+ continue;
+
+ $updateStr .= "`" . $col . "` = '" . $this->db->esc($this->$col) . "', ";
+ }
+
+ if (strlen($updateStr) > 0)
+ {
+ $updateStr = substr($updateStr, 0, -2); // remove ", " from the end
+ $query = "UPDATE `" . $this->table . "` SET " . $updateStr . " WHERE `guid` = '" . $this->db->esc($this->guid) . "'";
+ $this->db->query($query);
+ }
+
+ /* Update Child */
+ $updateStr = "";
+
+ foreach ($this->childCols as $col)
+ {
+ if (!isset($this->$col))
+ continue;
+
+ $updateStr .= "`" . $col . "` = '" . $this->db->esc($this->$col) . "', ";
+ }
+
+ if (strlen($updateStr) > 0)
+ {
+ $updateStr = substr($updateStr, 0, -2); // remove ", " from the end
+ $query = "UPDATE `" . $this->childTable . "` SET " . $updateStr . " WHERE `guid` = '" . $this->db->esc($this->guid) . "'";
+ $this->db->query($query);
+ }
+ }
+
+ else
+ {
+ $this->guid = $this->getNewGUID();
+
+ /* Insert Base */
+ $colsStr = "";
+ $valsStr = "";
+
+ foreach ($this->cols as $col)
+ {
+ if (!isset($this->$col))
+ continue;
+
+ $colsStr .= "`" . $col . "`, ";
+ $valsStr .= "'" . $this->db->esc($this->$col) . "', ";
+ }
+
+ if (strlen($colsStr) > 0)
+ {
+ $colsStr = substr($colsStr, 0, -2); // remove ", "
+ $valsStr = substr($valsStr, 0, -2);
+ $query = "INSERT INTO `" . $this->table . "` (" . $colsStr . ") VALUES (" . $valsStr . ")";
+ $this->db->query($query);
+ }
+
+ /* Insert Child */
+ $colsStr = "";
+ $valsStr = "";
+
+ foreach ($this->childCols as $col)
+ {
+ if (!isset($this->$col))
+ continue;
+
+ $colsStr .= "`" . $col . "`, ";
+ $valsStr .= "'" . $this->db->esc($this->$col) . "', ";
+ }
+
+ if (strlen($colsStr) > 0)
+ {
+ $colsStr = substr($colsStr, 0, -2); // remove ", "
+ $valsStr = substr($valsStr, 0, -2);
+ $query = "INSERT INTO `" . $this->childTable . "` (" . $colsStr . ") VALUES (" . $valsStr . ")";
+ $this->db->query($query);
+ }
+ }
+ }
+
+ /*
+ * Remove this object from the database
+ */
+ function delObj()
+ {
+ if (!isset($this->guid))
+ return;
+
+ /* Delete Base */
+ $query = "DELETE FROM `" . $this->table . "` WHERE `guid` = '" . $this->db->esc($this->guid) . "'";
+ $this->db->query($query);
+
+ /* Delete Child */
+ $query = "DELETE FROM `" . $this->childTable . "` WHERE `guid` = '" . $this->db->esc($this->guid) . "'";
+ $this->db->query($query);
+ }
+
+ /*
+ * Check whether given GUID exists
+ */
+ function isGUID($guid)
+ {
+ $query = "SELECT `guid` FROM `object` WHERE `guid` = '" . $this->db->esc($guid) . "'";
+ $result = $this->db->query($query);
+
+ if (count($result) > 0)
+ return true;
+
+ return false;
+ }
+
+ /*
+ * Get a new, unique GUID for a new system object
+ */
+ function getNewGUID()
+ {
+ do
+ {
+ $sha = hash("sha256", rand());
+ $guid = substr($sha, 0, 8);
+ }
+ while ($this->isGUID($guid));
+
+ return $guid;
+ }
+}
+
+/*
+ * Concrete Database Object which can be used in a polymorphic way
+ */
+class DBObject extends Object
+{
+ /*
+ * Constructor
+ */
+ function __construct($guid = null)
+ {
+ parent::__construct();
+ $this->loadObj($guid);
+ }
+}
+
+?>