Files
DomFramework/routeSQL.php

1114 lines
44 KiB
PHP

<?php
/** DomFramework
@package domframework
@author Dominique Fournier <dominique@fournier38.fr> */
require_once ("domframework/route.php");
require_once ("domframework/form.php");
require_once ("domframework/renderer.php");
error_reporting (E_ALL);
/** Automatic Routing for SQL database
Allow to do CRUD on datas with only one line in index.php */
class routeSQL
{
/** Activate the debug */
public $debug=0;
/** Display the Actions column in list of entries */
public $displayActions = true;
/** Do a confirmation in javascript before deleting entry */
public $deleteConfirm = true;
/** Push the actions buttons at end of line */
public $actionsAtEnd = false;
/** The Text to Delete */
public $textDelete = "";
/** The Text to Edit */
public $textEdit = "";
/** enable internal CSS */
public $enableInternalCSS = true;
/** Definition of the position in top bar at left
Allowed : addNew numberEntryByDisplay search informations paginator */
public $topBarLeft = array ("addNew", "numberEntryByDisplay");
/** Definition of the position in top bar at right
Allowed : addNew numberEntryByDisplay search informations paginator */
public $topBarRight = array ("search");
/** Definition of the position in bottom bar at left
Allowed : addNew numberEntryByDisplay search informations paginator */
public $bottomBarLeft = array ("informations");
/** Definition of the position in bottom bar at right
Allowed : addNew numberEntryByDisplay search informations paginator */
public $bottomBarRight = array ("paginator");
/** The cookie path used to determine the old parameters
It is automatically generated with the URL */
public $path = "";
/** Authentication */
public $auth = array ("email"=>"anonymous");
/** Authorization object. Should allow a method named
"allow ($module, $user, $object)" which return
- NO if the object is not defined
- RO if the object is in read-only mode
- RW if the object is in read-write mode */
public $authorization = null;
/** Module name for authorization */
public $module = null;
/** Chain multiple routeSQL. Wait for an routeSQL object */
public $chained = null;
/** Chain multiple routeSQL. Wait for the foreign key */
public $chainedForeign = null;
/** Allow one or multiple links by entry in the list
The array must be of the form array ("linkname"=>,"icon"=>)
icon is optional and the linkname is used if it is not provided */
public $internalLinks = array ();
/** The renderer class to use for HTML pages */
public $rendererHTMLclass = false;
/** The renderer method to use for HTML pages */
public $rendererHTMLmethod = false;
/** The layout HTML to use for HTML pages */
public $rendererHTMLlayout = false;
/** The model file containing the database description */
private $model_file = "";
/** The model class included in the model file */
private $model_class = "";
/** The prefix to be used in the URL. Should be the end of $model_file
Ex : if $model_file = models/model_zone.php, the url_prefix should be
zone */
private $url_prefix = "";
/** The SQL object created */
private $objectDB = null;
/** The DSN to connect to the database */
private $dsn = null;
/** The Username to connect to the database */
private $username = null;
/** The Password to connect to the database */
private $password = null;
/** The Options to the PDO driver if needed */
private $driver_options = null;
/** The Datas are protected in read-only */
private $readwriteAllowed = true;
/** Connect to the database */
public function __construct ($model_file, $model_class, $url_prefix, $dsn,
$username = null, $password = null, $driver_options = null)
{
$this->model_file = $model_file;
$this->model_class = $model_class;
$this->url_prefix = $url_prefix;
$this->dsn = $dsn;
$this->username = $username;
$this->password = $password;
$this->driver_options = $driver_options;
$this->textDelete = "&#10060;";
$this->textEdit = "&#9998;";
$pos = strpos ($_SERVER["REQUEST_URI"], "?");
if ($pos === false)
$this->path = $_SERVER["REQUEST_URI"];
else
$this->path = substr ($_SERVER["REQUEST_URI"], 0, $pos);
}
/** Connect to the database */
private function connect ()
{
include "models/$this->model_file";
$this->objectDB = new $this->model_class ($this->dsn, $this->username,
$this->password, $this->driver_options);
}
/** Display the flash informations if no flash view is available */
private function showflash ()
{
$dataflash = "";
if (file_exists ("views/flash.php"))
require ("views/flash.php");
else
{
if (isset ($_SESSION["renderer"]["flash"]))
{
foreach ($_SESSION["renderer"]["flash"] as $flash)
{
$dataflash .= "<div class='alert ";
switch ($flash[0])
{
case 4: $dataflash .= "alert-danger";
$alert = dgettext("domframework","Error!");break;
case 3: $dataflash .= "alert-warning";
$alert = dgettext("domframework","Warning!");break;
case 2: $dataflash .= "alert-info";
$alert = dgettext("domframework","Info :");break;
case 1: $dataflash .= "alert-success";
$alert = dgettext("domframework","Success : ");break;
}
$dataflash .= " alert-dismissable'>\n";
$dataflash .= "<strong>$alert</strong> ".$flash[1]."\n";
$dataflash .= "</div>\n";
}
unset ($_SESSION["renderer"]["flash"]);
}
}
return $dataflash;
}
/** Display a paginator
$nbentries is the total number of elements
num is the number of elements displayed by page
page is the page to display */
private function paginatorArea ($nbentries, $page, $num, $search)
{
// The maximum of links available in the paginator
$maxClickPaginator = 10;
$route = new route ();
$prePage = false;
$postPage = false;
$content = " <div class='paginatorArea'>\n";
$displayedNumbers = 0;
for ($i = 1 ; $i < (1+$nbentries/($num)) ; $i++)
{
if ($i < ($page - $maxClickPaginator/2))
{
if ($prePage === false)
$content .= " ...\n";
$prePage = true;
continue;
}
if ($displayedNumbers >= $maxClickPaginator)
{
if ($postPage === false)
$content .= " ...\n";
$postPage = true;
continue;
}
$displayedNumbers++;
$content .= " <a href='".$route->baseURL().$this->url_prefix.
"?page=$i&amp;num=$num&amp;search=".urlencode ($search)."'";
if ($page == $i)
$content .= " class='selected'";
$content .= ">$i</a>\n";
}
if ($displayedNumbers === 0)
{
$i = 1;
$content .= " <a href='".$route->baseURL().$this->url_prefix.
"?page=$i&amp;num=$num&amp;search=".urlencode($search)."'";
if ($page == $i)
$content .= " class='selected'";
$content .= ">$i</a>\n";
}
$content .= " </div>\n";
return $content;
}
/** Display the actions buttons outside of the table (actually, juste the
'Add new entry' button */
private function addNewArea ($nbentries, $page, $num, $search)
{
$content = "";
if ($this->displayActions && $this->readwriteAllowed)
{
$route = new route ();
$content .= " <div class='actionExtern'>\n";
$content .= " <a href='".$route->baseURL().$this->url_prefix."/add'>"
.dgettext("domframework","Add new entry")."</a>\n";
$content .= " </div>\n";
}
return $content;
}
/** Display the select list to choose the number of displayed entries */
private function numberEntryByDisplayArea ($nbentries, $page, $num, $search)
{
$route = new route ();
$content = "";
$content .= " <div class='numberEntryByDisplayArea'>\n";
$content .= " <form method='get' action='".$route->baseURL().
$this->url_prefix."'>\n";
$content .= " <select name='num' onchange='this.form.submit()' >\n";
$list = array (10,20,50,100,200,500,1000);
foreach ($list as $element)
{
$content .= " <option ";
if ($element == $num)
$content .= "selected='selected'";
$content .= ">$element</option>\n";
}
$content .= " </select>\n";
$content .= " ".dgettext("domframework"," elements")."\n";
$content .= " </form>\n";
$content .= " </div>\n";
return $content;
}
/** Display the search area */
private function searchArea ($nbentries, $page, $num, $search)
{
$route = new route ();
$content = "";
$content .= " <div class='searchArea'>\n";
$content .= " <form method='get' action='".$route->baseURL().
$this->url_prefix."'>\n";
$content .= " ".dgettext("domframework"," Search: ");
$content .= " <input type='text' name='search' value='".
htmlentities ($search, ENT_QUOTES)."'/>\n";
$content .= " </form>\n";
$content .= " </div>\n";
return $content;
}
/** Display the informations */
private function informationsArea ($nbentries, $page, $num, $search)
{
$content = "";
$content .= " <div class='informationsArea'>\n";
$message = dgettext("domframework",
"Display the element {FIRST} to {LAST} on {COUNT} elements");
if ($nbentries === 0)
$message = str_replace ("{FIRST}", 0, $message);
else
$message = str_replace ("{FIRST}", 1+($num*($page-1)), $message);
if ($nbentries < ($num*$page))
$message = str_replace ("{LAST}", $nbentries, $message);
else
$message = str_replace ("{LAST}", ($num*$page), $message);
$message = str_replace ("{COUNT}", $nbentries, $message);
$content .= $message;
$content .= " </div>\n";
return $content;
}
/** Create the routes and the associated actions */
public function routesHTML ()
{
// If chained routeSQL, the url_prefix must be adapted
if ($this->chained !== null)
{
if (strpos ($this->chained->url_prefix, "/{chain}/") !== false)
throw new Exception ("Chained can not have an already chained object",
500);
$this->url_prefix = $this->chained->url_prefix."/{chain}/".
$this->url_prefix;
}
/** Add HTML routes */
$route = new route ();
$route->debug = $this->debug;;
$route->allowSlashes=false;
$route->get ($this->url_prefix."/", function ($chain) use ($route)
{
$route->redirect ("/".str_replace ("{chain}", $chain, $this->url_prefix),
"");
});
$route->get ($this->url_prefix.
"(\?({p1}=({v1})?)(&{p2}=({v2})?(&{p3}=({v3})?)?)?)?",
function ($p1, $v1, $p2, $v2, $p3, $v3, $chain) use ($route)
{
// List all the objects of the table
if ($this->chained !== null)
{
if ($this->chained->accessright ($chain) !== TRUE)
{
if ($this->auth["email"] === "anonymous")
throw new Exception (dgettext("domframework",
"Anonymous not allowed"), 401);
throw new Exception (dgettext("domframework",
"Access forbidden"), 403);
}
$this->chained->connect();
// $chainedValues are the informations associated to the $chain
$chainedValues = $this->chained->keyexists ($chain);
if ($chainedValues === false)
throw new Exception (dgettext("domframework",
"Object not found"), 404);
}
if ($this->accessright () !== TRUE)
{
if ($this->auth["email"] === "anonymous")
throw new Exception (dgettext("domframework",
"Anonymous not allowed"), 401);
throw new Exception (dgettext("domframework",
"Access forbidden"), 403);
}
if ($this->chained !== null &&
$this->chained->editright ($chain) !== true)
$this->readwriteAllowed = false;
// num is the number of elements displayed by page
// page is the page to display
// Allow the parameters to be sent in any order
if (isset ($_COOKIE["num"])) $num = $_COOKIE["num"];
if (isset ($_COOKIE["page"])) $page = $_COOKIE["page"];
if (isset ($_COOKIE["search"])) $search = $_COOKIE["search"];
if ($p1 === "num") $num = $v1;
if ($p2 === "num") $num = $v2;
if ($p3 === "num") $num = $v3;
if ($p1 === "page") $page = $v1;
if ($p2 === "page") $page = $v2;
if ($p3 === "page") $page = $v3;
if ($p1 === "search") $search = $v1;
if ($p2 === "search") $search = $v2;
if ($p3 === "search") $search = $v3;
if (!isset ($num) || $num === null || $num === "") $num = 10;
if (!isset ($page) || $page === null || $page === "") $page = 1;
if (!isset ($search) || $search === null || $search === "") $search = "";
$page = intval ($page);
$num = intval ($num);
$search = rawurldecode ($search);
setcookie ("page", $page, time()+3600*24*30, $this->path);
setcookie ("num", $num, time()+3600*24*30, $this->path);
setcookie ("search", $search, time()+3600*24*30, $this->path);
$this->url_prefix = str_replace ("{chain}", $chain, $this->url_prefix);
//echo "PAGE=$page\n";
//echo "NUM=$num\n";
//echo "SEARCH=$search\n";
//$route->debug=$this->debug;
$this->connect();
$csrf = new csrf ();
$token = $csrf->createToken ();
$titles = $this->objectDB->titles ();
unset ($titles[$this->chainedForeign]);
$foreignSelect = null;
if ($this->chained !== null)
$foreignSelect = array (array ($this->chainedForeign, $chain));
if ($search === "")
$datas = $this->objectDB->read (null, array_keys($titles), null, null,
$foreignSelect);
else
{
$criteria = array ();
foreach (array_keys($titles) as $column)
{
$s = $search;
if ($search[0] === "^")
$s = substr ($s, 1);
else
$s = "%$s";
if (substr ($search, -1) === "$")
$s = substr ($s, 0, -1);
else
$s = "$s%";
$criteria[] = array ($column, "$s", "LIKE");
}
$datas = $this->objectDB->read ($criteria, array_keys ($titles), null,
true, $foreignSelect);
}
$nbentries = count ($datas);
if ($num > 1000)
$route->redirect ("/".str_replace ("{chain}", $chain, $this->url_prefix)
."?page=$page&num=1000&search=$search", "");
if ($page < 1)
$route->redirect ("/".str_replace ("{chain}", $chain, $this->url_prefix)
. "?page=1&num=$num&search=$search", "");
// Push on the last page if the values are too high
if ($nbentries > 0 && ($page-1)*$num > $nbentries)
{
$maxPage = intval ($nbentries/$num)+1;
$route->redirect ("/".str_replace("{chain}", $chain, $this->url_prefix).
"?page=$maxPage&num=$num&search=$search", "");
}
$content = "";
// Internal CSS
if ($this->enableInternalCSS === true)
{
$content .= "<style type='text/css' scoped='scoped'>\n";
$content .= ".routeSQL { width:95%; margin-left:auto; margin-right:auto; }\n";
$content .= ".routeSQL a { text-decoration:none; }\n";
$content .= ".routeSQL .topBar { display: block; overflow: auto; }\n";
$content .= ".routeSQL .topBar .topleft { display: inline; float:left; }\n";
$content .= ".routeSQL .topBar .topright { display: inline; float:right; }\n";
$content .= ".routeSQL .bottomBar { display: block; overflow: auto; }\n";
$content .= ".routeSQL .bottomBar .bottomleft { display: inline; float:left; }\n";
$content .= ".routeSQL .bottomBar .bottomright { display: inline; float:right; }\n";
$content .= ".routeSQL .actionExtern { border:1px solid #ddd; border-radius:5px; padding:10px; margin:3px; float:left; }\n";
$content .= ".routeSQL .numberEntryByDisplayArea { border:1px solid #ddd; border-radius:5px; padding:10px; margin:3px; float:left; }\n";
$content .= ".routeSQL .searchArea { border:1px solid #ddd; border-radius:5px; padding:10px; margin:3px; float:left; }\n";
$content .= ".routeSQL .searchArea form { margin:-1px; }\n";
$content .= ".routeSQL .numberEntryByDisplayArea form { margin:-3px; }\n";
$content .= ".routeSQL .informationsArea { border:1px solid #ddd; border-radius:5px; padding:10px; margin:3px; float:left; }\n";
$content .= ".routeSQL .paginatorArea { border:1px solid #ddd; border-radius:5px; padding:10px; margin:3px; float:left; }\n";
$content .= ".routeSQL .paginatorArea a { border:1px solid grey; border-radius:5px; padding:3px; }\n";
$content .= ".routeSQL .paginatorArea .selected { background-color:#04d; color:white;font-weight:bold; }\n";
$content .= ".routeSQL table { width:100%;overflow:auto; border-collapse:collapse; }\n";
$content .= ".routeSQL table tr { border-top:1px solid #ccc;}\n";
$content .= ".routeSQL table th { border-bottom:3px solid #ccc; border-top:1px solid #fff; padding:9px 5px 9px 1px; }\n";
$content .= ".routeSQL table td { empty-cells:true; padding:9px 5px 9px 1px; }\n";
$content .= ".routeSQL table .noentry { text-align:center; color:#c00; font-weight:bolder; }\n";
$content .= ".routeSQL table .action { text-align:center; }\n";
$content .= ".routeSQL table .action .edit { color:#222; font-weight:bolder;}\n";
$content .= ".routeSQL table .action .delete { color:#c00; font-weight:bolder; }\n";
$content .= ".routeSQL table .odd { background-color:#f9f9f9; }\n";
$content .= "</style>\n";
}
$content .= "<div class='routeSQL'>\n";
$content .= $this->showflash ();
$content .= " <div class='topBar'>\n";
$content .= " <div class='topleft'>\n";
foreach ($this->topBarLeft as $area)
{
$areaName = $area."Area";
$content .= $this->$areaName ($nbentries, $page, $num, $search);
}
$content .= " </div>\n";
$content .= " <div class='topright'>\n";
foreach ($this->topBarRight as $area)
{
$areaName = $area."Area";
$content .= $this->$areaName ($nbentries, $page, $num, $search);
}
$content .= " </div>\n";
$content .= " </div>\n"; // End of topBar
$content .= " <table>\n";
$content .= " <thead>\n";
$content .= " <tr>\n";
if ($this->readwriteAllowed && $this->displayActions &&
$this->actionsAtEnd === false)
$content .= " <th>".dgettext("domframework","Actions")."</th>\n";
foreach ($titles as $title)
$content .= " <th>".htmlentities ($title)."</th>\n";
if ($this->readwriteAllowed && $this->displayActions &&
$this->actionsAtEnd !== false)
$content .= " <th>".dgettext("domframework","Actions")."</th>\n";
$content .= " </tr>\n";
$content .= " </thead>\n";
$content .= " <tbody>\n";
if ($nbentries === 0)
{
// Add one column more for actions
$countTitles = count($titles);
if ($this->readwriteAllowed && $this->displayActions)
$countTitles++;
$content .= " <tr><td colspan='$countTitles' class='noentry'>";
$content .= dgettext("domframework","No entry available");
$content .= "</td></tr>\n";
}
else
{
$i = 1;
$odd = "odd";
foreach ($datas as $line)
{
if ($i <= (($page-1)*$num) || $i > (($page-1)*$num + $num))
{
$i++;
continue;
}
$content .= " <tr class='$odd'>";
if ($odd === "odd") $odd = "even";
else $odd = "odd";
if ($this->actionsAtEnd !== false)
{
foreach ($line as $col)
$content .= "<td>".htmlentities ($col)."</td>";
}
if ($this->readwriteAllowed && $this->displayActions)
{
$content .= "<td class='action'>";
$content .= " <a href='".$route->baseURL().$this->url_prefix."/".
$line[$this->objectDB->primary]."' class='edit'>".
$this->textEdit."</a>";
$content .= " <a href='".$route->baseURL().$this->url_prefix."/".
$line[$this->objectDB->primary]."/delete/$token'";
if ($this->deleteConfirm)
$content .= " onclick=\"return confirm('".
dgettext("domframework",
"Are you sure to delete this entry?")."')\"";
$content .= " class='delete'>".$this->textDelete."</a>";
foreach ($this->internalLinks as $linkData)
{
if (! isset ($linkData["linkname"]))
throw new Exception ("No linkname defined !", 500);
$content .= " <a href='".$route->baseURL().$this->url_prefix."/".
$line[$this->objectDB->primary]."/".
$linkData["linkname"]."'>";
if (isset ($linkData["icon"]))
$content .= $linkData["icon"];
else
$content .= $linkData["linkname"];
}
$content .= "</td>";
}
if ($this->actionsAtEnd === false)
{
foreach ($line as $col)
$content .= "<td>".htmlentities ($col)."</td>";
}
$content .= "</tr>\n";
$i++;
}
}
$content .= " </tbody>\n";
$content .= " </table>\n";
$content .= " <div class='bottomBar'>\n";
$content .= " <div class='bottomleft'>\n";
foreach ($this->bottomBarLeft as $area)
{
$areaName = $area."Area";
$content .= $this->$areaName ($nbentries, $page, $num, $search);
}
$content .= " </div>\n";
$content .= " <div class='bottomright'>\n";
foreach ($this->bottomBarRight as $area)
{
$areaName = $area."Area";
$content .= $this->$areaName ($nbentries, $page, $num, $search);
}
$content .= " </div>\n";
$content .= " </div>\n"; // End of bottomBar
$content .= "</div>\n";
$this->rendererhtml ($content);
});
$route->get ($this->url_prefix."/{id}/delete/{token}",
function ($id, $token, $chain)
{
// Delete an existing object if the token is valid
if ($this->chained !== null)
{
if ($this->chained->editright ($chain) !== TRUE)
{
if ($this->auth["email"] === "anonymous")
throw new Exception (dgettext("domframework",
"Anonymous not allowed"), 401);
throw new Exception (dgettext("domframework","Access forbidden"),
403);
}
$this->chained->connect();
// $chainedValues are the informations associated to the $chain
$chainedValues = $this->chained->keyexists ($chain);
if ($chainedValues === false)
throw new Exception (dgettext("domframework",
"Object not found"), 404);
}
if ($this->accessright ($id) !== TRUE)
{
if ($this->auth["email"] === "anonymous")
throw new Exception (dgettext("domframework","Anonymous not allowed"),
401);
throw new Exception (dgettext("domframework","Access forbidden"), 403);
}
if ($this->editright ($id) !== TRUE)
{
if ($this->auth["email"] === "anonymous")
throw new Exception (dgettext("domframework","Anonymous not allowed"),
401);
throw new Exception (dgettext("domframework","Access forbidden"), 403);
}
if ($this->readonly ($id) === TRUE)
throw new Exception (dgettext("domframework","Access forbidden"), 403);
$this->connect();
$csrf = new csrf ();
$renderer = new renderer ();
$route = new route ();
try
{
$csrf->checkToken ($token);
$this->objectDB->delete ($id);
$route->redirect ("/".
str_replace ("{chain}", $chain, $this->url_prefix),
"");
}
catch (Exception $e)
{
$renderer->flash ("ERROR", $e->getMessage());
$route->redirect ("/".
str_replace ("{chain}", $chain, $this->url_prefix),
"");
}
});
$route->get ($this->url_prefix."/add", function ($chain)
{
// Add a new entry : form to be filled by the user
if ($this->chained !== null)
{
if ($this->chained->editright ($chain) !== TRUE)
{
if ($this->auth["email"] === "anonymous")
throw new Exception (dgettext("domframework",
"Anonymous not allowed"), 401);
throw new Exception (dgettext("domframework","Access forbidden"),
403);
}
$this->chained->connect();
// $chainedValues are the informations associated to the $chain
$chainedValues = $this->chained->keyexists ($chain);
if ($chainedValues === false)
throw new Exception (dgettext("domframework",
"Object not found"), 404);
}
if ($this->accessright () !== TRUE)
{
if ($this->auth["email"] === "anonymous")
throw new Exception (dgettext("domframework","Anonymous not allowed"),
401);
throw new Exception (dgettext("domframework","Access forbidden"), 403);
}
if ($this->editright () !== TRUE)
{
if ($this->auth["email"] === "anonymous")
throw new Exception (dgettext("domframework","Anonymous not allowed"),
401);
throw new Exception (dgettext("domframework","Access forbidden"), 403);
}
if ($this->readonly () === TRUE)
throw new Exception (dgettext("domframework","Access forbidden"), 403);
$this->connect();
$content = $this->showflash ();
$values = array ();
$errors = array();
$titles = $this->objectDB->titles ();
if (isset ($_SESSION["domframework"]["routeSQL"]["errors"]))
{
$errors = $_SESSION["domframework"]["routeSQL"]["errors"];
unset ($_SESSION["domframework"]["routeSQL"]["errors"]);
}
if (isset ($_SESSION["domframework"]["routeSQL"]["values"]))
{
$values = $_SESSION["domframework"]["routeSQL"]["values"];
unset ($_SESSION["domframework"]["routeSQL"]["values"]);
}
if ($this->enableInternalCSS === true)
{
// CSS is in edit an id too !
$content .= "<style type='text/css' scoped='scoped'>\n";
$content .= "#form .col-sm-2 { float:left; width: 16.66666667%; padding-left:15px;padding-right:15px;};\n";
$content .= "#form .col-sm-10 {float:left; width: 83.33333333%; }\n";
$content .= "#form .col-sm-12 { width: 100%; }\n";
$content .= "#form input[type='submit'] { width:93%; margin-left:auto;margin-right:auto}\n";
$content .= "#form .btn-primary { color: #fff; background-color: #428bca; border-color: #357ebd; }\n";
$content .= ".help-block { margin-left:16.7%;display: block; color:#a94442;} \n";
$content .= "#form input[type='text'] { background-color:#fff;}\n";
$content .= "#form .has-error label { color:#a94442; }\n";
$content .= "#form .has-error .form-control { border-color:#a94442; box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.075) inset; }\n";
$content .= ".form-group { margin-bottom: 15px; }\n";
$content .= ".form-horizontal .control-label { text-align: right; }\n";
$content .= "label { display: inline-block; font-weight: 700; }\n";
$content .= ".form-control { display: block; border-radius: 4px;padding-top: 6px; padding-right: 12px; padding-bottom: 6px; padding-left: 12px; width: 80%; }\n";
$content .= "* { box-sizing: border-box;}\n";
$content .= "button, input, optgroup, select, textarea { margin:0px 0px 0px 0px; }\n";
$content .= "#form .form-control[disabled], #form .form-control[readonly], #form fieldset[disabled] .form-control { cursor: not-allowed; background-color: #eee; opacity: 1; }\n";
$content .= "</style>\n";
// CSS is in edit an id too !
}
$f = new form ();
$fields = array ();
foreach ($titles as $key=>$val)
{
$field = new formfield ($key, $val);
if (! isset ($this->objectDB->fields[$key]))
throw new Exception (sprintf (dgettext("domframework",
"Field '%s' (defined in titles) not found in fields"),
$key), 500);
if (in_array ("not null", $this->objectDB->fields[$key]))
$field->mandatory = true;
if (in_array ("autoincrement", $this->objectDB->fields[$key]))
$field->type = "hidden";
if ($key === $this->chainedForeign)
{
$field->defaults = $chain;
$field->readonly = true;
}
$fields[] = $field;
unset ($field);
}
$field = new formfield ("submit", dgettext("domframework",
"Save the datas"));
$field->defaults = dgettext("domframework","Save the datas");
$field->type = "submit";
$fields[] = $field;
unset ($field);
$f->fields ($fields);
$content .= $f->printHTML ("post", $values, $errors);
$this->rendererhtml ($content);
});
$route->post ($this->url_prefix."/add", function ($chain) use ($route)
{
// Add a new entry : effective save of the datas
if ($this->chained !== null)
{
if ($this->chained->editright ($chain) !== TRUE)
{
if ($this->auth["email"] === "anonymous")
throw new Exception (dgettext("domframework",
"Anonymous not allowed"), 401);
throw new Exception (dgettext("domframework","Access forbidden"),
403);
}
$this->chained->connect();
// $chainedvalues are the informations associated to the $chain
$chainedvalues = $this->chained->keyexists ($chain);
if ($chainedvalues === false)
throw new exception (dgettext("domframework",
"Object not found"), 404);
}
if ($this->accessright () !== TRUE)
{
if ($this->auth["email"] === "anonymous")
throw new Exception (dgettext("domframework","Anonymous not allowed"),
401);
throw new Exception (dgettext("domframework","Access forbidden"), 403);
}
if ($this->editright () !== TRUE)
{
if ($this->auth["email"] === "anonymous")
throw new Exception (dgettext("domframework","Anonymous not allowed"),
401);
throw new Exception (dgettext("domframework","Access forbidden"), 403);
}
if ($this->readonly () === TRUE)
throw new Exception (dgettext("domframework","Access forbidden"), 403);
$this->connect();
$f = new form ();
$values = $f->values ();
$errors = $this->objectDB->verify ($values);
if (count ($errors) == 0)
{
try
{
$this->objectDB->insert ($values);
$renderer = new renderer ();
$renderer->flash ("SUCCESS", dgettext("domframework",
"Creation done"));
$route->redirect ("/".
str_replace ("{chain}", $chain, $this->url_prefix),
"");
}
catch (Exception $e)
{
$renderer = new renderer ();
$renderer->flash ("ERROR", $e->getMessage ());
}
}
else
{
$renderer = new renderer ();
foreach ($errors as $error)
$renderer->flash (strtoupper ($error[0]), $error[1]);
}
// If errors : save them and redirect to the page of editing to be
// corrected
$_SESSION["domframework"]["routeSQL"]["errors"] = $errors;
$_SESSION["domframework"]["routeSQL"]["values"] = $values;
$route->redirect ("/".str_replace("{chain}", $chain, $this->url_prefix).
"/add", "");
});
$route->get ($this->url_prefix."/{id}", function ($id, $chain)
{
// List the details of one existing object
if ($this->chained !== null)
{
if ($this->chained->accessright ($chain) !== TRUE)
{
if ($this->auth["email"] === "anonymous")
throw new Exception (dgettext("domframework",
"Anonymous not allowed"), 401);
throw new Exception (dgettext("domframework","Access forbidden"),
403);
}
$this->chained->connect();
// $chainedvalues are the informations associated to the $chain
$chainedvalues = $this->chained->keyexists ($chain);
if ($chainedvalues === false)
throw new exception (dgettext("domframework",
"Object not found"), 404);
}
if ($this->accessright ($id) !== TRUE)
{
if ($this->auth["email"] === "anonymous")
throw new Exception (dgettext("domframework","Anonymous not allowed"),
401);
throw new Exception (dgettext("domframework","Access forbidden"), 403);
}
if ($this->chained !== null &&
$this->chained->editright ($chain) !== true)
$this->readwriteAllowed = false;
if ($this->readwriteAllowed === true)
$this->readwriteAllowed = $this->editright ($id);
$readonly = $this->readonly ($id);
$this->connect();
$content = $this->showflash ();
$values = array ();
$errors = array();
$titles = $this->objectDB->titles ();
$values = $this->objectDB->read (array (array ($this->objectDB->primary,
$id)));
if (count ($values) === 0)
throw new Exception (dgettext("domframework", "Object not found"), 404);
$values = $values[0];
if (isset ($_SESSION["domframework"]["routeSQL"]["errors"]))
{
$errors = $_SESSION["domframework"]["routeSQL"]["errors"];
unset ($_SESSION["domframework"]["routeSQL"]["errors"]);
}
if (isset ($_SESSION["domframework"]["routeSQL"]["values"]))
{
$values = $_SESSION["domframework"]["routeSQL"]["values"];
unset ($_SESSION["domframework"]["routeSQL"]["values"]);
}
if ($this->enableInternalCSS === true)
{
// CSS is in add too !
$content .= "<style type='text/css' scoped='scoped'>\n";
$content .= "#form .col-sm-2 { float:left; width: 16.66666667%; padding-left:15px;padding-right:15px;};\n";
$content .= "#form .col-sm-10 {float:left; width: 83.33333333%; }\n";
$content .= "#form .col-sm-12 { width: 100%; }\n";
$content .= "#form input[type='submit'] { width:93%; margin-left:auto;margin-right:auto}\n";
$content .= "#form .btn-primary { color: #fff; background-color: #428bca; border-color: #357ebd; }\n";
$content .= ".help-block { margin-left:16.7%;display: block; color:#a94442;} \n";
$content .= "#form input[type='text'] { background-color:#fff;}\n";
$content .= "#form .has-error label { color:#a94442; }\n";
$content .= "#form .has-error .form-control { border-color:#a94442; box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.075) inset; }\n";
$content .= ".form-group { margin-bottom: 15px; }\n";
$content .= ".form-horizontal .control-label { text-align: right; }\n";
$content .= "label { display: inline-block; font-weight: 700; }\n";
$content .= ".form-control { display: block; border-radius: 4px;padding-top: 6px; padding-right: 12px; padding-bottom: 6px; padding-left: 12px; width: 80%; }\n";
$content .= "* { box-sizing: border-box;}\n";
$content .= "button, input, optgroup, select, textarea { margin:0px 0px 0px 0px; }\n";
$content .= "#form .form-control[disabled], #form .form-control[readonly], #form fieldset[disabled] .form-control { cursor: not-allowed; background-color: #eee; opacity: 1; }\n";
$content .= "</style>\n";
// CSS is in add too !
}
$f = new form ();
$fields = array ();
foreach ($titles as $key=>$val)
{
$field = new formfield ($key, $val);
if (! isset ($this->objectDB->fields[$key]))
throw new Exception (sprintf (dgettext("domframework",
"Field '%s' (defined in titles) not found in fields"),
$key), 500);
if (in_array ("not null", $this->objectDB->fields[$key]))
$field->mandatory = true;
if (in_array ("autoincrement", $this->objectDB->fields[$key]))
$field->type = "hidden";
if ($readonly === true || $this->readwriteAllowed === false)
$field->readonly = true;
if ($key === $this->chainedForeign)
{
$field->defaults = $chain;
$field->readonly = true;
}
$fields[] = $field;
unset ($field);
}
if ($readonly === false && $this->readwriteAllowed === true)
{
$field = new formfield ("submit", dgettext("domframework",
"Save the datas"));
$field->defaults = dgettext("domframework","Save the datas");
$field->type = "submit";
$fields[] = $field;
unset ($field);
}
$f->fields ($fields);
$content .= $f->printHTML ("post", $values, $errors);
$this->rendererhtml ($content);
});
$route->post ($this->url_prefix."/{id}", function ($id, $chain) use ($route)
{
// Save the details of one existing object
if ($this->chained !== null)
{
if ($this->chained->editright ($chain) !== TRUE)
{
if ($this->auth["email"] === "anonymous")
throw new Exception (dgettext("domframework",
"Anonymous not allowed"), 401);
throw new Exception (dgettext("domframework","Access forbidden"),
403);
}
$this->chained->connect();
// $chainedvalues are the informations associated to the $chain
$chainedvalues = $this->chained->keyexists ($chain);
if ($chainedvalues === false)
throw new exception (dgettext("domframework",
"Object not found"), 404);
}
if ($this->accessright ($id) !== TRUE)
{
if ($this->auth["email"] === "anonymous")
throw new Exception (dgettext("domframework","Anonymous not allowed"),
401);
throw new Exception (dgettext("domframework","Access forbidden"), 403);
}
if ($this->editright ($id) !== TRUE)
{
if ($this->auth["email"] === "anonymous")
throw new Exception (dgettext("domframework","Anonymous not allowed"),
401);
throw new Exception (dgettext("domframework","Access forbidden"), 403);
}
if ($this->readonly ($id) === TRUE)
throw new Exception (dgettext("domframework","Access forbidden"), 403);
$this->connect();
$oldvalues = $this->objectDB->read (array (array
($this->objectDB->primary, $id)));
if (count ($oldvalues) === 0)
throw new Exception (dgettext("domframework", "Object not found"), 404);
$oldvalues = $oldvalues[0];
$f = new form ();
$values = $f->values ();
if ($values[$this->objectDB->primary] !== $id)
throw new Exception (dgettext("domframework",
"Can not change the primary key"), 403);
$errorsChain = array ();
if ($this->chainedForeign !== null &&
$values[$this->chainedForeign] !== $chain)
$errorsChain[$this->chainedForeign] =
array ("error", dgettext("domframework",
"Can not change the external key"));
$errors = $this->objectDB->verify ($values, $id);
if (count ($errors) == 0 && count ($errorsChain) == 0)
{
try
{
$this->objectDB->update ($id, $values);
$renderer = new renderer ();
$renderer->flash ("SUCCESS", dgettext("domframework","Update done"));
$route->redirect ("/".
str_replace ("{chain}", $chain, $this->url_prefix),
"");
}
catch (Exception $e)
{
$renderer = new renderer ();
$renderer->flash ("ERROR", $e->getMessage ());
}
}
else
{
$renderer = new renderer ();
foreach ($errors as $error)
$renderer->flash (strtoupper ($error[0]), $error[1]);
foreach ($errorsChain as $error)
$renderer->flash (strtoupper ($error[0]), $error[1]);
}
// If errors : save them and redirect to the page of editing to be
// corrected
$_SESSION["domframework"]["routeSQL"]["errors"] = $errors;
$_SESSION["domframework"]["routeSQL"]["values"] = $values;
$route->redirect ("/".str_replace ("{chain}", $chain, $this->url_prefix).
"/$id", "");
});
}
/** Authorization : Return TRUE if the user right allow to see the datas
Return FALSE else */
public function accessright ($id=null)
{
// echo "accessright=".var_export ($id, TRUE)." for ".
// var_export($this->model_class, TRUE)."\n";
if ($this->authorization !== null)
{
$result = $this->authorization->allow ($this->module,
$this->auth["email"], "/$id");
// echo "RESULT=$result\n";
if ($result === "RO") return TRUE;
if ($result === "RW") return TRUE;
return FALSE;
}
return TRUE;
}
/** Authorization : Return TRUE if the user right allow to edit the datas
Return FALSE else */
public function editright ($id=null)
{
// echo "editright=".var_export ($id, TRUE)." for ".
// var_export($this->model_class, TRUE)."\n";
if ($this->authorization !== null)
{
$result = $this->authorization->allow ($this->module,
$this->auth["email"], "/$id");
// echo "RESULT=$result\n";
if ($result === "RW") return TRUE;
return FALSE;
}
return TRUE;
}
/** Authorization : Return TRUE if the $id is in READONLY for the user or
FALSE if the user have the RW rights */
public function readonly ($id=null)
{
// echo "readonly=".var_export ($id, TRUE)." for ".
// var_export($this->model_class, TRUE)."\n";
if ($this->authorization !== null)
{
$result = $this->authorization->allow ($this->module,
$this->auth["email"], "/$id");
// echo "RESULT=$result\n";
if ($result === "RO") return TRUE;
return FALSE;
}
return FALSE;
}
/** Return the datas of the row if the $id exists in the primary key of the
table
Return FALSE in the other cases */
public function keyexists ($id)
{
$datas = $this->objectDB->read (array (array ($this->objectDB->primary,
$id)));
if (count ($datas) > 0)
return $datas[0];
return FALSE;
}
/** Display the data in HTML with the view class/method if they are defined */
private function rendererhtml ($data)
{
require_once ("domframework/outputhtml.php");
$route = new route ();
$html = new outputhtml ();
$replacement = array ("{baseurl}"=>$route->baseURL ());
if ($this->rendererHTMLlayout === false)
{
$this->rendererHTMLlayout = "<!DOCTYPE html>
<html>
<head>
<title>{title}</title>
<meta http-equiv='Content-Type' content='text/html; charset=UTF-8'/>
</head>
<body>
{content}
</body>
</html>";
}
// TODO : Test the $this->rendererHTMLclass, $this->rendererHTMLmethod !
echo $html->out ($data, FALSE,
$this->rendererHTMLclass, $this->rendererHTMLmethod,
$this->rendererHTMLlayout, $replacement);
}
}