diff options
author | Malf Furious <m@lfurio.us> | 2015-12-18 23:18:33 -0500 |
---|---|---|
committer | Malf Furious <m@lfurio.us> | 2015-12-18 23:18:33 -0500 |
commit | 9068e6916ad68194fce2518ab5841af1c8949f3d (patch) | |
tree | 2f91e9e3be00c1492cdf4ce88d8e77a909c5c287 | |
parent | 2ebdbaa48f10d6a6f5a1b78f4ef2c5433e50c8cf (diff) | |
parent | b21251ef971d262dc414869fed83f52d0098bfe6 (diff) | |
download | scrott-9068e6916ad68194fce2518ab5841af1c8949f3d.tar.gz scrott-9068e6916ad68194fce2518ab5841af1c8949f3d.zip |
Merge branch 'framework' into dev
Diffstat (limited to '')
29 files changed, 1309 insertions, 9 deletions
diff --git a/app/assets/img/.gitkeep b/app/assets/img/.gitkeep new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/app/assets/img/.gitkeep diff --git a/app/assets/img/amy.png b/app/assets/img/amy.png Binary files differdeleted file mode 100644 index 493fb13..0000000 --- a/app/assets/img/amy.png +++ /dev/null diff --git a/app/assets/img/eggman.png b/app/assets/img/eggman.png Binary files differdeleted file mode 100644 index 6fea441..0000000 --- a/app/assets/img/eggman.png +++ /dev/null diff --git a/app/assets/img/knuckles.png b/app/assets/img/knuckles.png Binary files differdeleted file mode 100644 index 7c7b7e2..0000000 --- a/app/assets/img/knuckles.png +++ /dev/null diff --git a/app/assets/img/sonic.png b/app/assets/img/sonic.png Binary files differdeleted file mode 100644 index db3559f..0000000 --- a/app/assets/img/sonic.png +++ /dev/null diff --git a/app/assets/img/tails.png b/app/assets/img/tails.png Binary files differdeleted file mode 100644 index 8639790..0000000 --- a/app/assets/img/tails.png +++ /dev/null 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); + } +} + +?> diff --git a/app/controller/root.control.php b/app/controller/root.control.php new file mode 100644 index 0000000..437cae1 --- /dev/null +++ b/app/controller/root.control.php @@ -0,0 +1,67 @@ +<?php + +require_once "class/controller.class.php"; +require_once "controller/sysconf.control.php"; + +/* + * Root-level controller for Scrott app. This object will delegate the page request to the + * appropriate controller or handle it with an error message page. + */ +class Root extends Controller +{ + /* + * Controller implementation + */ + function handle($argv) + { + /* TODO -- Catch app exceptions here and display a special view to communicate them to user */ + /* TODO -- Authentication (login / logout / register) MVC */ + + $argv = $this->normalizeArgv($argv); + + /* First, make sure the system configuration file has been included */ + if (!$this->scrottConfExists()) + { + $ctrl = new Sysconf(); + $ctrl->handle($argv); + } + + /* TODO */ + else + echo "Configuration is present!"; + } + + /* + * Get a useful path string by normalizeing the $argv array received from the main function. + * This will remove directory names that appear in the $this->ar() string and the initial + * and trailing (if present) empty strings + */ + function normalizeArgv($argv) + { + $argv = array_values(array_filter($argv)); + $ar = array_values(array_filter(explode("/", $this->ar()))); + $i = 0; + $trunc = true; + + if (count($ar) == 0) + return $argv; + + foreach ($ar as $elem) + { + if ($elem != $argv[$i]) + { + $trunc = false; + break; + } + + $i++; + } + + if (!$trunc) + return $argv; + + return array_values(array_slice($argv, count($ar))); + } +} + +?> diff --git a/app/controller/sysconf.control.php b/app/controller/sysconf.control.php new file mode 100644 index 0000000..a7db1e8 --- /dev/null +++ b/app/controller/sysconf.control.php @@ -0,0 +1,43 @@ +<?php + +require_once "class/controller.class.php"; +require_once "model/sysconf.mod.php"; + +/* + * SysConf is the interface for creating an install's "scrott.conf.php" file if it doesn't exist + */ +class Sysconf extends Controller +{ + /* + * Controller implementation + */ + function handle($argv) + { + $mod = new SysconfModel(); + + switch ($_REQUEST['input']['action']) + { + case "save": + $this->action_save($mod); + break; + + default: + $this->action_default($mod); + break; + } + } + + function action_default($mod) + { + $mod->deflt(); + include "view/sysconf/default.view.php"; + } + + function action_save($mod) + { + $mod->save($_REQUEST['input']); + $this->action_default($mod); + } +} + +?> diff --git a/app/index.php b/app/index.php new file mode 100644 index 0000000..37258d7 --- /dev/null +++ b/app/index.php @@ -0,0 +1,16 @@ +<?php + +require_once "controller/root.control.php"; + +/* + * Entry-point to app. Grab the requested path and pass it to the root app controller. + */ +function main($argv) +{ + $app = new Root(); + $app->handle($argv); +} + +main(explode("/", $_REQUEST['path'])); // Start rendering web page for the requested path. + +?> diff --git a/app/model/master.mod.php b/app/model/master.mod.php new file mode 100644 index 0000000..aebcaaa --- /dev/null +++ b/app/model/master.mod.php @@ -0,0 +1,42 @@ +<?php + +require_once "class/model.class.php"; + +class MasterModel extends Model +{ + /* + * Get the appropriate alert class to use when showing the notice modal + */ + function getNoticeModalAlertClass() + { + if ($this->isError()) + return "alert-danger"; + + if ($this->isWarning()) + return "alert-warning"; + + if ($this->isNotice()) + return "alert-info"; + + return ""; + } + + /* + * Get the appropriate glyphicon to use when showing the notice modal + */ + function getNoticeModalGlyphicon() + { + if ($this->isError()) + return "glyphicon glyphicon-remove-sign"; + + if ($this->isWarning()) + return "glyphicon glyphicon-exclamation-sign"; + + if ($this->isNotice()) + return "glyphicon glyphicon-info-sign"; + + return ""; + } +} + +?> diff --git a/app/model/sysconf.mod.php b/app/model/sysconf.mod.php new file mode 100644 index 0000000..30ebd58 --- /dev/null +++ b/app/model/sysconf.mod.php @@ -0,0 +1,60 @@ +<?php + +require_once "model/master.mod.php"; +require_once "class/form.class.php"; + +class SysconfModel extends MasterModel +{ + var $CONF_FILE = "scrott.conf.php"; + + /* + * Default action + */ + function deflt() + { + } + + /* + * Save the submitted data to the config file + */ + function save($input) + { + $form = new Form(); + $form->field_text("dbAddress"); + $form->field_text("dbName"); + $form->field_text("dbUser"); + $form->field_text("dbPass", null, false); + $form->field_enum("settSSL", array("force", "neither", "forbid")); + + if (!$form->populate($input)) + { + $this->logFormErrors($form); + return; + } + + /* TODO -- test database connection before proceeding */ + + $f = fopen($this->CONF_FILE, "w"); + + if (!$f) + { + $this->logError("Can not create configuration file"); + return; + } + + fwrite($f, "<?php\n"); + fwrite($f, "\$_SCROTT['conf'] = 'conf';\n"); + fwrite($f, "\$_SCROTT['dbEngine'] = 'mysql';\n"); + fwrite($f, "\$_SCROTT['dbAddress'] = '" . $form->dbAddress . "';\n"); + fwrite($f, "\$_SCROTT['dbName'] = '" . $form->dbName . "';\n"); + fwrite($f, "\$_SCROTT['dbUser'] = '" . $form->dbUser . "';\n"); + fwrite($f, "\$_SCROTT['dbPass'] = '" . $form->dbPass . "';\n"); + fwrite($f, "\$_SCROTT['settSSL'] = '" . $form->settSSL . "';\n"); + fwrite($f, "?>\n"); + + fclose($f); + $this->redirectTo($this->ar()); + } +} + +?> diff --git a/app/view/master/foot.view.php b/app/view/master/foot.view.php new file mode 100644 index 0000000..6220f89 --- /dev/null +++ b/app/view/master/foot.view.php @@ -0,0 +1,10 @@ +<script type="text/javascript" src="<?=$mod->ar()?>/assets/js/jquery.min.js"></script> +<script type="text/javascript" src="<?=$mod->ar()?>/assets/js/bootstrap.min.js"></script> + +<?php if ($mod->isError() || $mod->isWarning() || $mod->isNotice()) { ?> + <script type="text/javascript"> + $(window).load(function() { + $("#noticeModal").modal("show"); + }); + </script> +<?php } ?> diff --git a/app/view/master/head.view.php b/app/view/master/head.view.php new file mode 100644 index 0000000..54294b8 --- /dev/null +++ b/app/view/master/head.view.php @@ -0,0 +1,5 @@ +<meta charset="utf-8" /> +<meta http-equiv="X-UA-Compatible" content="IE=edge" /> +<meta name="viewport" content="width=device-width, initial-scale=1" /> + +<link rel="stylesheet" type="text/css" href="<?=$mod->ar()?>/assets/css/bootstrap.min.css" /> diff --git a/app/view/master/topp.view.php b/app/view/master/topp.view.php new file mode 100644 index 0000000..fe430ff --- /dev/null +++ b/app/view/master/topp.view.php @@ -0,0 +1,44 @@ +<?php if ($mod->isError() || $mod->isWarning() || $mod->isNotice()) { ?> + <div id="noticeModal" class="modal fade" tabindex="-1" role="dialog"> + <div class="modal-dialog" role="document"> + <div class="modal-content"> + + <div class="modal-body alert <?=$mod->getNoticeModalAlertClass()?>" style="margin: 0;"> + <h1 class="text-center"><span class="<?=$mod->getNoticeModalGlyphicon()?>"></span></h1> + <h5 class="text-center">Something Happened</h5> + + <?php if ($mod->isError()) { ?> + <p> + <?php foreach ($mod->errorlist as $err) { ?> + <span class="label label-danger">Error</span> <?=$err?><br /> + <?php } ?> + </p> + <?php } ?> + + <?php if ($mod->isWarning()) { ?> + <p> + <?php foreach ($mod->warninglist as $warn) { ?> + <span class="label label-warning">Warning</span> <?=$warn?><br /> + <?php } ?> + </p> + <?php } ?> + + <?php if ($mod->isNotice()) { ?> + <p> + <?php foreach ($mod->noticelist as $note) { ?> + <span class="label label-info">Notice</span> <?=$note?><br /> + <?php } ?> + </p> + <?php } ?> + + <div class="text-center"> + <button type="button" class="btn btn-default" data-dismiss="modal"> + <span class="glyphicon glyphicon-ok"></span> Got it + </button> + </div> + </div> + + </div> + </div> + </div> +<?php } ?> diff --git a/app/view/sysconf/default.view.php b/app/view/sysconf/default.view.php new file mode 100644 index 0000000..1ecedd8 --- /dev/null +++ b/app/view/sysconf/default.view.php @@ -0,0 +1,124 @@ +<!DOCTYPE html> + +<html lang="en"> + <head> + <?php include "view/master/head.view.php"; ?> + <title>Scrott - System-level configuration missing</title> + + <style type="text/css"> + body { padding-top: 50px; } + a { color: inherit; } + </style> + </head> + + <body> + <?php include "view/master/topp.view.php"; ?> + + <?php include "view/sysconf/user.modal.view.php"; ?> + <?php include "view/sysconf/group.modal.view.php"; ?> + <?php include "view/sysconf/pad.modal.view.php"; ?> + <?php include "view/sysconf/stage.modal.view.php"; ?> + <?php include "view/sysconf/issue.modal.view.php"; ?> + <?php include "view/sysconf/message.modal.view.php"; ?> + + <div class="container"> + <div class="jumbotron"> + <h2 class="text-center"> + <a href="#" data-toggle="modal" data-target="#userModal"><span class="glyphicon glyphicon-user"></span></a> + <a href="#" data-toggle="modal" data-target="#groupModal"><span class="glyphicon glyphicon-th"></span></a> + <a href="#" data-toggle="modal" data-target="#padModal"><span class="glyphicon glyphicon-edit"></span></a> + <a href="#" data-toggle="modal" data-target="#stageModal"><span class="glyphicon glyphicon-tasks"></span></a> + <a href="#" data-toggle="modal" data-target="#issueModal"><span class="glyphicon glyphicon-inbox"></span></a> + <a href="#" data-toggle="modal" data-target="#messageModal"><span class="glyphicon glyphicon-envelope"></span></a> + </h2> + + <h1 class="text-center">Welcome to Scrott!</h1> + <hr /> + + <p class="text-center">You're seeing this page because the file "scrott.conf.php" is missing.<br />Please fill out the form below to create a config automatically!</p> + <p class="text-center">Click the icons above to learn more about core Scrott features and constructs!</p> + <hr /> + + <p class="text-center">This form will initialize Scrott's system-level configuration.<br />These are things the app needs before it can begin functioning at all!</p> + <h5 class="text-center">It is presumed that you are the administrator for this Scrott install.<br />There is a security risk involved with exposing this page to the public!</h5> + + <div class="row"> + <div class="col-md-2"></div> + + <div class="col-md-8"> + <div class="panel panel-default"> + <div class="panel-body"> + <form method="post" action="<?=$mod->ap()?>"> + <input type="hidden" name="input[action]" value="save" /> + <legend>Database Connection</legend> + <div class="form-group"> + <label for="inputDBEngine">Engine</label> + <input type="text" id="inputDBEngine" class="form-control" value="Mysql" disabled /> + </div> + + <div class="form-group"> + <label for="inputDBAddress">Server Address</label> + <input type="text" name="input[dbAddress]" id="inputDBAddress" class="form-control" placeholder="localhost" /> + </div> + + <div class="form-group"> + <label for="inputDBName">Database Name</label> + <input type="text" name="input[dbName]" id="inputDBName" class="form-control" placeholder="db_scrott" /> + </div> + + <div class="form-group"> + <label for="inputDBUser">Username</label> + <input type="text" name="input[dbUser]" id="inputDBUser" class="form-control" placeholder="root" /> + </div> + + <div class="form-group"> + <label for="inputDBPass">Password</label> + <input type="password" name="input[dbPass]" id="inputDBPass" class="form-control" /> + </div> + + <legend>Application Installation</legend> + <div class="form-group"> + <label for="inputAppPath">Install Location</label> + <input type="text" id="inputAppPath" class="form-control" value="<?=$mod->ar()?>/" disabled /> + <h6 class="pull-right">Detected from location of files in web document root</h6> + </div> + + <legend>Settings</legend> + <div class="form-group"> + <label>HTTP(S)</label> + <div class="radio"> + <label> + <input type="radio" name="input[settSSL]" value="force" /> + Always Force SSL + </label> + </div> + + <div class="radio"> + <label> + <input type="radio" name="input[settSSL]" value="neither" checked /> + Neither (Application can override) + </label> + </div> + + <div class="radio"> + <label> + <input type="radio" name="input[settSSL]" value="forbid" /> + Always Forbid SSL + </label> + </div> + </div> + + <button type="submit" class="btn btn-primary pull-right">Use these settings</button> + </form> + </div> + </div> + </div> + + <div class="col-md-2"></div> + </div> + </div> + </div> + + <?php include "view/master/foot.view.php"; ?> + </body> +</html> diff --git a/app/view/sysconf/group.modal.view.php b/app/view/sysconf/group.modal.view.php new file mode 100644 index 0000000..cd05642 --- /dev/null +++ b/app/view/sysconf/group.modal.view.php @@ -0,0 +1,31 @@ +<div id="groupModal" class="modal fade" tabindex="-1" role="dialog"> + <div class="modal-dialog" role="document"> + <div class="modal-content"> + <div class="modal-header"> + + <button type="button" class="close" data-dismiss="modal"> + <span aria-hidden="true">×</span> + </button> + + <h4 class="modal-title">User Groups</h4> + + </div> + + <div class="modal-body"> + <p>Groups make sharing objects and collaborating easier. Instead of creating your + own pad and adding each of your team members to it individualy, create a + group-owned pad. With the appropriate permissions set, all group members will be + able to use the pad. This also solves issues where a shared pad is owned by a + single user whose account gets closed. Instead pads and other objects are safe as + long as the group exists. Groups are owned and managed by a single user, who must + stick around or transfer ownership for the group to continue functioning.</p> + </div> + + <div class="modal-footer"> + <button type="button" class="btn btn-primary" data-dismiss="modal"> + Close + </button> + </div> + </div> + </div> +</div> diff --git a/app/view/sysconf/issue.modal.view.php b/app/view/sysconf/issue.modal.view.php new file mode 100644 index 0000000..a77c1e3 --- /dev/null +++ b/app/view/sysconf/issue.modal.view.php @@ -0,0 +1,34 @@ +<div id="issueModal" class="modal fade" tabindex="-1" role="dialog"> + <div class="modal-dialog" role="document"> + <div class="modal-content"> + <div class="modal-header"> + + <button type="button" class="close" data-dismiss="modal"> + <span aria-hidden="true">×</span> + </button> + + <h4 class="modal-title">Issues</h4> + + </div> + + <div class="modal-body"> + <p>Issues represent work. This work could be anything from a software project's bug + needing fixed or a new feature to implement to a record of needing to order more + celing panels for the new home office build. Work is done by someone, for someone, + and this is reflected in Scrott's internal data structures. Every issues has a + distinct owner (someone requesting work be done) and assignee (someone who needs to + do the work). If others are interested in the work or have valuable input on the + work, they may be included (cc'd) on the issue to receive updates on it or to be able + to post their own updates. Scrott tracks issues using pads which are representative + of a high-level project or some similar effort. So related issues are tracked + together.</p> + </div> + + <div class="modal-footer"> + <button type="button" class="btn btn-primary" data-dismiss="modal"> + Close + </button> + </div> + </div> + </div> +</div> diff --git a/app/view/sysconf/message.modal.view.php b/app/view/sysconf/message.modal.view.php new file mode 100644 index 0000000..715bb06 --- /dev/null +++ b/app/view/sysconf/message.modal.view.php @@ -0,0 +1,37 @@ +<div id="messageModal" class="modal fade" tabindex="-1" role="dialog"> + <div class="modal-dialog" role="document"> + <div class="modal-content"> + <div class="modal-header"> + + <button type="button" class="close" data-dismiss="modal"> + <span aria-hidden="true">×</span> + </button> + + <h4 class="modal-title">Messages</h4> + + </div> + + <div class="modal-body"> + <p>Although Scrott is formost an issue-tracking tool, it can also be a useful + communication tool for team and client relationships. Scrott includes a lightweight + messaging system that is aparent:</p> + <ul> + <li>In pads: where users can start new general discussion threads and post + replys</li> + <li>In issues: where users can post comments or updates on work status</li> + <li>In general: where users can send other users direct private messages</li> + </ul> + <p>With email integration, Scrott also allows communication with non-scrott user + clients or owners of issues.</p> + <p>Scrott's built-in logging system utilizes this messaging framework and is used + by object owners and site administrators to monitor usage and activity on Scrott</p> + </div> + + <div class="modal-footer"> + <button type="button" class="btn btn-primary" data-dismiss="modal"> + Close + </button> + </div> + </div> + </div> +</div> diff --git a/app/view/sysconf/pad.modal.view.php b/app/view/sysconf/pad.modal.view.php new file mode 100644 index 0000000..88c62e7 --- /dev/null +++ b/app/view/sysconf/pad.modal.view.php @@ -0,0 +1,50 @@ +<div id="padModal" class="modal fade" tabindex="-1" role="dialog"> + <div class="modal-dialog" role="document"> + <div class="modal-content"> + <div class="modal-header"> + + <button type="button" class="close" data-dismiss="modal"> + <span aria-hidden="true">×</span> + </button> + + <h4 class="modal-title">Pads</h4> + + </div> + + <div class="modal-body"> + <p>Use pads to track tasks, issues, bugs, client tickets, etc. Within Scrott, these + are collectively referred to as 'issues'. Use a pad to track your progress on a + project's completion, to manage user issues for a help-desk, to keep a shopping list, + bucket list, lists to track your progress on a television series (or series you'd + like to start), or books to read, or anything inbetween.</p> + + <p>In addition to issue tracking, pads enable users to communicate with one another. + Each pad consists of a lightweight message board (forum) and users can also post + messages directly to issues if they are relevant. With email integration, even + non-scrott users may be clued in to the progress of their open issues. Pads even + allow for message threads to be converted (elevated) to a new open issue if need be. + (for example: Imagine you started a thread to discuss a potential new website design. + Once your team had arrived at something the majority liked, the thread can be elevated + to a new issue (preserving all messaging history), assigned to one of the team + members, and tracked just like all of your other issues.)</p> + + <p>If you would like to allow the public to open issues in your pad (ie: if your pad + is allowing bug reports/feature requests from users of your project or if your pad + is the issue tracker for a help-desk), you can enable an issue portal for your pad. + A portal is a page accessible to the public where anonymous internet users (with a + verified email address) may submit new issues to your pad. Their email address + becomes the issue owner so the requester is still able to receive updates on the + issue as well as respond with their own messages.</p> + + <p>Pads can be used by a single user or many; and pads can be owned by a user or a + user group</p> + </div> + + <div class="modal-footer"> + <button type="button" class="btn btn-primary" data-dismiss="modal"> + Close + </button> + </div> + </div> + </div> +</div> diff --git a/app/view/sysconf/stage.modal.view.php b/app/view/sysconf/stage.modal.view.php new file mode 100644 index 0000000..4eaab43 --- /dev/null +++ b/app/view/sysconf/stage.modal.view.php @@ -0,0 +1,34 @@ +<div id="stageModal" class="modal fade" tabindex="-1" role="dialog"> + <div class="modal-dialog" role="document"> + <div class="modal-content"> + <div class="modal-header"> + + <button type="button" class="close" data-dismiss="modal"> + <span aria-hidden="true">×</span> + </button> + + <h4 class="modal-title">Pad Stages</h4> + + </div> + + <div class="modal-body"> + <p>The issue-tracking aspect of pads is divided up using pad stages. A stage is + representative of the level of completion or high-level status of an issue. For + example, a pad may have an initial stage named "To Do", an intermediate stage named + "In Progress", and another stage "In Review". Stages form a sequence in practice. + When the appropriate amount of work has been done on an issue, it is advanced to the + next stage in the pipeline. This allows other pad members and the issue's owner to + at a glance get an idea of the high-level progress of the issue.</p> + + <p>Once an issue is closed, it is removed from this stage pipeline, but retained in + the pad itself for archival purposes and for generating reports.</p> + </div> + + <div class="modal-footer"> + <button type="button" class="btn btn-primary" data-dismiss="modal"> + Close + </button> + </div> + </div> + </div> +</div> diff --git a/app/view/sysconf/user.modal.view.php b/app/view/sysconf/user.modal.view.php new file mode 100644 index 0000000..b3650b0 --- /dev/null +++ b/app/view/sysconf/user.modal.view.php @@ -0,0 +1,30 @@ +<div id="userModal" class="modal fade" tabindex="-1" role="dialog"> + <div class="modal-dialog" role="document"> + <div class="modal-content"> + <div class="modal-header"> + + <button type="button" class="close" data-dismiss="modal"> + <span aria-hidden="true">×</span> + </button> + + <h4 class="modal-title">User Accounts</h4> + + </div> + + <div class="modal-body"> + <p>Scrott implements a very flexible permissions and access-control system. It all + starts with users' accounts. Users can create and maintain objects and share them + with other users if they choose. This is done with a mechanism similar to unix + file permission modes. Scrott objects form a hierarchy, so permissions from 'higher' + objects will cascade down. This means that a non-trusted user may control an issue + in your pad, but have no access to the pad itself.</p> + </div> + + <div class="modal-footer"> + <button type="button" class="btn btn-primary" data-dismiss="modal"> + Close + </button> + </div> + </div> + </div> +</div> diff --git a/app/index.html b/examples/example.html index 15b8602..44c91f2 100644 --- a/app/index.html +++ b/examples/example.html @@ -2,14 +2,8 @@ <html lang="en"> <head> - <meta charset="utf-8" /> - <meta http-equiv="X-UA-Compatible" content="IE=edge" /> - <meta name="viewport" content="width=device-width, initial-scale=1" /> - <title>Scrott - Save the World</title> - <link rel="stylesheet" type="text/css" href="assets/css/bootstrap.min.css" /> - <style type="text/css"> body { @@ -223,8 +217,5 @@ body </div> </div> - <!-- JS --> - <script type="text/javascript" src="assets/js/jquery.min.js"></script> - <script type="text/javascript" src="assets/js/bootstrap.min.js"></script> </body> </html> |