Commit 68976edc authored by Denis S. Valdenaire's avatar Denis S. Valdenaire

fixes #64 - plus d'autres trucs...

parent 222fba83
<?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, loans.end_date AS loan_end_date,
r.id AS reservation_id
FROM games
LEFT OUTER JOIN loans ON (games.id = loans.game_id AND loans.is_back = 0)
LEFT OUTER JOIN reservations r ON (games.id = r.game_id)
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;
}
......@@ -39,10 +42,10 @@ 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);
}
......@@ -58,19 +61,23 @@ class Game extends Record {
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);
......
......@@ -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
......
<?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 = "";
// roles is just an array of role name, not objects
public $roles;
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
......@@ -24,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 static function fetch_by_name($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) {
// 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"])) {
......@@ -112,10 +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);
}
}
......@@ -132,6 +132,21 @@ class GamesController extends AppController {
}
}
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);
......
......@@ -55,6 +55,41 @@ class UsersController extends AppController {
return "users/loginform";
}
function _options_update() {
try {
$user = User::fetch($GLOBALS["data"]->db_escape_string($_REQUEST["i"]));
if($user->id != 0) {
if($user->update()) {
$this->set_message("Les changements ont été enregistrés");
}
if($GLOBALS["data"]->db_escape_string($_REQUEST["password_change"]) != "") {
if($user->update_password()) {
$this->set_message("Le mot de passe a été mis à jour");
}
}
$this->set("user", $user);
return "users/options";
}
return "users/not_found"; // TODO
} catch(data_exception $e) {
return "data_exception";
}
}
function _options() {
try {
$user = User::fetch($GLOBALS["data"]->db_escape_string($_REQUEST["i"]));
if($user->id != 0) {
$this->set("user", $user);
return "users/options";
}
return "users/not_found"; // TODO
} catch(data_exception $e) {
return "data_exception";
}
}
function _switch_state() { // API CALL
try {
$user = User::fetch($GLOBALS["data"]->db_escape_string($_REQUEST["i"]));
......
......@@ -39,6 +39,7 @@
<ul class="dropdown-menu">
<li><a href="index.php?o=games">Catalogue des jeux</a></li>
<li><a href="index.php?o=loans">Emprunts</a></li>
<li><a href="index.php?o=reservations">Réservations</a></li>
</ul>
</li>
{% endif %}
......@@ -65,7 +66,7 @@
{% endif %}
</ul>
<ul class="nav navbar-nav navbar-right">
<li><a href="index.php?o=users&a=edit&i=<?=$logged_user->id?>"><span class="glyphicon glyphicon-user"></span></a></li>
<li><a href="index.php?o=users&a=options&i={{ global['logged_user'].id }}"><span class="glyphicon glyphicon-user"></span></a></li>
<li><a href="index.php?a=logout"><span class="glyphicon glyphicon-log-out"></span></a></li>
</ul>
<form class="navbar-form navbar-right">
......@@ -92,7 +93,7 @@
<pre>
REQUEST :
{% for key, val in request %}
{% if val != "passwd" %}
{% if key != "passwd" %}
{{ key }} : {{ val }}
{% endif %}
{% endfor %}
......
......@@ -72,6 +72,7 @@
$(document).ready(function() {
$('#object_list').DataTable({
"autoWidth": false,
"columnDefs": [{ "orderable": false, "targets": -1 }],
"fnDrawCallback": function() {
$(this).show();
}
......
{% extends "base.html" %}
{% block title %}Catalogue des jeux{% endblock %}
{% block title %}
{% if game.id %}{{ game.name }}{% else %}Nouveau jeu{% endif %}
{% endblock %}
{% block content %}
<div class="panel panel-default">
<div class="panel-heading">
......@@ -9,16 +11,20 @@
{{ game.name }}
<small>
{% if game.loan_id %}
<span class="label label-warning">
INDISPONIBLE :
jusqu'au {{ game.loan_end_date | date("d/m/Y") }}
<button type="button" class="btn btn-primary btn-md"
style="float: right; margin-right: 20px"
data-toggle="modal" data-target="#editModal" data-id="reservations">
<i class="glyphicon glyphicon-plus"></i>
<span>Réserver...</span>
</button>
</span>
<button type="button" class="btn btn-primary btn-md"
style="float: right; margin-right: 20px"
data-toggle="modal" data-target="#editModal" data-id="reservations">
<i class="glyphicon glyphicon-plus"></i>
<span>Réserver...</span>
</button>
{% else %}
<span class="label label-success">
DISPONIBLE
</span>
{% endif %}
</small>
<button type="button" class="btn btn-primary btn-md"
......
......@@ -8,7 +8,8 @@
</div>
<div class="modal-body">
{% if game.reservation_id %}
Ce jeu est déjà réservé
Ce jeu est déjà réservé par {{ game.reservation_member_name }}
pour le {{ game.reservation_date | date ("d/m/Y") }}
{% else %}
<div class="form-group">
<label class="control-label col-sm-3" for="member_id">Adhérent</label>
......@@ -74,6 +75,11 @@
{% if game.reservation_id == 0 %}
<input type="hidden" name="game_id" id="game_id" value="{{ game.id }}">
<input type="button" class="btn btn-success" id="reservation_button" value="Enregistrer">
{% else %}
<button type="button" class="btn btn-danger" id="delete_reservation_button">
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span>
<span>Effacer cette réservation</span>
</button>
{% endif %}
</div>
</div>
......@@ -88,4 +94,10 @@ $('#reservation_button').click(function(){
document.defaultform.submit();
return true;
});
$('#delete_reservation_button').click(function(){
if(confirm('Êtes vous sur ?')) {
$('#a').val('delete_reservation');
defaultform.submit();
}
});
</script>
{% extends "base.html" %}
{% block title %}Emprunt en cours{% endblock %}
{% block content %}
<div class="panel panel-default">
<div class="panel-heading">
<span style="font-size: 150%;" class="glyphicon glyphicon-knight"></span>
......@@ -65,3 +68,4 @@ $(document).ready(function() {
see https://datatables.net/plug-ins/i18n/French
*/
</script>
{% endblock %}
......@@ -74,6 +74,7 @@
$(document).ready(function() {
$('#object_list').DataTable({
"autoWidth": false,
"columnDefs": [{ "orderable": false, "targets": -1 }],
"fnDrawCallback": function() {
$(this).show();
}
......
......@@ -72,6 +72,7 @@
$(document).ready(function() {
$('#object_list').DataTable({
"autoWidth": false,
"columnDefs": [{ "orderable": false, "targets": -1 }],
"fnDrawCallback": function() {
$(this).show();
}
......
<div class="panel panel-default">
<div class="panel-heading">
<h4><!-- class="panel-title" -->
<span class="glyphicon glyphicon-th-list" style="margin-right: 10px" ></span>
{% if object.id %} {{ object.game_name }} :: Emprunt du {{ object.start_date }} {% else %} Nouvelle réservation {% endif %}
</h4>
</div>
<div class="panel-body">
<div class="form-group">
{% if object.id %}
<div class="control-label col-sm-6">
Créé le : {{ object.created_at }} / Mis-à-jour le {{ object.updated_at }}
</div>
<!-- FIXME : this field may be a little bit useless when creating a new loan -->
<label class="control-label col-sm-2" for="is_back">Rendu ?</label>
<div class="col-sm-4">
<input type="checkbox" id="is_back" name="is_back" class="form-control is_back_cbx"
data-switch-with-ajax {{ object.is_back ? "checked" }}/>
</div>
{% else %}
<label class="control-label col-sm-2" for="game_id">Jeu</label>
<div class="col-sm-4">
<input type="hidden" name="game_id" id="game_id" value="">
<div id="search-games-for-loans" >
<input class="typeahead form-control" type="text" placeholder="Jeu...">
</div>
</div>
<div class="control-label col-sm-6">
Si la date de retour est vide, elle sera positionnée à 21 jours plus tard.
</div>
{% endif %}
</div>
<input type="hidden" name="member_id" id="member_id" value="{{ object.member_id }}">
<div class="form-group">
<div class="col-sm-12" align="center">
<input type="button" class="btn btn-primary" id="back_button" value="&lt;&lt; Retour à la liste">
{% if object.id %}
<input type="button" class="btn btn-success" id="save_button" value="Enregistrer les changements">
<input type="button" class="btn btn-danger" id="delete_button" value="Supprimer">
{% else %}
<input type="button" class="btn btn-success" id="save_button" value="Créer">
{% endif %}
</div>
</div>
<!-- end of panel -->
</div>
</div>
<script>
// buttons events
$(document).ready(function () {
$('#save_button').click(function(){
if(document.defaultform.start_date.value == 0) {
alert ("Vous n'avez pas saisi de date de début !");
return false;
}
// go to the members controllers, as this action should display
// the list of the loans for the current user
$('#o').val('members');
if($('#i').val() == 0) {
$('#a').val('create_loan');
} else {
$('#a').val('update_loan');
}
document.defaultform.submit();
return true;
});
$('#delete_button').click(function(){
var msg = 'Voulez-vous réellement supprimer un emprunt ?\n' +
'Cette action n\'est possible qu\'en cas d\'erreur de saisie.\n';
if(confirm(msg)) {
$('#a').val('delete_loan');
document.defaultform.submit();
}
});
$('#back_button').click(function(){
// TODO this function should verify that the object has not been modified
// and if yes, ask for confirmation from the user.
window.location.href='index.php?o=members&a=loans&i={{ object.member_id }}';
});
$('.is_back_cbx').bootstrapSwitch({
onText: "Oui",
offText: "Non",
}).on('switchChange.bootstrapSwitch', function(event, state) {
$.ajax({
url: 'api.php?o=loans&a=switch_state&i=' + {{ object.id ?: '' }}
+ "&state=" + (state ? 1 : 0), // post on the API
type: 'POST',
xhr: function() { // Custom XMLHttpRequest
var myXhr = $.ajaxSettings.xhr();
return myXhr;
},
success: function(){
if(state) {
alert('Ce jeu est noté comme restitué aujourd\'hui.');
} else {
alert('Ce jeu est noté comme non restitué aujourd\'hui.');
}
},
error: function(){
alert('Une erreur a eu lieu lors de la restitution de ce jeu.');
},
cache: false,
contentType: false,
processData: false
});
});
});
</script>
{% extends "base.html" %}
{% block title %}Réservations{% endblock %}
{% block content %}
<div class="panel panel-default">
<div class="panel-heading">
<span style="font-size: 150%;" class="glyphicon glyphicon-knight"></span>
<span style="font-size: 150%; font-weight: bold">
Réservations
</span>
</div>
</div>
<div class="panel-body">
<div class="col-sm-12" align="center">
<table id="object_list" style="display:none">
<thead>
<tr>
<th>Jeu</th>
<th>Adhérent</th>
<th>Date</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for key, val in objects %}
<tr>
<td>
<a href="index.php?o=games&a=edit&i={{ val.game_id }}">{{ val.game_name }}</a>
</td>