Commit 1a417720 authored by Denis S. Valdenaire's avatar Denis S. Valdenaire

Merge branch 'complete_templating' into 'master'

Complete templating

C'est suffisamment avancé, on peut à nouveau retourner dans le master.

See merge request !3
parents 0ad01526 68976edc
<?php
session_start();
function myautoload($class_name) {
if(strpos($class_name, "Controller")) {
include "controllers/". //strtolower(substr($class_name, 0, -10)).".php";
......@@ -14,6 +13,7 @@ spl_autoload_register("myautoload");
include("config/config.php");
global $data;
$data = new data();
$session_db = new session_db();
global $logged_user;
$logged_user = new User(0);
if(!array_key_exists("user_id", $_SESSION)) {
......@@ -33,4 +33,3 @@ if(array_key_exists("o", $_REQUEST) && $_REQUEST["o"] != ""
} else {
header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request', true, 400);
}
?>
......@@ -97,6 +97,7 @@ class data {
public function insert ($query) {
if (!$this->db_handle->query($query)) {
trigger_error($query, E_USER_NOTICE);
throw new data_exception(
mysqli_errno($this->db_handle),
mysqli_error($this->db_handle),
......
<?php
class Game extends Record {
public $id;
public $name, $reference, $maker, $category, $esar_category_id;
public $comments, $maker_info, $content_inventory, $aquisition_date, $price;
public $players_min, $players_max, $age_min, $age_max, $game_type;
public $id;
public $name, $reference, $maker, $category, $esar_category_id;
public $comments, $maker_info, $content_inventory, $aquisition_date, $price;
public $players_min, $players_max, $age_min, $age_max, $game_type;
// array containing the medias associated with the game
public $medias;
// loan history
public $loans;
public $table = "games";
public $table = "games";
public function __construct($id = 0) {
if (!$this->id) {
$this->id = $id;
}
}
public function __construct($id = 0) {
if (!$this->id) {
$this->id = $id;
}
}
public static function fetch($id) {
// SQL SELECT games prets
$sql = "SELECT games.id, name, reference, maker, category, esar_category_id,
comments, maker_info, content_inventory, DATE_FORMAT(aquisition_date, '%m/%d/%Y') as aquisition_date,
price, players_min, players_max,
age_min, age_max, game_type, loans.id as loan_id, DATE_FORMAT(loans.end_date, '%d/%m/%Y') AS loan_end_date
FROM games
LEFT OUTER JOIN loans ON (games.id = loans.game_id AND loans.is_back = 0)
WHERE games.id = ".$id;
$sql = "SELECT g.id, g.name, g.reference, g.maker, g.category, g.esar_category_id,
g.comments, g.maker_info, g.content_inventory,
DATE_FORMAT(g.aquisition_date, '%m/%d/%Y') as aquisition_date,
g.price, g.players_min, g.players_max,
g.age_min, g.age_max, g.game_type,
l.id as loan_id, l.end_date AS loan_end_date,
r.id AS reservation_id, r.member_id, r.reservation_date,
CONCAT(m.firstname, ' ', m.lastname) AS reservation_member_name
FROM games g
LEFT OUTER JOIN loans l ON (g.id = l.game_id AND l.is_back = 0)
LEFT OUTER JOIN reservations r ON (g.id = r.game_id)
LEFT OUTER JOIN members m ON (r.member_id = m.id)
WHERE g.id = ".$id;
$GLOBALS["data"]->select($sql, $game, "Game");
return $game;
}
......@@ -34,30 +42,44 @@ class Game extends Record {
echo json_encode($this);
}
// probably not used - done in javascript on the edit view for the game
// probably not used - done in javascript on the edit view for the game
public function fetch_medias() {
$this->medias = array();
Media::fetch_all($this->medias, $this->id);
Media::fetch_all($this->medias, $this->id);
return sizeof($this->medias);
}
public function fetch_loans() {
$this->loans = array();
Loan::fetch_loans($this->loans, $this->id);
return sizeof($this->loans);
}
public function create_reservation() {
$reservation = new Reservation();
$reservation->create();
return $reservation->id;
}
public function delete_reservation() {
Reservation::delete_game_reservation($GLOBALS["data"]->db_escape_string($_REQUEST["i"]));
}
public static function fetch_all(&$games) {
$games = array();
$where_clause = "";
if(array_key_exists("filter", $_REQUEST) && $_REQUEST["filter"] == "available") {
$where_clause = "WHERE l.id IS NULL";
}
$where_clause = "";
if(array_key_exists("filter", $_REQUEST) && $_REQUEST["filter"] == "available") {
$where_clause = "WHERE l.id IS NULL";
}
$sql = "SELECT g.id, g.name,
CONCAT (ec.label, ' - ', ec.name) AS label,
l.id as loan_status
FROM games g
LEFT OUTER JOIN esar_categories ec ON g.esar_category_id = ec.id
LEFT OUTER JOIN loans l ON (g.id = l.game_id AND l.is_back = 0)
$where_clause
$where_clause
ORDER BY g.name";
$GLOBALS["data"]->select($sql, $games, "Game");
return sizeof($games);
}
}
?>
......@@ -51,6 +51,20 @@ class Loan extends Record {
return sizeof($loans);
}
public static function fetch_loans(&$loans, $id) {
// SQL SELECT loans members
$sql = " SELECT l.id, start_date, end_date, is_back, l.created_at, l.updated_at,
CASE WHEN (end_date < curdate() AND is_back = 0) THEN 1 ELSE 0 END AS is_late,
l.member_id, CONCAT(m.firstname, ' ', m.lastname) AS member_name
FROM loans l, members m
WHERE l.game_id = $id
AND l.member_id = m.id
ORDER BY start_date DESC ";
$GLOBALS["data"]->select($sql, $loans, "Loan", true);
return sizeof($loans);
}
public static function fetch($id) {
// SQL SELECT loans
......@@ -87,5 +101,3 @@ class Loan extends Record {
return $GLOBALS["data"]->update($sql);
}
}
?>
......@@ -145,7 +145,7 @@ class Member extends Record {
}
public function update_loan() {
$loan = Loan::fetch($GLOBALS["data"]->db_escape_string($_REQUEST["i"]));
$loan = Loan::fetch($GLOBALS["data"]->db_escape_string($_REQUEST["loan_id"]));
$loan->update();
}
......
<?php
class Reservation extends Record {
public $id, $game_id, $member_id;
public $id, $game_id, $member_id, $reservation_date;
public $created_at, $updated_at;
public $member_name;
......@@ -24,8 +24,8 @@ class Reservation extends Record {
FROM reservations r, games g, members m
WHERE
g.id = r.game_id
".($member_id == 0 ? "" : "AND l.member_id = ".$member_id)."
AND l.member_id = m.id
".($member_id == 0 ? "" : "AND r.member_id = ".$member_id)."
AND r.member_id = m.id
ORDER BY r.created_at ASC
".($limit != 0 ? "LIMIT 0,$limit" : "");
$GLOBALS["data"]->select($sql, $reservations, "Reservation", true);
......@@ -43,6 +43,13 @@ class Reservation extends Record {
return $reservation;
}
public static function delete_game_reservation($game_id) {
// SQL DELETE reservations
$sql = " DELETE FROM reservations
WHERE game_id = $game_id ";
return $GLOBALS["data"]->delete($sql);
}
public static function delete($id) {
// SQL SELECT reservations
$sql = " SELECT id
......
......@@ -15,8 +15,10 @@ class Role extends Record {
/* fetch ALL the roles, with the selected field = user_id if the
user has the role, NULL otherwise.
Be sure to use user->has_role to check if a user has a role.
FIXME : i can't see the point of doing that
I can't see the point of doing that
Back to a better solution but we'll see that next
OK ! Now i see. I need a list of to modify a user and give him more roles
What i do now is another function for that.
*/
public static function fetch_user_roles($user_id) {
$roles = array();
......@@ -33,6 +35,16 @@ class Role extends Record {
return $roles;
}
public static function fetch_roles_for_user($user_id) {
$roles = array();
// SELECT user_roles roles
$sql = " SELECT r.id, r.name, r.description, ur.user_id AS selected
FROM roles r
LEFT JOIN user_roles ur ON r.id = ur.role_id AND ur.user_id = ".$user_id;
$GLOBALS["data"]->select($sql, $roles, "Role");
return $roles;
}
public static function fetch_all(&$roles) {
$roles = array();
// SQL SELECT roles
......
<?php
// This class store/retrieve the sessions in/from the database.
// You will need this if you have more than one webserver
// to enable the persistence of sessions accross different front servers.
class session_db extends data {
private $life_time = 0;
public function __construct() {
session_set_save_handler(
array(&$this, 'open'),
array(&$this, 'close'),
array(&$this, 'read'),
array(&$this, 'write'),
array(&$this, 'destroy'),
array(&$this, 'gc')
);
$this->connect();
// session_name("whatever");
$this->life_time = ini_get('session.gc_maxlifetime');
// -- Define a lifetime on session cookie
if ( ini_get('session.use_only_cookies') == 1 && intval($this->life_time)>0 ) {
ini_set('session.cookie_secure', FALSE);
ini_set('session.cookie_httponly', TRUE);
session_set_cookie_params($this->life_time);
}
session_start();
}
public function __destruct() {
// DEBUG trigger_error('session_db::__destruct called', E_USER_NOTICE);
}
public function open() {
// DEBUG trigger_error('session_db::open called', E_USER_NOTICE);
// -- Maintain session cookie updated for each requests
if ( ini_get('session.use_only_cookies')==1 && intval($this->life_time)>0 ) {
setcookie(session_name(),session_id(),(time()+$this->life_time), '/');
}
return true;
}
public function close() {
// DEBUG trigger_error('session_db::close called', E_USER_NOTICE);
$this->gc($this->life_time);
return true;
}
public function read($id) {
// DEBUG trigger_error('session_db::read called', E_USER_NOTICE);
// SQL SELECT sessions
$sql = " SELECT session_data
FROM sessions
WHERE session_key = '$id'";
if ( $this->select($sql, $rset) ) {
if ( $rset->numrows ) {
return base64_decode($rset->value("session_data"));
}
}
return '';
}
public function write($id, $data) {
// DEBUG trigger_error('session_db::write called', E_USER_NOTICE);
// SQL INSERT sessions
$sql = " REPLACE INTO sessions
(session_key, session_expires, session_data)
VALUES ('$id', '".(time() + $this->life_time)."',
'".base64_encode($data)."')";
return $this->insert($sql);
}
public function destroy($id, $key_only = FALSE) {
// DEBUG trigger_error('session_db::destroy called', E_USER_NOTICE);
// SQL DELETE sessions
$sql = " DELETE FROM ".$this->tbpx."sessions
WHERE session_key = '".$id."'";
return $this->delete($sql);
}
public function gc($max) {
// DEBUG trigger_error('session_db::gc called', E_USER_NOTICE);
// SQL DELETE sessions
$sql = " DELETE LOW_PRIORITY FROM sessions
WHERE session_expires < ".(time() - $this->life_time);
return $this->delete($sql);
}
}
......@@ -73,5 +73,3 @@ class Subscription extends Record {
}
}
?>
<?php
class User extends Record {
public $id;
public $name, $password_digest, $email, $active;
public $id;
public $name, $password_digest, $email, $active;
public $alert_msg = "";
public $alert_msg = "";
public $roles;
// roles is just an array of role name, not objects
public $roles;
public $table = "users";
public $table = "users";
public function __construct($id = 0) {
if (!$this->id) {
$this->id = $id;
}
$this->roles = array();
}
public function __construct($id = 0) {
if (!$this->id) {
$this->id = $id;
}
$this->roles = array();
}
public static function fetch($id) {
// SQL SELECT users
......@@ -23,51 +24,99 @@ class User extends Record {
FROM users
WHERE id = ".$id;
$GLOBALS["data"]->select($sql, $user, "User");
if($user->id != 0) {
$user->roles = Role::fetch_user_roles($user->id);
}
if($user->id != 0) {
$user->roles = Role::fetch_user_roles($user->id);
}
return $user;
}
public static function fetch_all(&$users) {
public static function fetch_all(&$users) {
$users = array();
// SQL SELECT users
$sql = "SELECT id, name, email, active,
created_at, updated_at
created_at, updated_at
FROM users
ORDER BY name";
$GLOBALS["data"]->select($sql, $users, "User", true);
return sizeof($users);
}
public static function validate($name, $password) {
// SQL SELECT users
$sql = "SELECT id, name, email, active
FROM users
WHERE name = '".$name."'
AND password_digest = '".crypt($name, $password)."'";
$GLOBALS["data"]->select($sql, $user, "User");
if($user->id == 0) {
$user = new User(0);
$user->alert_msg = "Echec de l'authentification";
} else {
$user->roles = Role::fetch_user_roles($user->id);
}
return $user;
}
public static function validate($name, $password) {
// SQL SELECT users
$sql = "SELECT id, name, email, active, password_digest
FROM users
WHERE name = '".$name."'";
$GLOBALS["data"]->select($sql, $user, "User");
if($user->id != 0 && $user->validate_pw($password, $user->password_digest)) {
$user->roles = Role::fetch_user_roles($user->id);
} else {
$user = new User(0);
// user not found
$user->alert_msg = "Echec de l'authentification";
}
return $user;
}
public function update_password() {
$new_password = $this->generate_hash($GLOBALS["data"]->db_escape_string($_REQUEST["password_change"]));
// SQL UPDATE users
$sql = " UPDATE users SET password_digest = '".$new_password."'
WHERE id = ".$this->id;
return $GLOBALS["data"]->update($sql);
}
// give credit where credit is due - cut-and-pasted from
// http://php.net/manual/fr/function.crypt.php#114060
// FIXME : audit this
private function generate_hash($password, $cost=11){
/* To generate the salt, first generate enough random bytes. Because
* base64 returns one character for each 6 bits, the we should generate
* at least 22*6/8=16.5 bytes, so we generate 17. Then we get the first
* 22 base64 characters
*/
$salt = substr(base64_encode(openssl_random_pseudo_bytes(17)),0,22);
/* As blowfish takes a salt with the alphabet ./A-Za-z0-9 we have to
* replace any '+' in the base64 string with '.'. We don't have to do
* anything about the '=', as this only occurs when the b64 string is
* padded, which is always after the first 22 characters.
*/
$salt = str_replace("+",".",$salt);
/* Next, create a string that will be passed to crypt, containing all
* of the settings, separated by dollar signs
*/
$param='$'.implode('$',array(
"2y", //select the most secure version of blowfish (>=PHP 5.3.7)
str_pad($cost,2,"0",STR_PAD_LEFT), //add the cost in two digits
$salt //add the salt
));
//now do the actual hashing
return crypt($password,$param);
}
/*
* Check the password against a hash generated by the generate_hash
* function.
*/
private function validate_pw($password, $hash){
/* Regenerating the with an available hash as the options parameter should
* produce the same hash if the same password is passed.
*/
return crypt($password, $hash)==$hash;
}
public static function fetch_by_name($user) {
public static function fetch_by_name($user) {
// SQL SELECT users
$sql = "SELECT id, name, password_digest, email, active
FROM users
WHERE name = '".$user."'";
WHERE name = '".$user."'";
$GLOBALS["data"]->select($sql, $users, "User");
return sizeof($users);
}
}
public function has_role($role_name) {
public function has_role($role_name) {
return (array_key_exists($role_name, $this->roles));
}
}
public function update_roles() {
if(!array_key_exists("roles", $_REQUEST) || !is_array($_REQUEST["roles"])) {
......@@ -78,15 +127,18 @@ class User extends Record {
} else {
$list_to_delete = "";
while(list($key, $val) = each($this->roles)) {
if($val->selected && !in_array($val->name, $_REQUEST["roles"])) {
$list_to_delete .= $val->id.",";
if(!in_array($key, $_REQUEST["roles"])) {
$list_to_delete .= "'".$key."',";
}
}
reset($this->roles);
if($list_to_delete != "") {
// SQL DELETE user_roles
$sql = " DELETE FROM user_roles WHERE user_id = ".$this->id.
" AND role_id IN ( ".substr($list_to_delete, 0, -1).") ";
// SQL DELETE user_roles JOIN roles
$sql = " DELETE ur
FROM user_roles ur
JOIN roles r ON ur.role_id = r.id
WHERE ur.user_id = ".$this->id."
AND r.name IN ( ".substr($list_to_delete, 0, -1).") ";
$GLOBALS["data"]->delete($sql);
}
$list_to_add = "";
......@@ -99,8 +151,8 @@ class User extends Record {
// SQL INSERT user_roles SELECT roles
$sql = " INSERT INTO user_roles (user_id, role_id, created_at)
SELECT ".$this->id.", id, now()
FROM roles
WHERE name IN (".substr($list_to_add, 0, -1).")";
FROM roles r
WHERE r.name IN (".substr($list_to_add, 0, -1).")";
$GLOBALS["data"]->insert($sql);
}
}
......@@ -108,12 +160,10 @@ class User extends Record {
}
function change_state($new_state) {
// SQL UPDATE users
$sql = " UPDATE users SET active = ".$new_state.",
updated_at = now()
WHERE id = ".$this->id;
return $GLOBALS["data"]->update($sql);
}
// SQL UPDATE users
$sql = " UPDATE users SET active = ".$new_state.",
updated_at = now()
WHERE id = ".$this->id;
return $GLOBALS["data"]->update($sql);
}
}
?>
......@@ -20,6 +20,11 @@ class AppController {
));
$this->context["global"] = $GLOBALS;
$this->context["request"] = $_REQUEST;
if(!method_exists($this, "_".$_REQUEST["a"])) {
$this->render("bad_method");
exit();
}
}
function set($var, &$val) {
......
......@@ -74,12 +74,12 @@ class GamesController extends AppController {
$game = Game::fetch($GLOBALS["data"]->db_escape_string($_REQUEST["i"]));
if($game->id != 0) {
$_REQUEST["a"] = "confirm_delete";
$render = "games/confirm_delete";
return "games/confirm_delete";
} else {
$render = "games/not_found"; // TODO
return "games/not_found"; // TODO
}
} catch(data_exception $e) {
$render = "data_exception";
return "data_exception";
}
return $render;
}
......@@ -90,6 +90,63 @@ class GamesController extends AppController {
return "games/list";
}
function _loans() {
try {
$game = Game::fetch($GLOBALS["data"]->db_escape_string($_REQUEST["i"]));
if($game->id != 0) {
$game->fetch_loans();
$this->set("game", $game);
return "games/loans";
}
return "games/not_found";
} catch (data_exception $e) {
return "data_exception";
}
}
function _reservations() {
try {
$game = Game::fetch($GLOBALS["data"]->db_escape_string($_REQUEST["i"]));
if($game->id != 0) {
$this->set("game", $game);
return "games/reservations";
}
return "games/not_found";
} catch (data_exception $e) {
return "data_exception";
}
}
function _create_reservation() {
try {
$game = Game::fetch($GLOBALS["data"]->db_escape_string($_REQUEST["i"]));
if($game->id != 0) {
$game->create_reservation();
$this->set("game", $game);
$this->set_message("La réservation a été faite");
return "games/edit";
}
return "games/not_found";
} catch (data_exception $e) {
return "data_exception";
}
}
function _delete_reservation() {
try {
$game = Game::fetch($GLOBALS["data"]->db_escape_string($_REQUEST["i"]));
if($game->id != 0) {
$game->delete_reservation();
$this->set("game", $game);
$this->set_message("La réservation a été effacée");
return "games/edit";
}
return "games/not_found";
} catch (data_exception $e) {
return "data_exception";
}
}
function _list() {
try {
Game::fetch_all($games);
......
......@@ -4,8 +4,12 @@ class HomeController extends AppController {