Working on web admin

Former-commit-id: 251cc1989b
This commit is contained in:
Jérôme Schneider 2012-03-29 14:14:01 +02:00
parent 0ec2777af0
commit 4a8453dd0d
20 changed files with 382 additions and 55 deletions

View file

@ -5,7 +5,7 @@ namespace Baikal\Core;
class ClassLoader { class ClassLoader {
public static function register() { public static function register() {
return spl_autoload_register(array(get_called_class(), 'loadClass')); return spl_autoload_register(array(__CLASS__, 'loadClass'));
} }
public static function loadClass($sFullClassName) { public static function loadClass($sFullClassName) {

View file

@ -7,10 +7,21 @@ class Form extends \Flake\Core\Controler {
const BASEPATH = "/admin/"; const BASEPATH = "/admin/";
protected $aMessages = array(); protected $aMessages = array();
function execute() { public function __construct() {
parent::__construct();
if(($iUser = self::editRequested()) !== FALSE) { if(($iUser = self::editRequested()) !== FALSE) {
if(self::editSubmitted()) { $this->oModel = new \BaikalAdmin\Model\User($iUser);
}
$this->initForm();
}
function execute() {
if(($iUser = self::editRequested()) !== FALSE) {
if($this->oForm->submitted()) {
$this->oForm->execute();
/*
$aPost = \Flake\Util\Tools::POST(); $aPost = \Flake\Util\Tools::POST();
$aErrors = array(); $aErrors = array();
@ -44,7 +55,7 @@ class Form extends \Flake\Core\Controler {
"Changes on <i class='icon-user'></i> <strong>" . $oUser->get("username") . "</strong> have been saved." "Changes on <i class='icon-user'></i> <strong>" . $oUser->get("username") . "</strong> have been saved."
); );
} }
*/
} }
} }
@ -78,6 +89,20 @@ class Form extends \Flake\Core\Controler {
} }
} }
function initForm() {
$aOptions = array(
"closeurl" => $this::BASEPATH
);
if($this->editRequested()) {
$this->oForm = $this->oModel->formForInstance($aOptions);
} else {
$this->oForm = \BaikalAdmin\Model\User::formEmpty(array(
"closeurl" => $this::BASEPATH
));
}
}
public static function editRequested() { public static function editRequested() {
if(($iUser = intval(\Flake\Util\Tools::GET("useredit"))) > 0) { if(($iUser = intval(\Flake\Util\Tools::GET("useredit"))) > 0) {
return $iUser; return $iUser;
@ -86,12 +111,6 @@ class Form extends \Flake\Core\Controler {
return FALSE; return FALSE;
} }
public static function editSubmitted() {
return self::editRequested() && (
intval(\Flake\Util\Tools::POST("formedit-submitted")) === 1
);
}
public static function deleteRequested() { public static function deleteRequested() {
if(($iUser = intval(\Flake\Util\Tools::GET("userdel"))) > 0) { if(($iUser = intval(\Flake\Util\Tools::GET("userdel"))) > 0) {
return $iUser; return $iUser;
@ -124,7 +143,9 @@ class Form extends \Flake\Core\Controler {
$oView->setData("user", $oUser); $oView->setData("user", $oUser);
$oView->setData("messages", $sMessages); $oView->setData("messages", $sMessages);
$sHtml .= $this->oForm->render();
$sHtml .= $oView->render(); $sHtml .= $oView->render();
} else { } else {
$sHtml .= $sMessages; $sHtml .= $sMessages;
} }

View file

@ -8,6 +8,9 @@ require_once(dirname(dirname(dirname(__FILE__))) . "/Baikal/Core/Bootstrap.php")
# Bootstrap Flake # Bootstrap Flake
require_once(dirname(dirname(dirname(__FILE__))) . "/Flake/Core/Bootstrap.php"); require_once(dirname(dirname(dirname(__FILE__))) . "/Flake/Core/Bootstrap.php");
# Bootstrap Formal
require_once(dirname(dirname(dirname(__FILE__))) . "/Formal/Core/Bootstrap.php");
# Registering BaikalAdmin classloader # Registering BaikalAdmin classloader
require_once(dirname(__FILE__) . '/ClassLoader.php'); require_once(dirname(__FILE__) . '/ClassLoader.php');
\BaikalAdmin\Core\ClassLoader::register(); \BaikalAdmin\Core\ClassLoader::register();

View file

@ -5,7 +5,7 @@ namespace BaikalAdmin\Core;
class ClassLoader { class ClassLoader {
public static function register() { public static function register() {
return spl_autoload_register(array(get_called_class(), 'loadClass')); return spl_autoload_register(array(__CLASS__, 'loadClass'));
} }
public static function loadClass($sFullClassName) { public static function loadClass($sFullClassName) {

View file

@ -5,6 +5,7 @@ namespace BaikalAdmin\Model;
class User extends \Flake\Core\Model\Db { class User extends \Flake\Core\Model\Db {
const DATATABLE = "users"; const DATATABLE = "users";
const PRIMARYKEY = "id"; const PRIMARYKEY = "id";
const LABELFIELD = "username";
protected $oIdentityPrincipal = null; protected $oIdentityPrincipal = null;
@ -56,4 +57,37 @@ class User extends \Flake\Core\Model\Db {
public function getMailtoURI() { public function getMailtoURI() {
return "mailto:" . rawurlencode($this->get("displayname") . " <" . $this->get("email") . ">"); return "mailto:" . rawurlencode($this->get("displayname") . " <" . $this->get("email") . ">");
} }
# Empty form,
public static function formEmpty($options = array()) {
$sClass = get_called_class();
$oForm = new \Formal\Core\Form($sClass, $options);
$oForm->add(new \Formal\Element\Text(array(
"prop" => "username",
"label" => "Username",
"validation" => "required"
)));
$oForm->add(new \Formal\Element\Text(array(
"prop" => "displayname",
"label" => "Display name",
"validation" => "required"
)));
$oForm->add(new \Formal\Element\Text(array(
"prop" => "email",
"label" => "Email",
"validation" => "required,email"
)));
return $oForm;
}
public function formForInstance($options = array()) {
$oForm = self::formEmpty($options)->setModelInstance($this);
$oForm->element("username")->setOption("readonly", true);
return $oForm;
}
} }

View file

@ -1,30 +1 @@
<form class="form-horizontal" action="<?= $action ?>" method="post" enctype="multipart/formdata">
<input type="hidden" name="formedit-submitted" value="1" />
<fieldset>
<legend>Editing user <strong><?= $user->get("username") ?></strong></legend>
<?= $messages ?> <?= $messages ?>
<div class="control-group">
<label class="control-label" for="disabledInput">Username</label>
<div class="controls">
<input class="input-xlarge disabled" id="username" type="text" placeholder="<?= htmlspecialchars($user->get("username")) ?>" disabled="" />
<!--p class="help-block">The username is readonly, and may not be changed.</p-->
</div>
</div>
<div class="control-group">
<label class="control-label" for="displayname">Display name</label>
<div class="controls">
<input type="text" class="input-xlarge" id="displayname" name="displayname" value="<?= htmlspecialchars($user->get("displayname")) ?>"/>
</div>
</div>
<div class="control-group">
<label class="control-label" for="displayname">Email</label>
<div class="controls">
<input type="text" class="input-xlarge" id="email" name="email" value="<?= htmlspecialchars($user->get("email")) ?>"/>
</div>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">Save changes</button>
<a class="btn" href="<?= $linkcancel ?>">Close</a>
</div>
</fieldset>
</form>

View file

@ -29,6 +29,7 @@ require_once(FLAKE_PATH_ROOT . 'Core/ClassLoader.php');
require_once(FLAKE_PATH_ROOT . "config.php"); require_once(FLAKE_PATH_ROOT . "config.php");
if(!\Flake\Util\Tools::isCliPhp()) { if(!\Flake\Util\Tools::isCliPhp()) {
ini_set("html_errors", TRUE);
session_start(); session_start();
\Flake\Util\Tools::decode_GET(); \Flake\Util\Tools::decode_GET();
} }

View file

@ -5,7 +5,7 @@ namespace Flake\Core;
class ClassLoader { class ClassLoader {
public static function register() { public static function register() {
return spl_autoload_register(array(get_called_class(), 'loadClass')); return spl_autoload_register(array(__CLASS__, 'loadClass'));
} }
public static function loadClass($sFullClassName) { public static function loadClass($sFullClassName) {

View file

@ -27,6 +27,15 @@ class Collection extends \Flake\Core\FLObject implements \Iterator {
return ($key !== NULL && $key !== FALSE); return ($key !== NULL && $key !== FALSE);
} }
public function getForKey($sKey) {
$aKeys = $this->keys();
if(!in_array($sKey, $aKeys)) {
throw new \Exception("\Flake\Core\Collection->getForKey(): key '" . $sKey . "' not found in Collection");
}
return $this->aCollection[$sKey];
}
public function &each() { public function &each() {
list($key, $val) = each($this->aCollection); list($key, $val) = each($this->aCollection);
return $val; return $val;
@ -127,6 +136,14 @@ class Collection extends \Flake\Core\FLObject implements \Iterator {
return $this->aCollection; return $this->aCollection;
} }
# Create a new collection like this one
# This abstraction is useful because of CollectionTyped
protected function newCollectionLikeThisOne() {
$oCollection = \Flake\Core\Collection();
return $oCollection;
}
/* /*
* Méthode magique __call * Méthode magique __call
* *

View file

@ -30,4 +30,12 @@ class CollectionTyped extends \Flake\Core\Collection {
parent::push($mMixed); parent::push($mMixed);
} }
# Create a new collection like this one
# This abstraction is useful because of CollectionTyped
protected function newCollectionLikeThisOne() {
$oCollection = \Flake\Core\CollectionTyped($this->sTypeClassOrProtocol);
return $oCollection;
}
} }

View file

@ -7,8 +7,16 @@ class FLObject {
return print_r($this, TRUE); return print_r($this, TRUE);
} }
public static function getClass() { public function getClass() {
// throw new \Exception("getClass() is deprecated");
if(!isset($this)) {
# echo "STATIC<br />";
return get_called_class(); return get_called_class();
} else {
# echo "INSTANCE<br />";
# debug($this);
return get_class($this);
}
} }
public function isA($sClassOrProtocolName) { public function isA($sClassOrProtocolName) {

View file

@ -26,4 +26,8 @@ abstract class Model extends \Flake\Core\FLObject {
throw new \Exception("\Flake\Core\Model->set(): property " . htmlspecialchars($sPropName) . " does not exist on " . self::getClass()); throw new \Exception("\Flake\Core\Model->set(): property " . htmlspecialchars($sPropName) . " does not exist on " . self::getClass());
} }
public function getLabel() {
return $this->get($this::LABELFIELD);
}
} }

View file

@ -0,0 +1,9 @@
<?php
namespace Formal;
class Core {
public static function handle($sName) {
debug("Handling " . $sName);
}
}

View file

@ -0,0 +1,7 @@
<?php
define("FORMAL_PATH_ROOT", dirname(dirname(__FILE__)) . "/");
# Registering BaikalAdmin classloader
require_once(dirname(__FILE__) . '/ClassLoader.php');
\Formal\Core\ClassLoader::register();

View file

@ -0,0 +1,41 @@
<?php
namespace Formal\Core;
class ClassLoader {
public static function register() {
return spl_autoload_register(array(__CLASS__, 'loadClass'));
}
public static function loadClass($sFullClassName) {
$aParts = explode("\\", $sFullClassName);
if(count($aParts) === 1) {
return;
}
if($aParts[0] !== "Formal") {
return;
}
// ejecting the Radical
$sRadical = array_shift($aParts);
if($sRadical === "Formal") {
$sRootPath = FORMAL_PATH_ROOT;
}
$sClassName = array_pop($aParts);
$sBasePath = $sRootPath . implode("/", $aParts) . "/";
$sClassPath = $sBasePath . $sClassName . ".php";
if(file_exists($sClassPath) && is_readable($sClassPath)) {
require_once($sClassPath);
} else {
echo '<h1>PHP Autoload Error. Cannot find ' . $sFullClassName . '</h1>';
echo "<pre>" . print_r(debug_backtrace(), TRUE) . "</pre>";
die();
}
}
}

View file

@ -0,0 +1,139 @@
<?php
namespace Formal\Core;
class Form {
protected $sModelClass = "";
protected $aOptions = array();
protected $oModelInstance = null;
protected $oElements = null;
public function __construct($sModelClass, $aOptions = array()) {
$this->sModelClass = $sModelClass;
$this->aOptions = array_merge($this->aOptions, $aOptions);
$this->oElements = new \Flake\Core\CollectionTyped("\Formal\Element");
}
public function option($sName) {
return $this->aOptions[$sName];
}
public function setModelInstance($oModelInstance) {
if(!\Flake\Util\Tools::is_a($oModelInstance, $this->sModelClass)) {
throw new \Exception("\Formal\Core->setModelInstance(): Given instance is not of class '" . $this->sModelClass . "'");
}
$this->oModelInstance = $oModelInstance;
$this->oElements->reset();
foreach($this->oElements as $oElement) {
$oElement->setValue(
$this->getModelInstance()->get(
$oElement->option("prop")
)
);
}
return $this;
}
public function getModelInstance() {
return $this->oModelInstance;
}
public function hasModelInstance() {
return !is_null($this->getModelInstance());
}
public function add(\Formal\Element $oElement) {
$this->oElements->push($oElement);
}
public function element($sPropName) {
$this->oElements->reset();
foreach($this->oElements as $oElement) {
if($oElement->option("prop") === $sPropName) {
return $oElement;
}
}
throw new \Exception("\Formal\Core\Form->element(): Element prop='" . $sPropName . "' not found");
}
public function execute() {
if(!$this->hasModelInstance()) {
return;
}
$this->oElements->reset();
foreach($this->oElements as $oElement) {
# If element is readonly, skip process
if($oElement->option("readonly")) {
continue;
}
$sPropName = $oElement->option("prop");
# posted value is fetched, then passes to element before persistance
$sPostValue = $this->postValue($sPropName);
$oElement->setValue($sPostValue);
$this->getModelInstance()->set(
$sPropName,
$oElement->value()
);
}
$this->getModelInstance()->persist();
}
public function postValue($sPropName) {
# could be as well \Flake\Util\Tools::POST($sPropName)
return \Flake\Util\Tools::POST($this->element($sPropName)->option("prop"));
}
public function render() {
$aHtml = array();
$this->oElements->reset();
foreach($this->oElements as $oElement) {
$aHtml[] = $oElement->render();
}
$elements = implode("\n", $aHtml);
if($this->hasModelInstance()) {
$sTitle = "Editing " . $this->getHumanModelName() . " <strong>" . $this->getModelInstance()->getLabel() . "</strong>";
} else {
$sTitle = "Creating new " . $this->getHumanModelName();
}
$sSubmittedFlagName = $this->sModelClass . "::submitted";
$sCloseUrl = $this->option("closeurl");
$sHtml =<<<HTML
<form class="form-horizontal" action="" method="post" enctype="multipart/formdata">
<input type="hidden" name="{$sSubmittedFlagName}" value="1" />
<fieldset>
<legend>{$sTitle}</legend>
{$elements}
<div class="form-actions">
<button type="submit" class="btn btn-primary">Save changes</button>
<a class="btn" href="{$sCloseUrl}">Close</a>
</div>
</fieldset>
</form>
HTML;
return $sHtml;
}
public function submitted() {
return intval(\Flake\Util\Tools::POST($this->sModelClass . "::submitted")) === 1;
}
public function getHumanModelName() {
return array_pop(explode("\\", $this->sModelClass));
}
}

View file

@ -0,0 +1,35 @@
<?php
namespace Formal;
class Element {
protected $aOptions = array(
"readonly" => FALSE,
);
protected $sValue = "";
public function __construct($aOptions) {
$this->aOptions = array_merge($aOptions, $this->aOptions);
}
public function option($sName) {
if(array_key_exists($sName, $this->aOptions)) {
return $this->aOptions[$sName];
}
throw new \Exception("\Formal\Element->option(): Option '" . htmlspecialchars($sName) . "' not found.");
}
public function setOption($sOptionName, $sOptionValue) {
$this->aOptions[$sOptionName] = $sOptionValue;
}
public function value() {
return $this->sValue;
}
public function setValue($sValue) {
$this->sValue = $sValue;
}
}

View file

@ -0,0 +1,29 @@
<?php
namespace Formal\Element;
class Text extends \Formal\Element {
public function render() {
$value = htmlspecialchars($this->value());
$label = $this->option("label");
$prop = $this->option("prop");
$disabled = "";
$class = "";
if($this->option("readonly") === TRUE) {
$class = " disabled";
$disabled = " disabled";
}
$sHtml =<<<HTML
<div class="control-group">
<label class="control-label" for="displayname">{$label}</label>
<div class="controls">
<input type="text" class="input-xlarge{$sClass}" id="{$prop}" name="{$prop}" value="{$value}"{$disabled} />
</div>
</div>
HTML;
return $sHtml;
}
}

Binary file not shown.