Compare commits

..

No commits in common. "master" and "page-home" have entirely different histories.

51 changed files with 10039 additions and 2486 deletions

View File

@ -26,7 +26,7 @@
if( !isset($_SESSION['CAS']) || !is_array($_SESSION['CAS']) ) $_SESSION['CAS'] = [];
if( !isset($_SESSION['AUTH']) || !is_array($_SESSION['AUTH']) ) $_SESSION['AUTH'] = [];
if( !isset($_SESSION['AvailableDepartments']) || !is_array($_SESSION['AvailableDepartments']) ) $_SESSION['AvailableDepartments'] = [];
if( !isset($_SESSION['VERSION']) || !is_array($_SESSION['VERSION']) ) $_SESSION['VERSION'] = [];
if( !isset($_SESSION['VERSION']) || !is_string($_SESSION['VERSION']) ) $_SESSION['VERSION'] = null;
if( !isset($_SESSION['CurrentDepartmentId']) || !is_int($_SESSION['CurrentDepartmentId']) ) $_SESSION['CurrentDepartmentId'] = null;

View File

@ -100,7 +100,7 @@ class casController{
/* (2) Check ticket (validate)
---------------------------------------------------------*/
/* (1) Build useful variables */
$service = ($_SERVER['SERVER_PORT'] == 80) ? 'http' : 'https';
$service = ($_SERVER['SERVER_NAME'] == 'ptut.com' ) ? 'http' : 'https';
$service .= '://'.$_SERVER['HTTP_HOST'].'/api/v/1.0/cas/'.$popup_mode;
$ticket = urlencode($_GET['ticket']);
$validate_url = "https://sso.univ-pau.fr/cas/serviceValidate?ticket=$ticket&service=$service";
@ -146,33 +146,14 @@ class casController{
$_SESSION['AvailableDepartments'] = $departments;
/* (4) Choose first department by default */
$_SESSION['CurrentDatabase'] = $departments[0]['dbName'];
$_SESSION['CurrentDepartmentId'] = $departments[0]['idDep'];
$_SESSION['VERSION'] = [
'list' => $departments[0]['versions'],
'current' => null
];
/* (5) select version with default = 1 */
foreach($_SESSION['VERSION']['list'] as $v){
if( $v['default'] == 1 ){
$_SESSION['VERSION']['current'] = intval($v['iddatabase']);
$_SESSION['CurrentDatabase'] = $v['dbName'];
break;
}
}
/* (6) if no default -> select first */
if( !is_int($_SESSION['VERSION']) ){
$_SESSION['VERSION']['current'] = intval($_SESSION['VERSION']['list'][0]['iddatabase']);
$_SESSION['CurrentDatabase'] = $_SESSION['VERSION']['list'][0]['dbName'];
}
/* (7) Use this department's database */
/* (5) Use this department's database */
Repo::switchDatabase($_SESSION['CurrentDatabase']);
/* (4) Fetch @cas_login professor data
---------------------------------------------------------*/
/* (1) Try to fetch professor */
@ -259,4 +240,4 @@ class casController{
}
}
}

View File

@ -1,182 +0,0 @@
<?php
/**
* Created by PhpStorm.
* User: lucas
* Date: 10/05/18
* Time: 13:03
*/
namespace api\module\department;
use database\core\Repo;
use database\repo\cours;
use database\repo\formation;
use database\repo\td;
use database\repo\tp;
use database\repo\ue;
use PhpOffice\PhpSpreadsheet\Reader\Xls\Style\Border;
use PhpOffice\PhpSpreadsheet\Spreadsheet;
use PhpOffice\PhpSpreadsheet\Style\Borders;
class exportController
{
public function get($args){
/** @var ue $ueRepo */
$ueRepo = Repo::getRepo("ue");
/** @var cours $cmRepo */
$cmRepo = Repo::getRepo("cours");
/** @var td $tdRepo */
$tdRepo = Repo::getRepo("td");
/** @var tp $tpRepo */
$tpRepo = Repo::getRepo("tp");
/** @var formation $formationRepo */
$formationRepo = Repo::getRepo("formation");
$ues = $ueRepo->exportUE();
$formations = [];
//cache formations labels
foreach ($formationRepo->get(null) as $form){
$formations[$form["idForm"]] = $form["labelForm"];
}
$formatFormations = function(String $formationsId) use ($formations) : String{
$returned = "";
foreach (json_decode($formationsId,true) as $form){
$returned .= $formations[$form]."+";
}
return rtrim($returned,"+");
};
$spreadsheet = new Spreadsheet();
$excel = $spreadsheet->getActiveSheet();
//set up base document
$arrayHeaders = ["Code","Intitulé","Statut","V.H.","Cours","NbGr","Enseignant","TD","NbGr","Enseignant","TP","NbGr","Enseignant","Equ. TD","Total"];
$excel->setCellValue("B2","CODIFICATION DES UE");
$i = 1;
foreach ($arrayHeaders as $header){
$excel->setCellValueByColumnAndRow($i,4,$header);
$i++;
}
$excel->freezePane("O5");
//set up state variables
$currentFormation = null;
$currentLine = 5;
foreach ($ues as $ue){
if($currentFormation != $ue["labelFormation"]){
$currentFormation = $ue["labelFormation"];
$excel->setCellValue("A$currentLine",$ue["labelFormation"]);
$excel->getStyle("A$currentLine")->getFont()->setBold(true)->setSize(11);
$excel->getStyle("A$currentLine:O$currentLine")->getBorders()->getTop()->setBorderStyle(\PhpOffice\PhpSpreadsheet\Style\Border::BORDER_DOUBLE);
$currentLine += 2;
}
//get all groups data
$cm = $cmRepo->getGroups($ue["code"]);
$td = $tdRepo->getGroups($ue["code"]);
$tp = $tpRepo->getGroups($ue["code"]);
//set UE header
$nbrGroupeCM = count($cm);
$nbrGroupeTD = count($td);
$nbrGroupeTP = count($tp);
$volumeUE = $ue["volumeCours"] + $ue["volumeTD"] + $ue["volumeTP"];
$equiTDUE = $nbrGroupeCM*1.5*$ue["volumeCours"] + $nbrGroupeTD*$ue["volumeTD"] + $nbrGroupeTP*$ue["volumeTP"];
$excel->setCellValue("A$currentLine",$ue["code"]);
$excel->setCellValue("B$currentLine",$ue["label"]);
$excel->setCellValue("C$currentLine",$ue["required"] == 1? "OBL" : "OPT");
$excel->setCellValue("D$currentLine","$volumeUE");
$excel->setCellValue("E$currentLine","{$ue["volumeCours"]}");
$excel->setCellValue("F$currentLine","$nbrGroupeCM");
$excel->setCellValue("H$currentLine","{$ue["volumeTD"]}");
$excel->setCellValue("I$currentLine","$nbrGroupeTD");
$excel->setCellValue("K$currentLine","{$ue["volumeTP"]}");
$excel->setCellValue("L$currentLine","$nbrGroupeTP");
$excel->setCellValue("O$currentLine","$equiTDUE");
$excel->getStyle("A$currentLine:O$currentLine")->getFont()->setBold( true )->setSize(9);
$currentLine++;
$nbrLine = max($nbrGroupeCM,$nbrGroupeTD,$nbrGroupeTP)-1;
foreach (range(0,$nbrLine) as $n){
$excel->setCellValue("A$currentLine",$ue["code"]);
$excel->setCellValue("B$currentLine",$ue["label"]);
$equiTD = 0;
if(isset($cm[$n])){
$excel->setCellValue("E$currentLine","{$cm[$n]["volume"]}");
$excel->setCellValue("F$currentLine",$formatFormations($cm[$n]["formations"]));
$excel->setCellValue("G$currentLine",$cm[$n]["lastName"]." ".$cm[$n]["firstName"]);
$equiTD += 1.5*$cm[$n]["volume"];
}
if(isset($td[$n])){
$excel->setCellValue("H$currentLine","{$td[$n]["volume"]}");
$excel->setCellValue("I$currentLine",$formatFormations($td[$n]["formations"]));
$excel->setCellValue("J$currentLine",$td[$n]["lastName"]." ".$td[$n]["firstName"]);
$equiTD += $td[$n]["volume"];
}
if(isset($tp[$n])){
$excel->setCellValue("K$currentLine","{$tp[$n]["volume"]}");
$excel->setCellValue("L$currentLine",$formatFormations($tp[$n]["formations"]));
$excel->setCellValue("M$currentLine",$tp[$n]["lastName"]." ".$tp[$n]["firstName"]);
$equiTD += $tp[$n]["volume"];
}
$excel->setCellValue("N$currentLine","$equiTD");
$excel->getStyle("A$currentLine:O$currentLine")->getFont()->setSize(8);
$currentLine++;
}
$currentLine++;
}
//resize all columns
foreach(range('A','O') as $columnID) {
$excel->getColumnDimension($columnID)->setAutoSize(true);
}
//set content type headers
// header("Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
//reating writer
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xls($spreadsheet);
//as phpSpreadSheet do not support output on the buffer, we have to write in a temp file then read it
//create temporary file;
$file = tmpfile();
//get URI
$metaDatas = stream_get_meta_data($file);
$tmpFilename = $metaDatas['uri'];
//close file pointer
fclose($file);
//write data
$writer->save("$tmpFilename");
//get file content
return [
'headers' => [
'Content-Type' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'Content-Disposition' => 'attachment; filename=archive.xls'
],
'body' => file_get_contents($tmpFilename)
];
}
}

View File

@ -1,28 +0,0 @@
<?php
/**
* Created by PhpStorm.
* User: lucas
* Date: 07/05/18
* Time: 17:59
*/
namespace api\module\department\version;
use database\core\Repo;
class switchController
{
public function get($args){
$version = null;
extract($args);
$versionData = Repo::getRepo("meta")->getVersionById($version);
$_SESSION['CurrentDatabase'] = $versionData["dbName"];
$_SESSION['VERSION']['current'] = intval( $version );
return ["success" => true];
}
}

View File

@ -10,7 +10,6 @@ namespace api\module\department;
use database\core\Repo;
use database\repo\meta;
use error\core\Error;
use error\core\Err;
use database\repo\department;
@ -19,6 +18,95 @@ use Ifsnop\Mysqldump\Mysqldump;
class versionController{
/* Attributes
---------------------------------------------------------*/
private $backup_path;
private $origin_dbname;
/* (1) Initialize object
*
---------------------------------------------------------*/
public function __construct(){
$this->backup_path = null;
$this->origin_dbname = null;
}
/* (2) Initializes a directory
*
* @db_name<String> The database name
*
* @return outName<outType> outDesc
*
--------------------------------------------------------*/
private function initDir(string $db_name){
/* (1) Manage 'BACKUP' root directory
---------------------------------------------------------*/
/* (1) If not exists -> try to create it */
if( !is_dir(__BACKUP__) )
mkdir(__BACKUP__);
/* (2) Try to override permissions */
chmod(__BACKUP__, 0775);
/* (2) Manage sub-directory
---------------------------------------------------------*/
/* (1) If @db_name is 'preview_@database_hash' -> extract @database */
if( preg_match('/^preview_(\w+)_[a-f0-9]+$/', $db_name, $m) )
$db_name = $m[1];
/* (2) Store @db_name as origin */
$this->origin_dbname = $db_name;
/* (3) Store backup path */
$this->backup_path =__BACKUP__."/$db_name";
/* (4) If dir. already exists -> do nothing more */
if( is_dir($this->backup_path) )
return;
/* (4) Else -> try to create + set permissions */
mkdir($this->backup_path);
chmod($this->backup_path, 0775);
}
/* (3) Scan directory files
*
* @path<String> Directory path
*
* @return files<array> Array containing file names
*
---------------------------------------------------------*/
private function scandir(string $path) : array {
/* (1) Return [] if not a directory */
if( !is_dir($path) )
return [];
/* (2) Scan the directory */
$files = scandir($path);
/* (3) Remove '.' and '..' (2 first elements) */
$files = array_slice($files, 2);
/* (4) Return file list */
return $files;
}
/* (4) List available versions for this department
*
* @return versions<array> Version list
@ -26,12 +114,17 @@ class versionController{
---------------------------------------------------------*/
public function get($args){
//search for the current department in the session and return its versions
/** @var meta $depRepo */
$depRepo = Repo::getRepo("meta");
/* (1) Initialize directory for current database (department) */
$this->initDir( $_SESSION['CurrentDatabase'] );
//if no department found, return an empty array
return ['versions' => $depRepo->getAllVersions($_SESSION['CurrentDepartmentId']) ];
/* (2) Strip extensions */
$versions = array_map(
function($e){ return pathinfo($e, PATHINFO_FILENAME); },
$this->scandir($this->backup_path)
);
/* (3) Return versions */
return ['versions' => $versions ];
}
@ -39,7 +132,7 @@ class versionController{
/* (5) Remove an existing version for this department
*
* @version<int> Version name (typically snapshot date)
* @version<String> Version name (typically snapshot date)
*
* @return deleted<bool> Whether the version has been deleted
*
@ -48,35 +141,11 @@ class versionController{
$version = null;
extract($args);
/* (1) Try to delete */
$deleted = Repo::getRepo("meta")->deleteVersion($version);
if( !$deleted )
return ['error' => new Error(Err::ModuleError)];
/* (2) Update version list */
$_SESSION['VERSION']['list'] = Repo::getRepo("meta")->getAllVersions($_SESSION['CurrentDepartmentId']);
/* (3) Update current */
$_SESSION['VERSION']['current'] = null;
// select version with default = 1
foreach($_SESSION['VERSION']['list'] as $v){
if( $v['default'] == 1 ){
$_SESSION['VERSION']['current'] = intval($v['iddatabase']);
$_SESSION['CurrentDatabase'] = $v['dbName'];
break;
}
}
// if no default -> select first
if( !is_int($_SESSION['VERSION']) ){
$_SESSION['VERSION']['current'] = intval($_SESSION['VERSION']['list'][0]['iddatabase']);
$_SESSION['CurrentDatabase'] = $_SESSION['VERSION']['list'][0]['dbName'];
}
return ['deleted' => true];
/* (1) Initialize directory for current database (department) */
$this->initDir( $_SESSION['CurrentDatabase'] );
/* (2) Dispatch 'unlink' result */
return [ 'deleted' => unlink($this->backup_path."/$version.sql") ];
}
@ -87,8 +156,10 @@ class versionController{
*
---------------------------------------------------------*/
public function post($args){
$label = null;
extract($args);
/* (1) Initialize directory for current database (department) */
$this->initDir( $_SESSION['CurrentDatabase'] );
/* (2) Try to create the snapshot */
try{
@ -97,33 +168,21 @@ class versionController{
$conf = Repo::getDBConfig();
/* (2.2) Try to dump the database */
/** @var Mysqldump*/
$dump = new Mysqldump(
'mysql:host='.$conf['host'].';dbname='.$_SESSION['CurrentDatabase'],
'mysql:host='.$conf['host'].';dbname='.$conf['dbname'],
$conf['username'],
$conf['password']
$conf['password'],
[ "compress" => Mysqldump::GZIP ]
);
//create temporary file;
$file = tmpfile();
//get URI
$metaDatas = stream_get_meta_data($file);
$tmpFilename = $metaDatas['uri'];
//close file pointer
fclose($file);
//fill the file with sql dump
$dump->start($tmpFilename);
/* (2.3) Get current date (for naming the version) */
$current_date = date('d-m-Y');
/** @var department $depRepo */
$depRepo = Repo::getRepo("department");
$dbName = $depRepo->createVersionDatabase($_SESSION['CurrentDepartmentId'],file_get_contents($tmpFilename));
/** @var meta $metaRep */
$metaRep = Repo::getRepo("meta");
$versionId = $metaRep->createVersion($label, $dbName,$_SESSION['CurrentDepartmentId'] ,false);
/* (2.4) Store the version */
$dump->start($this->backup_path."/$current_date.sql");
/* (2.5) Return status */
return ['created_id' => $versionId ];
return ['created_id' => $current_date ];
/* (3) On error -> dispatch error */
}catch(\Exception $e){
@ -135,24 +194,106 @@ class versionController{
}
/* (6) Modify a version and use it
/* (6) ( Switches to preview || Applies on prod ) for a version
*
* @apply<int> If 0 -> preview version
* If 1 -> apply version into prod database
* @version<String> [OPT] Version name to use (if ommited, switch back to prod database)
*
* @return bool success
* @return created_id<String> The created version id (date)
*
---------------------------------------------------------*/
public function put($args){
$label = null;
$apply = null;
$version = null;
$default = null;
extract($args);
/** @var meta $metaRepo */
$metaRepo = Repo::getRepo("meta");
/* (1) Initialisation
---------------------------------------------------------*/
/* (1) Initialize directory for current database (department) */
$this->initDir( $_SESSION['CurrentDatabase'] );
/* (2) Get department repository */
/** @var department $dept_repo */
$dept_repo = Repo::getRepo('department');
/* (3) Check whether we have to [apply OR preview] */
$apply = ( $apply === '1' );
/* (2) If back to 'prod' database
---------------------------------------------------------*/
if( is_null($version) ){
/* (1) Reset database to 'prod' */
$_SESSION['CurrentDatabase'] = $this->origin_dbname;
$_SESSION['VERSION'] = null;
/* (2) Return success */
return [ 'updated' => true ];
}
/* (3) Read the backup version
---------------------------------------------------------*/
/* (1) Start buffer */
ob_start();
/* (2) Read backup file */
readgzfile($this->backup_path."/$version.sql");
/* (3) Store & Flush buffer into variable */
$snapshot = ob_get_clean();
/* (4) Manage error */
if( strlen($snapshot) === 0 )
return ['error' => new Error(Err::RepoError)];
/* (4) APPLY into 'prod' database
---------------------------------------------------------*/
if( $apply ){
/* (1) Restore from this version */
$restored = $dept_repo->restore($this->origin_dbname, $snapshot);
$_SESSION['VERSION'] = null;
/* (2) Dispatch repo execution status */
return [ 'updated' => $restored ];
}
/* (5) PREVIEW version database
---------------------------------------------------------*/
/* (1) Try to get 'preview' database name */
$preview_dbname = $dept_repo->previewExists($this->origin_dbname, $version);
/* (2) If does not exist -> create it */
if( is_null($preview_dbname) ){
/* 1. Try to create preview */
$preview_dbname = $dept_repo->createPreview($this->origin_dbname, $version);
/* 2. Switch to preview */
$dept_repo->restore($preview_dbname, $snapshot);
}
/* (3) Store 'preview' database in session */
$_SESSION['CurrentDatabase'] = $preview_dbname;
$_SESSION['VERSION'] = $version;
/* (4) Return status */
return [ 'updated' => $metaRepo->updateVersion($version,$label,$default) ];
return [ 'updated' => true ];
}
}

View File

@ -10,10 +10,7 @@ namespace api\module;
use database\core\Repo;
use database\repo\database;
use database\repo\department;
use database\repo\meta;
use database\repo\professor;
class departmentController
{
@ -78,29 +75,11 @@ class departmentController
foreach($deps as $dep){
if( $dep['idDep'] == $department ){
if( $dep['idDep'] == $department){
$_SESSION['AvailableDepartments'] = $deps;
$_SESSION['CurrentDatabase'] = $dep['dbName'];
$_SESSION['CurrentDepartmentId'] = $dep['idDep'];
$_SESSION['VERSION'] = [
'list' => $dep['versions'],
'current' => null
];
// select version with default = 1
foreach($_SESSION['VERSION']['list'] as $v){
if( $v['default'] == 1 ){
$_SESSION['VERSION']['current'] = intval($v['iddatabase']);
$_SESSION['CurrentDatabase'] = $v['dbName'];
break;
}
}
// if no default -> select first
if( !is_int($_SESSION['VERSION']) ){
$_SESSION['VERSION']['current'] = intval($_SESSION['VERSION']['list'][0]['iddatabase']);
$_SESSION['CurrentDatabase'] = $_SESSION['VERSION']['list'][0]['dbName'];
}
return ['switched' => true];
@ -113,52 +92,4 @@ class departmentController
}
public function post($args){
$name = null;
extract($args);
/** @var meta $metaRepo */
$metaRepo = Repo::getRepo("meta");
/** @var professor $profRep */
$profRep = Repo::getRepo("professor");
/** @var database $dbRepo */
$dbRepo = Repo::getRepo("database");
//create the department in the meta database
$depId = $metaRepo->createDepartment($name);
//link the current user to that department
$metaRepo->link($_SESSION['CAS']['login'],$depId);
//create the database and init the structure
$dbName = $dbRepo->init($depId);
//link the new database to the department
$metaRepo->createVersion("Version 1",$dbName,$depId,true);
//get the current user data from current department
$user = $profRep->get($_SESSION['CAS']['id'])[0];
//switch our connexion to that database
Repo::switchDatabase($dbName);
//create the default admin in the new database (categories are common to all department)
$profRep->create($user["lastName"],
$user["firstName"],
$user["Categorie_idCategorie"],
$user["hoursToDo"],
$user["abreviation"],
true,
$user["casLogin"]);
//update user session
$departments = $metaRepo->get_prof_departments($user["casLogin"]);
$_SESSION['AvailableDepartments'] = $departments;
//we are good now
return [];
}
}

View File

@ -14,7 +14,7 @@ use database\core\Repo;
class formationController
{
public function get($args){
public static function get($args){
$form_id = null;
extract($args);
@ -24,37 +24,4 @@ class formationController
return ['formations' => $repo->get($form_id)];
}
public function post($args){
$label = null;
$isInternal = null;
extract($args);
/** @var \database\repo\formation $repo */
$repo = Repo::getRepo("formation");
return ['idFormation' => $repo->create($label,$isInternal)];
}
public function put($args){
$idFormation = null;
$label = null;
$isInternal = null;
extract($args);
/** @var \database\repo\formation $repo */
$repo = Repo::getRepo("formation");
return ['success' => $repo->update($idFormation,$label,$isInternal)];
}
public function delete($args){
$idFormation = null;
extract($args);
/** @var \database\repo\formation $repo */
$repo = Repo::getRepo("formation");
return ['success' => $repo->delete($idFormation)];
}
}

View File

@ -22,17 +22,9 @@ class pdfController{
* @return download<File> The PDF fiche
---------------------------------------------------------*/
public static function get($args){
$prof_id = -1;
extract($args);
if(!$_SESSION["CAS"]["admin"] && $_SESSION["CAS"]["id"] != $prof_id){
return [
'headers' => ["Content-Type" => "text/html"],
'body' => "Unauthorized access"
];
}
/* (0) Initialize
---------------------------------------------------------*/
/* (1) Initialize data structure */
@ -132,7 +124,7 @@ class pdfController{
$fetched = $form_repo->get(null);
/* (2) Error: no formation found */
if( !is_array($fetched) )
if( count($fetched) < 1 )
return ['error' => new Error(Err::RepoError)];
/* (3) Reference formations by key = idForm */
@ -285,6 +277,10 @@ class pdfController{
Il est à noter qu\'elle contient les données générées au '.$date.'. Elle peut ne plus être à jour.
</blockquote>
<p>
Si un problème persiste veuillez contacter les responsables à l\'addresse mail suivante <span style="color: red;">&lt;mail_display_error&gt;</span>.
</p>
<br><br><br>
@ -313,12 +309,12 @@ class pdfController{
</tr>
<tr>
<td class="ar">Sous service</td>
<td class="ar">Heures manquantes</td>
<td>'.( $missing_h>0 ? "$svg_warning $missing_h" : $missing_h).' h</td>
</tr>
<tr>
<td class="ar">Sur service</td>
<td class="ar">Heures sup.</td>
<td>'.$sup_h.' h</td>
</tr>
</tbody>
@ -348,6 +344,10 @@ class pdfController{
Il est à noter qu\'elle contient les données générées au '.$date.'. Elle peut ne plus être à jour.
</blockquote>
<p>
Si un problème persiste veuillez contacter les responsables à l\'addresse mail suivante <span style="color: red;">&lt;mail_display_error&gt;</span>.
</p>
<br><br><br>
<table>

View File

@ -25,31 +25,10 @@ class coursController{
$formations = [];
extract($args);
/* Get the repos */
/* Get the cours repo */
/** @var cours $cours_repo */
$cours_repo = Repo::getRepo('cours');
/** @var ue $ue_repo */
$ue_repo = Repo::getRepo('ue');
/* (1) Fetch default formation from UE
---------------------------------------------------------*/
/* (1) Try to fetch the cours' UE */
$fetched_ue = $ue_repo->get($code);
/* (2) Manage error */
if( !is_array($fetched_ue) || count($fetched_ue) < 1 )
return ['error' => new Error(Err::RepoError)];
$defaultForm = intval($fetched_ue[0]['idForm']);
/* (3) Add to formation list if got a valid default formation */
if( is_int($defaultForm) && $defaultForm >= 0 && !in_array($defaultForm, $formations) )
$formations[] = $defaultForm;
/* (2) Create the cours
---------------------------------------------------------*/
/* (1) Try to create cours */
$created_id = $cours_repo->create($code, $idProf, $volume, $formations);
@ -57,7 +36,7 @@ class coursController{
if( is_null($created_id) || !is_int($created_id) )
return ['error' => new Error(Err::RepoError)];
return ['created_id' => $created_id, 'formations' => $formations];
return ['created_id' => $created_id];
}

View File

@ -25,31 +25,10 @@ class tdController{
$formations = [];
extract($args);
/* Get the repos */
/* Get the td repo */
/** @var td $td_repo */
$td_repo = Repo::getRepo('td');
/** @var ue $ue_repo */
$ue_repo = Repo::getRepo('ue');
/* (1) Fetch default formation from UE
---------------------------------------------------------*/
/* (1) Try to fetch the TD' UE */
$fetched_ue = $ue_repo->get($code);
/* (2) Manage error */
if( !is_array($fetched_ue) || count($fetched_ue) < 1 )
return ['error' => new Error(Err::RepoError)];
$defaultForm = intval($fetched_ue[0]['idForm']);
/* (3) Add to formation list if got a valid default formation */
if( is_int($defaultForm) && $defaultForm >= 0 && !in_array($defaultForm, $formations) )
$formations[] = $defaultForm;
/* (2) Create the TD
---------------------------------------------------------*/
/* (1) Try to create td */
$created_id = $td_repo->create($code, $idProf, $volume, $formations);
@ -57,7 +36,7 @@ class tdController{
if( is_null($created_id) || !is_int($created_id) )
return ['error' => new Error(Err::RepoError)];
return ['created_id' => $created_id, 'formations' => $formations];
return ['created_id' => $created_id];
}

View File

@ -25,31 +25,10 @@ class tpController{
$formations = [];
extract($args);
/* Get the repos */
/* Get the tp repo */
/** @var tp $tp_repo */
$tp_repo = Repo::getRepo('tp');
/** @var ue $ue_repo */
$ue_repo = Repo::getRepo('ue');
/* (1) Fetch default formation from UE
---------------------------------------------------------*/
/* (1) Try to fetch the TP' UE */
$fetched_ue = $ue_repo->get($code);
/* (2) Manage error */
if( !is_array($fetched_ue) || count($fetched_ue) < 1 )
return ['error' => new Error(Err::RepoError)];
$defaultForm = intval($fetched_ue[0]['idForm']);
/* (3) Add to formation list if got a valid default formation */
if( is_int($defaultForm) && $defaultForm >= 0 && !in_array($defaultForm, $formations) )
$formations[] = $defaultForm;
/* (2) Create the TP
---------------------------------------------------------*/
/* (1) Try to create tp */
$created_id = $tp_repo->create($code, $idProf, $volume, $formations);
@ -57,7 +36,7 @@ class tpController{
if( is_null($created_id) || !is_int($created_id) )
return ['error' => new Error(Err::RepoError)];
return ['created_id' => $created_id, 'formations' => $formations];
return ['created_id' => $created_id];
}

View File

@ -49,11 +49,7 @@ class PDOWrapper extends \PDO
if($this->stacking){
return new PDOStatementWrapper($statement, $this);
}else{
$st = parent::prepare($statement, $options);
if($st === false){
throw new \PDOException("There is an error in your SQL statement");
}
return $st;
return parent::prepare($statement, $options);
}
}
@ -158,19 +154,23 @@ class PDOWrapper extends \PDO
//find the given pattern in the request, then call our function and replace the matched string by the return value of our function
$finalStatement .= rtrim(preg_replace_callback("/(:[a-z_\-0-9]*)/is",function($matches) use (&$i,&$tempParametes){
//get next number
$i++;
//delete the ':' at the beginning of the string
$tempKey = ltrim($matches[0],':');
//copy the parameter with the modified index
$tempParametes[":$i"] = $tempParametes[$matches[0]];
$tempParametes[$tempKey.$i] = $tempParametes[$tempKey];
//delete the old index
unset($tempParametes[$matches[0]]);
unset($tempParametes[$tempKey]);
//return the modified string for replacement
return ":".$i++;
return $matches[0].$i;
},$statement),';').';';
$finalExecute += $tempParametes;
$finalExecute = array_merge($finalExecute,$tempParametes);
}
//disable stacking
@ -184,7 +184,6 @@ class PDOWrapper extends \PDO
$req = $this->prepare($finalStatement);
$success = $req->execute($finalExecute);
//as we execute multiple query that we don't fetch, we have to close the cursor if we want to do other requests later
$req->closeCursor();
$this->commit();

View File

@ -78,7 +78,7 @@
}
public static function switchDatabase(string $dbName){
return static::$driver->pdo()->exec("USE $dbName;SET autocommit=1;");
return static::$driver->pdo()->prepare("USE $dbName")->execute();
}
public static function getDBConfig() : array{

View File

@ -1,266 +0,0 @@
<?php
/**
* Created by PhpStorm.
* User: lucas
* Date: 09/05/18
* Time: 16:55
*/
namespace database\repo;
use database\core\Repo;
use database\core\Repo_i;
class database extends Repo_i {
/**
* @param int $depId
* @return String the name of the department database
* @throws \Exception
*/
public function init(int $depId) : String{
/** @var department $metaRep */
$metaRep = Repo::getRepo("department");
$SQL = "
SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES';
-- -----------------------------------------------------
-- Table `Categorie`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `Categorie` (
`idCategorie` INT(11) NOT NULL,
`labelCategorie` VARCHAR(100) NOT NULL,
PRIMARY KEY (`idCategorie`))
ENGINE = InnoDB
DEFAULT CHARACTER SET = latin1;
-- -----------------------------------------------------
-- Table `Professeur`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `Professeur` (
`idProfesseur` INT(11) NOT NULL AUTO_INCREMENT,
`casLogin` VARCHAR(50) NULL DEFAULT NULL,
`lastName` VARCHAR(50) NULL DEFAULT NULL,
`firstName` VARCHAR(50) NULL DEFAULT NULL,
`abreviation` VARCHAR(10) NULL DEFAULT NULL COMMENT 'Abreviation used in the excel document',
`admin` TINYINT(4) NOT NULL DEFAULT 0,
`hoursToDo` INT(11) NOT NULL DEFAULT 0,
`Categorie_idCategorie` INT(11) NOT NULL,
PRIMARY KEY (`idProfesseur`),
INDEX `fk_Professeur_Categorie1_idx` (`Categorie_idCategorie` ASC),
CONSTRAINT `fk_Professeur_Categorie1`
FOREIGN KEY (`Categorie_idCategorie`)
REFERENCES `Categorie` (`idCategorie`)
ON DELETE NO ACTION
ON UPDATE CASCADE)
ENGINE = InnoDB
AUTO_INCREMENT = 34
DEFAULT CHARACTER SET = latin1;
-- -----------------------------------------------------
-- Table `Formation`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `Formation` (
`idFormation` INT(11) NOT NULL AUTO_INCREMENT,
`labelFormation` VARCHAR(50) NULL DEFAULT NULL,
`isInternal` TINYINT(4) NULL DEFAULT 1,
PRIMARY KEY (`idFormation`))
ENGINE = InnoDB
AUTO_INCREMENT = 20
DEFAULT CHARACTER SET = latin1;
-- -----------------------------------------------------
-- Table `UE`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `UE` (
`code` VARCHAR(20) NOT NULL,
`label` VARCHAR(100) NOT NULL,
`required` TINYINT(4) NOT NULL DEFAULT 1,
`volumeCours` FLOAT NOT NULL DEFAULT 0,
`volumeTP` FLOAT NOT NULL DEFAULT 0,
`volumeTD` FLOAT NOT NULL,
`disabled` TINYINT(4) NOT NULL DEFAULT 0,
`Formation_idFormation` INT(11) NULL DEFAULT NULL,
PRIMARY KEY (`code`),
INDEX `fk_UE_Formation1_idx` (`Formation_idFormation` ASC),
CONSTRAINT `fk_UE_Formation1`
FOREIGN KEY (`Formation_idFormation`)
REFERENCES `Formation` (`idFormation`)
ON DELETE NO ACTION
ON UPDATE NO ACTION)
ENGINE = InnoDB
DEFAULT CHARACTER SET = latin1
COMMENT = 'Table contenant le code et le label des UE';
-- -----------------------------------------------------
-- Table `Cours`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `Cours` (
`idCours` INT(11) NOT NULL AUTO_INCREMENT,
`UE_code` VARCHAR(20) NOT NULL,
`Professeur_idProfesseur` INT(11) NULL DEFAULT NULL,
`volume` FLOAT NOT NULL DEFAULT 0,
PRIMARY KEY (`idCours`),
INDEX `fk_Cours_UE_idx` (`UE_code` ASC),
INDEX `fk_Cours_Professeur1_idx` (`Professeur_idProfesseur` ASC),
CONSTRAINT `fk_Cours_Professeur1`
FOREIGN KEY (`Professeur_idProfesseur`)
REFERENCES `Professeur` (`idProfesseur`)
ON DELETE SET NULL
ON UPDATE SET NULL,
CONSTRAINT `fk_Cours_UE`
FOREIGN KEY (`UE_code`)
REFERENCES `UE` (`code`)
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB
AUTO_INCREMENT = 88
DEFAULT CHARACTER SET = latin1;
-- -----------------------------------------------------
-- Table `GroupeCours`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `GroupeCours` (
`Formation_idFormation` INT(11) NOT NULL,
`Cours_idCours` INT(11) NOT NULL,
PRIMARY KEY (`Formation_idFormation`, `Cours_idCours`),
INDEX `fk_Formation_has_Cours_Cours1_idx` (`Cours_idCours` ASC),
INDEX `fk_Formation_has_Cours_Formation1_idx` (`Formation_idFormation` ASC),
CONSTRAINT `fk_Formation_has_Cours_Cours1`
FOREIGN KEY (`Cours_idCours`)
REFERENCES `Cours` (`idCours`)
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT `fk_Formation_has_Cours_Formation1`
FOREIGN KEY (`Formation_idFormation`)
REFERENCES `Formation` (`idFormation`)
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB
DEFAULT CHARACTER SET = latin1;
-- -----------------------------------------------------
-- Table `TD`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `TD` (
`idTD` INT(11) NOT NULL AUTO_INCREMENT,
`UE_code` VARCHAR(20) NOT NULL,
`Professeur_idProfesseur` INT(11) NULL DEFAULT NULL,
`volume` FLOAT NOT NULL DEFAULT 0,
PRIMARY KEY (`idTD`),
INDEX `fk_TD_UE1_idx` (`UE_code` ASC),
INDEX `fk_TD_Professeur1_idx` (`Professeur_idProfesseur` ASC),
CONSTRAINT `fk_TD_Professeur1`
FOREIGN KEY (`Professeur_idProfesseur`)
REFERENCES `Professeur` (`idProfesseur`)
ON DELETE SET NULL
ON UPDATE SET NULL,
CONSTRAINT `fk_TD_UE1`
FOREIGN KEY (`UE_code`)
REFERENCES `UE` (`code`)
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB
AUTO_INCREMENT = 107
DEFAULT CHARACTER SET = latin1;
-- -----------------------------------------------------
-- Table `GroupeTD`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `GroupeTD` (
`Formation_idFormation` INT(11) NOT NULL,
`TD_idTD` INT(11) NOT NULL,
PRIMARY KEY (`Formation_idFormation`, `TD_idTD`),
INDEX `fk_Formation_has_TD_TD1_idx` (`TD_idTD` ASC),
INDEX `fk_Formation_has_TD_Formation1_idx` (`Formation_idFormation` ASC),
CONSTRAINT `fk_Formation_has_TD_Formation1`
FOREIGN KEY (`Formation_idFormation`)
REFERENCES `Formation` (`idFormation`)
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT `fk_Formation_has_TD_TD1`
FOREIGN KEY (`TD_idTD`)
REFERENCES `TD` (`idTD`)
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB
DEFAULT CHARACTER SET = latin1;
-- -----------------------------------------------------
-- Table `TP`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `TP` (
`idTP` INT(11) NOT NULL AUTO_INCREMENT,
`UE_code` VARCHAR(20) NOT NULL,
`Professeur_idProfesseur` INT(11) NULL DEFAULT NULL,
`volume` FLOAT NOT NULL DEFAULT 0,
PRIMARY KEY (`idTP`),
INDEX `fk_TP_UE1_idx` (`UE_code` ASC),
INDEX `fk_TP_Professeur1_idx` (`Professeur_idProfesseur` ASC),
CONSTRAINT `fk_TP_Professeur1`
FOREIGN KEY (`Professeur_idProfesseur`)
REFERENCES `Professeur` (`idProfesseur`)
ON DELETE SET NULL
ON UPDATE SET NULL,
CONSTRAINT `fk_TP_UE1`
FOREIGN KEY (`UE_code`)
REFERENCES `UE` (`code`)
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB
AUTO_INCREMENT = 164
DEFAULT CHARACTER SET = latin1;
-- -----------------------------------------------------
-- Table `GroupeTP`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `GroupeTP` (
`Formation_idFormation` INT(11) NOT NULL,
`TP_idTP` INT(11) NOT NULL,
PRIMARY KEY (`Formation_idFormation`, `TP_idTP`),
INDEX `fk_Formation_has_TP_TP1_idx` (`TP_idTP` ASC),
INDEX `fk_Formation_has_TP_Formation1_idx` (`Formation_idFormation` ASC),
CONSTRAINT `fk_Formation_has_TP_Formation1`
FOREIGN KEY (`Formation_idFormation`)
REFERENCES `Formation` (`idFormation`)
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT `fk_Formation_has_TP_TP1`
FOREIGN KEY (`TP_idTP`)
REFERENCES `TP` (`idTP`)
ON DELETE CASCADE
ON UPDATE CASCADE)
ENGINE = InnoDB
DEFAULT CHARACTER SET = latin1;
SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
LOCK TABLES `Categorie` WRITE;
/*!40000 ALTER TABLE `Categorie` DISABLE KEYS */;
INSERT INTO `Categorie` VALUES (1,'Professeurs et Maîtres de Conférences'),(2,'ATERs'),(3,'CDDs enseignement'),(4,'Doctorants Vacataires'),(5,'Permanents UPPA'),(6,'Vacataires extérieurs');
/*!40000 ALTER TABLE `Categorie` ENABLE KEYS */;
UNLOCK TABLES;
";
return $metaRep->createVersionDatabase($depId,$SQL);
}
}

View File

@ -167,13 +167,36 @@ class department extends Repo_i
}
public function createVersionDatabase(String $depId, String $SQL) : String{
$dbName = uniqid($depId)."";
public function restore(string $dbName, string $SQL) : bool{
$this->pdo->exec("CREATE DATABASE $dbName; USE $dbName;".$SQL."USE {$_SESSION["CurrentDatabase"]};SET autocommit=1;");
$this->pdo->setAttribute(\PDO::ATTR_AUTOCOMMIT,1);
//get the list of command to execute
$this->pdo->exec("DROP DATABASE $dbName;");
$this->pdo->exec("CREATE DATABASE $dbName;");
$this->pdo->exec("USE $dbName;");
return $dbName;
}
return $this->pdo->exec($SQL);
}
private static function generatePreviewDBName(string $bdName, string $backupName) : string{
return "preview_{$bdName}_".sha1($bdName.$backupName);
}
public function previewExists(string $dbName, string $backupname) : ?string {
$previewDBName = static::generatePreviewDBName($dbName,$backupname);
$st = $this->pdo->prepare("SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = :dbName");
$st->execute(["dbName" => $previewDBName]);
if($st->fetch() === false) return null;
else return $previewDBName;
}
public function createPreview(string $dbName, string $backupname) : string {
$previewDBName = static::generatePreviewDBName($dbName,$backupname);
$this->pdo->exec("CREATE OR REPLACE DATABASE $previewDBName");
return $previewDBName;
}
}

View File

@ -42,26 +42,6 @@ class formation extends Repo_i {
}
public function update(int $idFormation, ?String $label, ?bool $isInternal) : bool{
$req = "";
$execute = [];
if($label != null){
$req .= "labelFormation=:label,";
$execute["label"] = $label;
}
if($isInternal != null){
$req .= "isInternal=:isInternal,";
$execute["isInternal"] = $isInternal?1:0;
}
$req = rtrim($req,",");
$execute["idFormation"] = $idFormation;
$st = $this->pdo->prepare("UPDATE `Formation` SET $req WHERE idFormation=:idFormation");
return $st->execute($execute);
}
/* (2) Check if a formation exists (by its label)

View File

@ -88,7 +88,7 @@ class meta extends Repo_i {
public function get_prof_departments(String $casLogin) : array{
/* (1) Prepare Statement */
$st = $this->pdo->prepare("SELECT d2.iddepartement idDep, d2.label labelDep
$st = $this->pdo->prepare("SELECT d2.iddepartement idDep, d2.label labelDep, d2.databaseName dbName
FROM meta_vhost.casUser
JOIN meta_vhost.linkedDep D ON casUser.casLogin = D.casUser_casLogin
JOIN meta_vhost.departement d2 ON D.departement_iddepartement = d2.iddepartement
@ -108,21 +108,11 @@ class meta extends Repo_i {
/* (5) Get data */
$fetched = $st->fetchAll();
/* (6) Return [] on no result */
if( $fetched === false )
return [];
/* (6) Return [] on no result */
if( $fetched === false )
return [];
/* (7) Add all possible databases to the department */
foreach ($fetched as &$dep){
$st = $this->pdo->prepare("SELECT *
FROM meta_vhost.`databases`
WHERE departement_iddepartement = :idDep
ORDER BY meta_vhost.`databases`.`default` DESC ");
$st->execute(["idDep" => $dep["idDep"]]);
$dep["versions"] = $st->fetchAll();
}
/* (8) Return data */
/* (7) Return data */
return $fetched;
}
@ -155,6 +145,15 @@ class meta extends Repo_i {
}
/* (5) Check if a link exists
*
* @casLogin<String> The professor's cas login
@ -255,97 +254,4 @@ class meta extends Repo_i {
}
public function deleteVersion(int $version) : bool{
//get version data
$st = $this->pdo->prepare("SELECT * FROM meta_vhost.`databases` WHERE iddatabase = :id");
$st->execute(["id" => $version]);
$versionData = $st->fetch();
if( !is_array($versionData) || $versionData["default"] == 1){
return false;
}
//delete database
$versionDatabase = $versionData['dbName'];
$this->pdo->exec("DROP DATABASE $versionDatabase;");
//remove from meta
$st = $this->pdo->prepare("DELETE FROM meta_vhost.`databases` WHERE iddatabase = :id");
return $st->execute(["id" => $version]);
}
public function createVersion(String $label, String $dbName, int $depId, bool $isDefault = false) : int{
$st = $this->pdo->prepare("INSERT INTO meta_vhost.`databases`(label, dbName, `default`, departement_iddepartement)
VALUE (:label,:dbName,:default,:depId)");
$st->execute([
"label" => $label,
"dbName" => $dbName,
"default" => $isDefault? 1 : 0,
"depId" => $depId
]);
return $this->pdo->lastInsertId();
}
public function getVersionById(int $id) : array{
$st = $this->pdo->prepare("SELECT * FROM meta_vhost.`databases` WHERE iddatabase = :idVersion");
$st->execute(["idVersion" => $id]);
$fetched = $st->fetch();
return $fetched == false ? [] : $fetched;
}
public function getAllVersions(int $idDep) : array {
$st = $this->pdo->prepare("SELECT * FROM meta_vhost.`databases` WHERE departement_iddepartement = :idDep");
$st->execute(["idDep" => $idDep]);
return $st->fetchAll();
}
public function updateVersion(int $version, ?String $label, ?bool $default) : bool{
//you can't un-default a version, you have to choose another version to be the default one
if($default == false){
return false;
}
$set = "";
$execute = [];
if($label != null){
$set .= "label=:label,";
$execute["label"] = $label;
}
if($default != null){
$set .= "`default`=:default,";
$execute["default"] = $default?1:0;
//if default = true, set previous default version to non-default
if($default == true){
$req = $this->pdo->prepare("UPDATE meta_vhost.`databases` SET `default`=0 WHERE departement_iddepartement=:idDep");
$req->execute(["idDep" => $_SESSION['CurrentDepartmentId']]);
}
}
$set = rtrim($set,",");
$execute["idVersion"] = $version;
$st = $this->pdo->prepare("UPDATE meta_vhost.`databases` SET $set WHERE iddatabase=:idVersion");
return $st->execute($execute);
}
public function createDepartment(String $name) : int{
$st = $this->pdo->prepare("INSERT INTO meta_vhost.departement(label) VALUE (:label)");
$st->execute(["label" => $name]);
return $this->pdo->lastInsertId();
}
}

View File

@ -321,7 +321,7 @@ class professor extends Repo_i {
$parm = is_null($prof_id) ? [] : [':id' => $prof_id];
/* (2) Prepare Statement */
$st = $this->pdo->prepare("SELECT * FROM `Professeur`$cond ORDER BY firstName, lastName ASC");
$st = $this->pdo->prepare("SELECT * FROM `Professeur`$cond ORDER BY abreviation ASC");
/* (3) Bind params and execute statement */
if( is_bool($st) ) return [];
@ -433,8 +433,7 @@ class professor extends Repo_i {
AND VHTp.idProf = Prof.idProfesseur
AND VHTd.idProf = Prof.idProfesseur
GROUP BY
Prof.idProfesseur
ORDER BY Prof.firstName, Prof.lastName ASC;");
Prof.idProfesseur;");
/* (3) Bind params and execute statement */
if( is_bool($st) ) return [];

View File

@ -180,56 +180,36 @@ class ue extends Repo_i {
$parm = is_null($code) ? [] : [':code' => $code];
/* (2) Prepare Statement */
$st = $this->pdo->prepare("
SELECT
ue.code,
ue.label,
ue.disabled,
ue.required,
ue.volumeCours,
ue.volumeTD,
ue.volumeTP,
IFNULL(ue.Formation_idFormation, -1) idForm,
fdef.labelFormation labelForm,
IFNULL(formlist.formations, '[]') formations,
IFNULL(formlist.nbrCours,0) nbrCours,
IFNULL(formlist.nbrTD,0) nbrTD,
IFNULL(formlist.nbrTP,0) nbrTP,
IFNULL(formlist.modCours,0) modCours,
IFNULL(formlist.modTD,0) modTD,
IFNULL(formlist.modTP,0) modTP,
IFNULL(formlist.nbrProfCours,0) nbrProfCours,
IFNULL(formlist.nbrProfTD,0) nbrProfTD,
IFNULL(formlist.nbrProfTP,0) nbrProfTP
FROM UE ue
LEFT JOIN Formation fdef ON ue.Formation_idFormation = fdef.idFormation
LEFT JOIN (
SELECT ue2.code code, CONCAT('[',GROUP_CONCAT(DISTINCT Formation.idFormation), ']') formations,
count(DISTINCT C.idCours) nbrCours, count(DISTINCT T.idTD) nbrTD, count(DISTINCT T2.idTP) nbrTP,
MOD(volC.vol,ue2.volumeCours) modCours,
MOD(volTD.vol,ue2.volumeTD) modTD,
MOD(volTP.vol,ue2.volumeTP) modTP,
count(DISTINCT C.Professeur_idProfesseur,C.idCours) nbrProfCours,
count(DISTINCT T.Professeur_idProfesseur,T.idTD) nbrProfTD,
count(DISTINCT T2.Professeur_idProfesseur,T2.idTP) nbrProfTP
FROM UE ue2
LEFT JOIN (SELECT sum(CO.volume) vol, CO.UE_code FROM Cours CO GROUP BY CO.UE_code) volC ON ue2.code = volC.UE_code
LEFT JOIN (SELECT sum(TD2.volume) vol, TD2.UE_code FROM TD TD2 GROUP BY TD2.UE_code) volTD ON ue2.code = volTD.UE_code
LEFT JOIN (SELECT sum(TP2.volume) vol, TP2.UE_code FROM TP TP2 GROUP BY TP2.UE_code) volTP ON ue2.code = volTP.UE_code
LEFT JOIN Cours C ON ue2.code = C.UE_code
LEFT JOIN TD T ON ue2.code = T.UE_code
LEFT JOIN TP T2 ON ue2.code = T2.UE_code
LEFT JOIN GroupeCours C2 ON C.idCours = C2.Cours_idCours
LEFT JOIN GroupeTD TD2 ON T.idTD = TD2.TD_idTD
LEFT JOIN GroupeTP TP2 ON T2.idTP = TP2.TP_idTP
JOIN Formation ON C2.Formation_idFormation = Formation.idFormation
OR TD2.Formation_idFormation = Formation.idFormation
OR TP2.Formation_idFormation = Formation.idFormation
GROUP BY `ue2`.`code`
) formlist ON formlist.code = ue.code
GROUP BY `ue`.`code`
$cond
ORDER BY `ue`.`label` ASC");
$st = $this->pdo->prepare("SELECT
ue.code,
ue.label,
ue.disabled,
ue.required,
ue.volumeCours,
ue.volumeTD,
ue.volumeTP,
IFNULL(ue.Formation_idFormation, -1) idForm,
fdef.labelFormation labelForm,
IFNULL(formlist.formations, '[]') formations
FROM UE ue
LEFT JOIN Formation fdef ON ue.Formation_idFormation = fdef.idFormation
LEFT JOIN (
SELECT ue2.code code, CONCAT('[',GROUP_CONCAT(DISTINCT Formation.idFormation), ']') formations
FROM UE ue2
LEFT JOIN Cours C ON ue2.code = C.UE_code
LEFT JOIN TD T ON ue2.code = T.UE_code
LEFT JOIN TP T2 ON ue2.code = T2.UE_code
LEFT JOIN GroupeCours C2 ON C.idCours = C2.Cours_idCours
LEFT JOIN GroupeTD TD2 ON T.idTD = TD2.TD_idTD
LEFT JOIN GroupeTP TP2 ON T2.idTP = TP2.TP_idTP
JOIN Formation ON C2.Formation_idFormation = Formation.idFormation
OR TD2.Formation_idFormation = Formation.idFormation
OR TP2.Formation_idFormation = Formation.idFormation
GROUP BY `ue2`.`code`
) formlist ON formlist.code = ue.code
GROUP BY `ue`.`code`
$cond
ORDER BY `ue`.`label` ASC");
/* (3) Bind params and execute statement */
if( is_bool($st) ) return [];
@ -290,13 +270,4 @@ class ue extends Repo_i {
return $fetched;
}
public function exportUE() : array{
$st = $this->pdo->prepare("SELECT * FROM UE
LEFT JOIN Formation
ON UE.Formation_idFormation = Formation.idFormation
ORDER BY Formation_idFormation IS NULL DESC, Formation_idFormation ASC");
$st->execute();
return $st->fetchAll();
}
}

View File

@ -33,43 +33,19 @@
}
/* (2) Only teacher */
if( !in_array('cas_admin', $_SESSION['AUTH']) ){
// redirection
if( $this->pagename != "fiche" ){
header('Location: /fiche/');
die();
}
// deconnection
if( $this->uri == 'logout' || $this->uri == 'logout/' ){
\session_destroy();
header('Location: /');
die();
}
// load page
include __PUBLIC__."/page/fiche.php";
die();
}
/* (3) Build page file name */
/* (1) Build page file name */
$page_fname = __PUBLIC__."/page/".$this->pagename.".php";
/* (4) If page does not exist -> 404 */
/* (2) If page does not exist -> 404 */
if( !file_exists($page_fname) )
include __PUBLIC__.'/page/404.php';
/* (5) Set URI arguments */
/* (3) Set URI arguments */
$_GET['uri'] = explode('/', $this->uri);
/* (6) Load page */
/* (4) Load page */
include __PUBLIC__."/page/".$this->pagename.".php";
}

View File

@ -29,7 +29,6 @@
"phpstan/phpstan": "^0.9.2",
"ifsnop/mysqldump-php": "2.*",
"setasign/fpdf": "1.8.1",
"mpdf/mpdf": "^7.0",
"php" : ">= 7.1"
"mpdf/mpdf": "^7.0"
}
}

Binary file not shown.

View File

@ -82,21 +82,13 @@
}
},
"POST": {
"des": "Create a new Department",
"per": [["cas_user"]],
"par": {
"name": { "des": "Name of the department", "typ": "text"}
}
},
"PUT":{
"des": "Switch the user on another department database",
"per": [["cas_user"]],
"par": {
"URL0": {"des": "Department id", "typ": "id", "ren": "department" }
},
"out": {
"output": {
"switched": { "des": "Whether the department has been switched", "typ": "bool" }
}
@ -117,54 +109,31 @@
}
}
},
"export":{
"GET": {
"des": "Export the data of the current department and version to a Excel file",
"per": [["cas_admin"]],
"par": {},
"opt": { "download": true }
}
},
"version":{
"switch":{
"GET": {
"des": "Switch database version for the current session",
"per": [["cas_admin"]],
"par": {
"URL0": { "des": "The version id", "typ": "id", "ren": "version" }
},
"out": {
"sucess": { "des": "success of the operation", "typ": "bool" }
}
}
},
"GET": {
"des": "Get the list of the versions of the department",
"per": [["cas_admin"]],
"par": {},
"out": {
"output": {
"versions": { "des": "List of available versions", "typ": "array" }
}
},
"POST": {
"des": "Create a backup if the name is empty, execute the backup if the name is set",
"per": [["cas_admin"]],
"par": {
"label": { "des": "Label of the version", "typ": "text" }
},
"out": {
"par": {},
"output": {
"created_id": { "des": "The id of the created version", "typ": "varchar(10,10,alphanumeric)" }
}
},
"PUT": {
"des": "Update a version and switch to this version",
"des": "Switches to a older version or to current state",
"per": [["cas_admin"]],
"par": {
"URL0": { "des": "id of the version", "typ": "id", "ren": "version" },
"label": { "des": "Label de la version", "typ": "text", "opt": true },
"default": { "des": "Whether this version should be used on login", "typ": "bool", "opt": true }
"URL0": { "des": "0 to preview, 1 to apply", "typ": "id", "ren": "apply" },
"URL1": { "des": "The version name, current state if ommited", "typ": "varchar(10,10,alphanumeric)", "ren": "version", "opt": true }
},
"out": {
"output": {
"updated": { "des": "Whether the version has been switched|applied", "typ": "bool" }
}
},
@ -172,9 +141,9 @@
"des": "Delete a backup",
"per": [["cas_admin"]],
"par": {
"URL0": { "des": "The version id", "typ": "id", "ren": "version" }
"URL0": { "des": "The version name", "typ": "varchar(10,10,alphanumeric)", "ren": "version" }
},
"out": {
"output": {
"deleted": { "des": "Whether the version has been deleted", "typ": "bool" }
}
}
@ -261,7 +230,7 @@
"GET": {
"des": "Get a professor's fiche",
"per": [["cas_admin"], ["cas_user"]],
"per": [["cas_admin"]],
"par": {
"URL0": { "des": "Optional professor UID.", "typ": "id", "ren": "prof_id" }
},
@ -346,9 +315,8 @@
"volume": { "des": "Number of hours for Cours", "typ": "id", "opt": true, "def": 0 },
"formations": { "des": "List of formations (ids)", "typ": "array<id>", "opt": true, "def": [] }
},
"out": {
"created_id" : { "des": "The id of the created Cours", "typ": "id" },
"formations" : { "des": "The ids of the linked formations", "typ": "array<id>" }
"output": {
"created_id" : { "des": "The id of the created Cours", "typ": "id" }
}
},
@ -370,7 +338,7 @@
"add_form": { "des": "Id of formations to add", "typ": "array<id>", "opt": true, "def": [] },
"rem_form": { "des": "Id of formations to remove", "typ": "array<id>", "opt": true, "def": [] }
},
"out": {
"output": {
"updated" : { "des": "Whether it has been updated", "typ": "bool" }
}
},
@ -381,7 +349,7 @@
"par": {
"URL0": { "des": "Id of the Cours", "typ": "id", "ren": "idCours" }
},
"out": {
"output": {
"deleted" : { "des": "Whether it has been deleted", "typ": "bool" }
}
}
@ -399,9 +367,8 @@
"volume": { "des": "Number of hours for TD", "typ": "id", "opt": true, "def": 0 },
"formations": { "des": "List of formations (ids)", "typ": "array<id>", "opt": true, "def": [] }
},
"out": {
"created_id" : { "des": "The id of the created TD", "typ": "id" },
"formations" : { "des": "The ids of the linked formations", "typ": "array<id>" }
"output": {
"created_id" : { "des": "The id of the created TD", "typ": "id" }
}
},
@ -423,7 +390,7 @@
"add_form": { "des": "Id of formations to add", "typ": "array<id>", "opt": true, "def": [] },
"rem_form": { "des": "Id of formations to remove", "typ": "array<id>", "opt": true, "def": [] }
},
"out": {
"output": {
"updated" : { "des": "Whether it has been updated", "typ": "bool" }
}
},
@ -434,7 +401,7 @@
"par": {
"URL0": { "des": "Id of the TD", "typ": "id", "ren": "idTD" }
},
"out": {
"output": {
"deleted" : { "des": "Whether it has been deleted", "typ": "bool" }
}
}
@ -452,9 +419,8 @@
"volume": { "des": "Number of hours for TP", "typ": "id", "opt": true, "def": 0 },
"formations": { "des": "List of formations (ids)", "typ": "array<id>", "opt": true, "def": [] }
},
"out": {
"created_id" : { "des": "The id of the created TP", "typ": "id" },
"formations" : { "des": "The ids of the linked formations", "typ": "array<id>" }
"output": {
"created_id" : { "des": "The id of the created TP", "typ": "id" }
}
},
@ -476,7 +442,7 @@
"add_form": { "des": "Id of formations to add", "typ": "array<id>", "opt": true, "def": [] },
"rem_form": { "des": "Id of formations to remove", "typ": "array<id>", "opt": true, "def": [] }
},
"out": {
"output": {
"updated" : { "des": "Whether it has been updated", "typ": "bool" }
}
},
@ -487,7 +453,7 @@
"par": {
"URL0": { "des": "Id of the TP", "typ": "id", "ren": "idTP" }
},
"out": {
"output": {
"deleted" : { "des": "Whether it has been deleted", "typ": "bool" }
}
}
@ -503,33 +469,6 @@
"par": {
"URL0":{"des" : "Id of the formation", "typ": "id", "ren": "form_id", "opt" : true }
}
},
"POST":{
"des": "Create a new formation",
"per": [["cas_admin"]],
"par": {
"label":{"des" : "name of the formation", "typ": "text" },
"isInternal":{"des" : "is this formation internal to the department", "typ": "bool" }
}
},
"PUT":{
"des": "Update a formation",
"per": [["cas_admin"]],
"par": {
"URL0": { "des": "Id of the formation", "typ": "id", "ren": "idFormation" },
"label":{"des" : "name of the formation", "typ": "text", "opt":true },
"isInternal":{"des" : "is this formation internal to the department", "typ": "bool", "opt":true }
}
},
"DELETE":{
"des": "Delete a formation",
"per": [["cas_admin"]],
"par": {
"URL0": { "des": "Id of the formation", "typ": "id", "ren": "idFormation" }
}
}
},

9419
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -37,7 +37,7 @@
"node-sass": "^4.7.2",
"sass-loader": "^6.0.6",
"vue-loader": "^13.0.5",
"vue-template-compiler": "^2.5.16",
"vue-template-compiler": "^2.5.9",
"webpack": "^3.8.1",
"webpack-dev-server": "^2.9.5"
}

View File

@ -1,44 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
height="32px"
id="Layer_1"
style="enable-background:new 0 0 32 32;"
version="1.1"
viewBox="0 0 32 32"
width="32px"
xml:space="preserve"
sodipodi:docname="back.svg"
inkscape:version="0.92.2 5c3e80d, 2017-08-06"><metadata
id="metadata9"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs7" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1015"
id="namedview5"
showgrid="false"
inkscape:zoom="7.375"
inkscape:cx="16"
inkscape:cy="16"
inkscape:window-x="0"
inkscape:window-y="29"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" /><path
d="M 12,6 C 11.469,6 11.005141,6.1930781 10.619141,6.5800781 L 2.6621094,14.537109 C 2.3341094,14.865109 2,15.271 2,16 c 0,0.729 0.2794844,1.080266 0.6464844,1.447266 l 7.9726566,7.972656 C 11.005141,25.806922 11.469,26 12,26 c 1.188,0 2,-1.016 2,-2 0,-0.516 -0.186078,-0.986859 -0.580078,-1.380859 L 8.8007812,18 H 28 c 1.104,0 2,-0.896 2,-2 0,-1.104 -0.896,-2 -2,-2 H 8.8007812 L 13.419922,9.3808594 C 13.813922,8.9868594 14,8.516 14,8 14,7.016 13.187,6 12,6 Z"
id="fill-edit"
inkscape:connector-curvature="0" /></svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -1,45 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
enable-background="new 0 0 500 500"
height="500px"
id="Layer_1"
version="1.1"
viewBox="0 0 500 500"
width="500px"
xml:space="preserve"
sodipodi:docname="pin-disabled.svg"
inkscape:version="0.92.2 5c3e80d, 2017-08-06"><metadata
id="metadata9"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs7" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1015"
id="namedview5"
showgrid="false"
inkscape:zoom="0.6675088"
inkscape:cx="186.10652"
inkscape:cy="69.51775"
inkscape:window-x="0"
inkscape:window-y="29"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1"
inkscape:snap-page="true" /><path
style="clip-rule:evenodd;fill:#010101;fill-rule:evenodd"
d="M 463.1543 1 L 349.9375 114.2168 L 349.9375 77.375 L 363.56836 77.375 C 376.10336 77.375 386.2832 67.198109 386.2832 54.662109 C 386.2832 42.126109 376.10336 31.949219 363.56836 31.949219 L 136.43164 31.949219 C 123.89564 31.949219 113.71875 42.126109 113.71875 54.662109 C 113.71875 67.199109 123.89564 77.375 136.43164 77.375 L 150.06055 77.375 L 150.06055 232.73828 L 82.828125 300.60742 C 79.465125 303.87442 77.376953 308.50466 77.376953 313.59766 C 77.376953 323.58866 85.459234 331.77148 95.365234 331.77148 L 132.38281 331.77148 L 1 463.1543 L 36.845703 499 L 499 36.845703 L 463.1543 1 z M 213.6582 77.375 C 223.6532 77.375 231.83008 85.552828 231.83008 95.548828 L 231.83008 213.65625 C 231.83008 223.65225 223.6542 231.82812 213.6582 231.82812 C 203.6642 231.82812 195.48633 223.65225 195.48633 213.65625 L 195.48633 95.548828 C 195.48633 85.552828 203.6642 77.375 213.6582 77.375 z M 352.55469 235.29102 L 222.74414 365.10156 L 222.74414 445.33984 C 222.74414 457.87584 232.92103 468.05078 245.45703 468.05078 C 257.99403 468.05078 268.17188 457.87584 268.17188 445.33984 L 268.17188 331.77148 L 404.63281 331.77148 C 414.53181 331.77148 422.62305 323.58866 422.62305 313.59766 C 422.62305 308.60466 420.61847 304.06211 417.35547 300.78711 L 352.55469 235.29102 z "
id="fill-edit" /></svg>

Before

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -32,14 +32,13 @@
inkscape:window-height="1015"
id="namedview5"
showgrid="false"
inkscape:zoom="0.6675088"
inkscape:cx="186.10652"
inkscape:cy="69.51775"
inkscape:zoom="0.472"
inkscape:cx="14.830508"
inkscape:cy="250"
inkscape:window-x="0"
inkscape:window-y="29"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1"
inkscape:snap-page="true" /><path
inkscape:current-layer="Layer_1" /><path
d="m 150.061,232.739 -67.232,67.868 c -3.363,3.267 -5.453,7.898 -5.453,12.991 0,9.991 8.083,18.173 17.989,18.173 H 222.744 V 445.34 c 0,12.536 10.177,22.711 22.713,22.711 12.537,0 22.715,-10.175 22.715,-22.711 V 331.771 h 136.46 c 9.899,0 17.992,-8.182 17.992,-18.173 0,-4.993 -2.006,-9.536 -5.269,-12.811 L 349.938,232.644 V 77.375 h 13.631 c 12.535,0 22.715,-10.177 22.715,-22.713 0,-12.536 -10.18,-22.713 -22.715,-22.713 H 136.432 c -12.536,0 -22.713,10.177 -22.713,22.713 0,12.537 10.177,22.713 22.713,22.713 h 13.629 V 232.739 Z M 231.83,95.548 v 118.109 c 0,9.996 -8.176,18.172 -18.172,18.172 -9.994,0 -18.171,-8.176 -18.171,-18.172 V 95.548 c 0,-9.996 8.177,-18.173 18.171,-18.173 9.995,0 18.172,8.177 18.172,18.173 z"
id="fill-edit"
inkscape:connector-curvature="0"

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -1,61 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
height="32"
viewBox="0 0 32 32"
width="32"
version="1.1"
id="svg6"
sodipodi:docname="switch.svg"
inkscape:version="0.92.2 5c3e80d, 2017-08-06">
<metadata
id="metadata12">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs10" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1022"
id="namedview8"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="4.9166667"
inkscape:cx="24"
inkscape:cy="29.898306"
inkscape:window-x="0"
inkscape:window-y="29"
inkscape:window-maximized="1"
inkscape:current-layer="svg6" />
<path
d="M 23.384615,24.615384 V 18.461538 H 8.615385 v 6.153846 L 0,15.999999 8.615385,7.3846151 v 6.1538459 h 14.76923 V 7.3846151 L 32,15.999999 Z"
id="fill-edit"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccccc"
style="stroke-width:1.23076928" />
</svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -1,45 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
enable-background="new 0 0 24 24"
height="24px"
id="Layer_1"
version="1.1"
viewBox="0 0 24 24"
width="24px"
xml:space="preserve"
sodipodi:docname="time.svg"
inkscape:version="0.92.2 5c3e80d, 2017-08-06"><metadata
id="metadata13"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
id="defs11" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1015"
id="namedview9"
showgrid="false"
inkscape:zoom="9.8333333"
inkscape:cx="12"
inkscape:cy="12"
inkscape:window-x="0"
inkscape:window-y="29"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" /><path
d="m 12,2.0344069 c -5.5018378,0 -9.9631017,4.4620943 -9.9631017,9.9655931 0,5.503499 4.4612639,9.965593 9.9631017,9.965593 5.502668,0 9.963102,-4.462094 9.963102,-9.965593 0,-5.5034988 -4.460434,-9.9655931 -9.963102,-9.9655931 z m 3.664016,13.8762581 -0.290663,0.290663 c -0.241665,0.241666 -0.649424,0.253292 -0.903547,0.02408 l -3.753707,-3.28449 C 10.460316,12.71254 10.266817,12.244987 10.283427,11.902835 l 0.347965,-5.9245446 c 0.01827,-0.3429825 0.313086,-0.6220191 0.656068,-0.6220191 h 0.40942 c 0.342982,0 0.636967,0.2790366 0.653577,0.6211886 l 0.28485,4.9279861 c 0.01744,0.342982 0.217582,0.830466 0.444299,1.08708 l 2.610985,3.012931 c 0.226718,0.256614 0.215921,0.663542 -0.02657,0.905208 z"
id="fill-edit"
inkscape:connector-curvature="0"
style="clip-rule:evenodd;fill-rule:evenodd;stroke-width:0.83046609" /></svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -7,29 +7,42 @@
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Espace enseignant</title>
<title>Gestion des enseignants</title>
<!-- Icon -->
<link rel='shortcut icon' href='/favicon.ico'>
<!-- Icon -->
<link rel='shortcut icon' href='/favicon.ico'>
<!-- CSS dependencies -->
<link href="https://fonts.googleapis.com/css?family=Fira+Sans" rel="stylesheet">
<link rel='stylesheet' type='text/css' href='/css/layout.css'>
<!-- CSS dependencies -->
<link href="https://fonts.googleapis.com/css?family=Fira+Sans" rel="stylesheet">
<link rel='stylesheet' type='text/css' href='/css/font-loader.css'>
<link rel='stylesheet' type='text/css' href='/css/global.css'>
<link rel='stylesheet' type='text/css' href='/css/pop-up.css'>
<link rel='stylesheet' type='text/css' href='/css/layout.css'>
<link rel='stylesheet' type='text/css' href='/css/menu.css'>
<link rel='stylesheet' type='text/css' href='/css/header.css'>
<link rel='stylesheet' type='text/css' href='/css/container.css'>
<body>
<!-- JS dependencies -->
<script type='text/javascript' src='/js/_SERVER.js'></script>
<div id="WRAPPER" class='login'>
</head>
<body class='loading'>
<div id='LOGIN_REDIRECT'>
<div class='icon'></div>
<div class='title'><b>P</b><i>lateforme</i> <b>A</b><i>ssistée de</i> <b>T</b><i>raitement</i> <b>A</b><i>dministratif des</i> <b>T</b><i>aches d'</i><b>E</b><i>nseignement</i></div>
<a href="/api/v/1.0/professor/pdf/<?php echo $_SESSION['CAS']["id"]; ?>"><button style='font-weight: normal; margin-bottom: 1em;'>Télécharger ma fiche</button></a>
<a href="/fiche/logout"><button style='font-weight: normal'>Me déconnecter</button></a>
<div id='main-vue'></div>
<!-- POPUP WINDOW -->
<div id='POPUP'>
<div class='header'></div>
<div class='body'></div>
<div class='footer'></div>
</div>
<div id='POPUP-BG'></div>
</div>
<!-- Main loop -->
<script type='text/javascript' src='/js/bundle@fiche.js' onload="document.body.className=''"></script>
</body>
</html>

View File

@ -7,24 +7,24 @@
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Connexion</title>
<title>PTUT web title</title>
<!-- Icon -->
<link rel='shortcut icon' href='/favicon.ico'>
<!-- Icon -->
<link rel='shortcut icon' href='/favicon.ico'>
<!-- CSS dependencies -->
<link href="https://fonts.googleapis.com/css?family=Fira+Sans" rel="stylesheet">
<link rel='stylesheet' type='text/css' href='/css/font-loader.css'>
<link rel='stylesheet' type='text/css' href='/css/global.css'>
<link rel='stylesheet' type='text/css' href='/css/pop-up.css'>
<link rel='stylesheet' type='text/css' href='/css/layout.css'>
<link rel='stylesheet' type='text/css' href='/css/menu.css'>
<link rel='stylesheet' type='text/css' href='/css/header.css'>
<link rel='stylesheet' type='text/css' href='/css/container.css'>
<link rel='stylesheet' type='text/css' href='/css/container/svg.css'>
<!-- CSS dependencies -->
<link href="https://fonts.googleapis.com/css?family=Fira+Sans" rel="stylesheet">
<link rel='stylesheet' type='text/css' href='/css/font-loader.css'>
<link rel='stylesheet' type='text/css' href='/css/global.css'>
<link rel='stylesheet' type='text/css' href='/css/pop-up.css'>
<link rel='stylesheet' type='text/css' href='/css/layout.css'>
<link rel='stylesheet' type='text/css' href='/css/menu.css'>
<link rel='stylesheet' type='text/css' href='/css/header.css'>
<link rel='stylesheet' type='text/css' href='/css/container.css'>
<link rel='stylesheet' type='text/css' href='/css/container/svg.css'>
<!-- JS dependencies -->
<script type='text/javascript' src='/js/_SERVER.js'></script>
<!-- JS dependencies -->
<script type='text/javascript' src='/js/_SERVER.js'></script>
</head>
<body class='loading'>
@ -46,5 +46,4 @@
<!-- Main loop -->
<script type='text/javascript' src='/js/bundle@home.js' onload="document.body.className=''"></script>
</body>
</html>

View File

@ -72,6 +72,18 @@ module.exports = [ {
module: mod_common,
devtool: (process.env.NODE_ENV==='development') ? '#eval-source-map' : false
}, {
name: "fiche",
entry: './webpack/page/fiche.js',
output: {
path: path.resolve(__dirname, './public_html/js/bundle'),
publicPath: '/js/bundle/',
filename: 'fiche@0.js'
},
module: mod_common,
devtool: (process.env.NODE_ENV==='development') ? '#eval-source-map' : false
}, {
name: "settings",

View File

@ -1,7 +1,7 @@
<template>
<div id='CONTAINER' class='list'>
<svg xmlns="http://www.w3.org/2000/svg" :viewBox="gstore.viewBox">
<svg xmlns="http://www.w3.org/2000/svg">
<template v-if="gstore.dimensions">
<path
:d="'m ' + (gstore.dimensions.padding + gstore.dimensions.text.size) + ',' + (gstore.dimensions.padding) + ' ' +

View File

@ -33,10 +33,6 @@
</h1>
<div class='table'>
<div title='équivalents TD'>
<span>0</span>
<span>HETD</span>
</div>
<div>
<span><input type='text' placeholder='???' v-model='gstore.create_h'></span>
<span>heures à faire</span>
@ -47,7 +43,6 @@
<div class='footer'>
<button class='valid' @click='gstore.ic_handler()'>Créer l'enseignant</button>
<button class='neutral' @click='gstore.ic_reset(); gstore.create_card=false'>Annuler</button>
</div>
</section>
@ -88,24 +83,22 @@
<div class='table'>
<div>
<!-- if VIEW MODE -->
<span v-show='gstore.edit_i!=pi'>{{prof.hoursToDo}}</span>
<!-- if EDIT MODE -->
<span v-show='gstore.edit_i==pi'><input type='text' placeholder='???' v-model='gstore.edit_h'></span>
<!-- endif -->
<span>heures à faire</span>
</div>
<div title='équivalents TD'>
<span>{{ prof.equiTD }}</span>
<span>HETD</span>
</div>
<!-- if EDIT MODE -->
<div v-show='gstore.edit_i==pi'>
<span><input type='text' placeholder='???' v-model='gstore.edit_h'></span>
<span>heures à faire</span>
</div>
<!-- endif -->
<div title='heures de décalage' v-show='gstore.edit_i!=pi'>
<span :data-error='prof.equiTD < prof.hoursToDo?1:0' :data-success='prof.equiTD < prof.hoursToDo?0:1'>{{ Math.floor( (prof.equiTD < prof.hoursToDo ? prof.hoursToDo-prof.equiTD : prof.equiTD-prof.hoursToDo)*100 )/100 }}</span>
<span>{{ prof.equiTD < prof.hoursToDo ? 'sous-service' : 'sur-service' }}</span>
</div>
</div>
<!-- if VIEW MODE -->
<div v-show='gstore.edit_i!=pi' class='sub' title='total des heures prévues'><strong>{{ prof.VHCours + prof.VHTd + prof.VHTp }}</strong> heures présentielles</div>
<div v-show='gstore.edit_i!=pi' class='pdfdl' title='Télécharger la fiche' @click='gstore.id_handler(prof.idProfesseur)'>fiche</div>
<!-- if EDIT MODE -->
<div v-show='gstore.edit_i==pi' :class="gstore.edit_err.length > 0 ? 'sub warning' : 'sub'" :data-valid='gstore.edit_err_valid?1:0'>{{ gstore.edit_err }}</div>
@ -113,7 +106,7 @@
<div class='footer'>
<!-- if VIEW MODE -->
<span v-show='gstore.edit_i!=pi' :class="(prof.VHCours == 0) ? 'course' : 'course active'">{{ prof.VHCours }}h <span>CM</span></span>
<span v-show='gstore.edit_i!=pi' :class="(prof.VHCours == 0) ? 'course' : 'course active'">{{ prof.VHCours }}h <span>Cours</span></span>
<hr v-show='gstore.edit_i!=pi'>
<span v-show='gstore.edit_i!=pi' :class="(prof.VHTd == 0) ? 'td' : 'td active'" >{{ prof.VHTd }}h <span>TD</span></span>
<hr v-show='gstore.edit_i!=pi'>
@ -123,12 +116,6 @@
<button v-show='gstore.edit_i==pi' class='grey' @click='gstore.ie_toggle(-1)'>Annuler</button>
<!-- endif -->
</div>
<div class='info'>
<strong>{{ prof.hoursToDo }}h</strong> à faire, <strong>{{ prof.VHCours + prof.VHTd + prof.VHTp }}h</strong> présentielles
</div>
</section>
</div>

View File

@ -5,16 +5,13 @@
<div class='list container' data-anim-incoming='1' :data-anim-bounce='gstore.nav_anim.out?1:0'>
<section class='filter'>
<button class='back reflow search' @click='$router.back()'>Retour</button>
</section>
<!-- FILTERS -->
<section class='filter'>
<div style='flex-basis: 3.2em'></div>
<div :data-filter='gstore.order.current===0?1:0' @click='gstore.order_toggle(0)'>enseignant <span class='arrow' :data-way='gstore.order.way'></span></div>
<div :data-filter='gstore.order.current===1?1:0' @click='gstore.order_toggle(1)'>volume horaire <span class='arrow' :data-way='gstore.order.way'></span></div>
<div :data-filter='gstore.order.current===2?1:0' @click='gstore.order_toggle(2)'>formations <span class='arrow' :data-way='gstore.order.way'></span></div>
<div>enseignant</div>
<div class='null'></div>
<div>volume horaire</div>
<div>formations</div>
</section>
@ -32,7 +29,7 @@
</select>
<select v-model='gstore.ccrea.type' class='min'>
<option value='-' disabled>Type</option>
<option value='0'>CM</option>
<option value='0'>Cours</option>
<option value='1'>TD</option>
<option value='2'>TP</option>
</select>
@ -48,22 +45,17 @@
<!-- COURS -->
<section class='bcours'
<section class='cours'
v-for='(c,i) in gstore.manage.cours'
:data-id='c.idCours'
data-anim-incoming='1'
:data-anim-bounce='gstore.nav_anim.out?1:0'
:data-prof='c.idProf'
:data-vol='c.volume'
:data-form='c.formations.join(`|`)'>
:data-id='c.idCours'>
<div class='icon remove' @click='gstore.rem(0, i)'></div>
<select v-model='c.new_prof' @change='gstore.upd_prof(0, i)'>
<option value='-1' v-show='c.idProf!=-1'>Aucun enseignant affecté</option>
<option v-for='p in gstore.manage.prof' :value='p.idProfesseur' v-show='p.idProfesseur!=c.idProf'>{{ `${p.firstName} ${p.lastName}` }}</option>
</select>
<div class='cm reflow active'>{{ c.volume }}</div>
<div>Cours</div>
<div><span class='mono'>{{ c.volume }}</span> heures</div>
<div class='taglist'>
<div v-for='f in c.formations' data-action>
<span class='tag'>{{ gstore.form_by_id(f).labelForm || '???' }}</span>
@ -86,22 +78,19 @@
<!-- TD -->
<section class='btd'
<section class='td'
v-for='(td,i) in gstore.manage.td'
:data-id='td.idTD'
data-anim-incoming='1'
:data-anim-bounce='gstore.nav_anim.out?1:0'
:data-prof='td.idProf'
:data-vol='td.volume'
:data-form='td.formations.join(`|`)'>
:data-anim-bounce='gstore.nav_anim.out?1:0'>
<div class='icon remove' @click='gstore.rem(1, i)'></div>
<select v-model='td.new_prof' @change='gstore.upd_prof(1, i)'>
<option value='-1' v-show='td.idProf!=-1'>Aucun enseignant affecté</option>
<option v-for='p in gstore.manage.prof' :value='p.idProfesseur' v-show='p.idProfesseur!=td.idProf'>{{ `${p.firstName} ${p.lastName}` }}</option>
</select>
<div class='td reflow active'>{{ td.volume }}</div>
<div>TD</div>
<div><span class='mono'>{{ td.volume }}</span> heures</div>
<div class='taglist'>
<div v-for='f in td.formations' data-action>
<span class='tag'>{{ gstore.form_by_id(f).labelForm || '???' }}</span>
@ -124,22 +113,19 @@
<!-- TP -->
<section class='btp'
<section class='tp'
v-for='(tp,i) in gstore.manage.tp'
:data-id='tp.idTP'
data-anim-incoming='1'
:data-anim-bounce='gstore.nav_anim.out?1:0'
:data-prof='tp.idProf'
:data-vol='tp.volume'
:data-form='tp.formations.join(`|`)'>
:data-anim-bounce='gstore.nav_anim.out?1:0'>
<div class='icon remove' @click='gstore.rem(2, i)'></div>
<select v-model='tp.new_prof' @change='gstore.upd_prof(2, i)'>
<option value='-1' v-show='tp.idProf!=-1'>Aucun enseignant affecté</option>
<option v-for='p in gstore.manage.prof' :value='p.idProfesseur' v-show='p.idProfesseur!=tp.idProf'>{{ `${p.firstName} ${p.lastName}` }}</option>
</select>
<div class='tp reflow active'>{{ tp.volume }}</div>
<div>TP</div>
<div><span class='mono'>{{ tp.volume }}</span> heures</div>
<div class='taglist'>
<div v-for='f in tp.formations' data-action>
<span class='tag'>{{ gstore.form_by_id(f).labelForm || '???' }}</span>

View File

@ -2,20 +2,6 @@
<div id='CONTAINER' class='card'>
<div class='card filter'>
<div v-for='(filter_grp, gname) in gstore.filters' :title='gname' data-unblur-filter>
<div class='fold-toggle' :data-show='gstore.filters[gname][0].visible?1:0' @click='gstore.show_fgroup(gname)' :data-count='gstore.filters[gname][0].active.length' data-unblur-filter>{{ gname }}</div>
<div class='fold' data-unblur-filter>
<span v-for='(data, i) in filter_grp' v-if='i > 0' :class="data.active == true ? 'active' : ''" @click='gstore.toggle_filter(gname, i); gstore.filter_handler(gname);' :title='data.code' data-unblur-filter>{{ data.name }}</span>
</div>
</div>
</div>
<div class='card container' :data-anim-outgoing='gstore.nav_anim.in?1:0'>
<input data-anim='0' class='card instant-search neutral' type='text' @keyup='gstore.is_handler($event)' placeholder='Recherche instantannée' id='ue_view_instant_search'>
@ -29,23 +15,11 @@
<option v-for='form in gstore.formations' :value='form.idForm'>{{ form.labelForm }}</option>
</select>
<h1 class='pin'>
<h1>
<input type='text' placeholder='Libellé' v-model='gstore.create_label'>
<span data-visible='1'>(<input type='text' placeholder='code' v-model='gstore.create_code'>)</span>
</h1>
<div class='table little'>
<div>
<span class='notlast active reflow' data-error='0'>0 CM</span>
</div>
<div>
<span class='notlast active reflow' data-error='0'>0 TD</span>
</div>
<div>
<span class='notlast active reflow' data-error='0'>0 TP</span>
</div>
</div>
<div :class="gstore.create_err.length > 0 ? 'sub warning' : 'sub'" :data-valid='gstore.create_err_valid?1:0'>{{ gstore.create_err }}</div>
<div class='footer'>
@ -58,7 +32,6 @@
<div class='footer'>
<button class='valid' @click='gstore.ic_handler()'>Créer l'UE</button>
<button class='neutral' @click='gstore.ic_reset(); gstore.create_card=false'>Annuler</button>
</div>
</section>
@ -72,10 +45,9 @@
<!-- if VIEW MODE -->
<div class='goo-menu' v-show='gstore.edit_i!=pi'>
<div class='enabled' :data-enabled='ue.code' :data-active='ue.disabled?0:1' title='UE activée' @click="gstore.ia_handler(pi)"></div>
<div class='required' :data-required='ue.code' :data-active='ue.required?1:0' title='UE obligatoire' @click="gstore.io_handler(pi)"></div>
<div class='remove' :data-remove='ue.code' title='Supprimer' @click="gstore.ir_handler(ue.code)"></div>
<div class='edit' :data-edit='ue.code' title='Modifier' @click="gstore.ie_toggle(pi)"></div>
<div class='enabled' :data-enabled='ue.code' :data-active='ue.disabled?0:1' title='UE activée' @click="gstore.ia_handler(pi)"></div>
<div class='remove' :data-remove='ue.code' title='Supprimer' @click="gstore.ir_handler(ue.code)"></div>
<div class='edit' :data-edit='ue.code' title='Modifier' @click="gstore.ie_toggle(pi)"></div>
</div>
<!-- if VIEW MODE -->
@ -89,35 +61,19 @@
<!-- endif -->
<!-- if VIEW MODE -->
<h1 v-show='gstore.edit_i!=pi' :class='ue.required?`pin`:`pin disabled`' :title='ue.required?`obligatoire`:`optionnelle`'><span :data-strike='ue.disabled?1:0'>{{ ue.label }}</span><span :data-visible='1'>({{ ue.code }})</span></h1>
<h1 v-show='gstore.edit_i!=pi'>{{ ue.label }}<span :data-visible='1'>({{ ue.code }})</span></h1>
<!-- if EDIT MODE -->
<h1 v-show='gstore.edit_i==pi' :class='ue.required?`pin`:`pin disabled`'>
<h1 v-show='gstore.edit_i==pi' :class="ue.required ? 'warning' : ''">
<input type='text' placeholder='Libellé' v-model='gstore.edit_label'>
<!-- <span :data-visible='1'>({{ ue.code }})</span> -->
<span data-visible='1'>(<input type='text' placeholder='code' v-model='gstore.edit_code'>)</span>
</h1>
<!-- endif -->
<div class='table little'>
<div>
<span class='active reflow' :data-error='ue.nbrCours>ue.nbrProfCours || ue.modCours > 0?1:0'>{{ ue.nbrCours }} CM</span>
<span v-show='ue.nbrCours>ue.nbrProfCours' class='notlast user-icon reflow nospace' :data-tooltip='`${ue.nbrCours-ue.nbrProfCours} enseignant(s) manquant(s)`'></span>
<span v-show='ue.modCours > 0' class='notlast time-icon reflow nospace' :data-tooltip='`${ue.volumeCours-ue.modCours} à ajouter | ${ue.modCours} à enlever`'></span>
</div>
<div>
<span class='active reflow' :data-error='ue.nbrTD>ue.nbrProfTD || ue.modTD > 0?1:0'>{{ ue.nbrTD }} TD</span>
<span v-show='ue.nbrTD>ue.nbrProfTD' class='notlast user-icon reflow nospace' :data-tooltip='`${ue.nbrCours-ue.nbrProfCours} enseignant(s) manquant(s)`'></span>
<span v-show='ue.modTD > 0' class='notlast time-icon reflow nospace' :data-tooltip='`${ue.volumeTD-ue.modTD} à ajouter | ${ue.modTD} à enlever`'></span>
</div>
<div>
<span class='active reflow' :data-error='ue.nbrTP>ue.nbrProfTP || ue.modTP > 0?1:0'>{{ ue.nbrTP }} TP</span>
<span v-show='ue.nbrTP>ue.nbrProfTP' class='notlast user-icon reflow nospace' :data-tooltip='`${ue.nbrCours-ue.nbrProfCours} enseignant(s) manquant(s)`'></span>
<span v-show='ue.modTP > 0' class='notlast time-icon reflow nospace' :data-tooltip='`${ue.volumeTP-ue.modTP} à ajouter | ${ue.modTP} à enlever`'></span>
</div>
</div>
<!-- if VIEW MODE -->
<div v-show='gstore.edit_i!=pi' :class="ue.required ? 'sub pin' : 'sub pin neutral'">{{ ue.required ? 'UE obligatoire' : 'UE optionnelle' }}</div>
<div v-show='gstore.edit_i!=pi' class='sub'><strong>{{ ue.volumeCours + ue.volumeTD + ue.volumeTP }}h</strong> totales</div>
<div v-show='gstore.edit_i!=pi' class='taglist'>
<span v-if='ue.formations.length==0' class='tag invalid'>Aucune formation</span>
<span v-for='form_id in ue.formations' :class="!!gstore.form_by_id(form_id).isInternal ? 'tag search' : 'tag'">{{ gstore.form_by_id(form_id).labelForm || '???' }}</span>
@ -127,6 +83,25 @@
<div v-show='gstore.edit_i==pi' :class="gstore.edit_err.length > 0 ? 'sub warning' : 'sub'" :data-valid='gstore.edit_err_valid?1:0'>{{ gstore.edit_err }}</div>
<!-- endif -->
<div class='footer'>
<!-- if VIEW MODE -->
<span v-show='gstore.edit_i!=pi' :class="(ue.volumeCours == 0) ? 'course' : 'course active'">{{ ue.volumeCours }}h <span>Cours</span></span>
<hr v-show='gstore.edit_i!=pi'>
<span v-show='gstore.edit_i!=pi' :class="(ue.volumeTD == 0) ? 'td' : 'td active'">{{ ue.volumeTD }}h <span>TD</span></span>
<hr v-show='gstore.edit_i!=pi'>
<span v-show='gstore.edit_i!=pi' :class="(ue.volumeTP == 0) ? 'tp' : 'tp active'">{{ ue.volumeTP }}h <span>TP</span></span>
<!-- if EDIT MODE -->
<span v-show='gstore.edit_i==pi' class='course'><input type='text' placeholder='???' v-model='gstore.edit_vol.c'><span>Cours</span></span>
<hr v-show='gstore.edit_i==pi'>
<span v-show='gstore.edit_i==pi' class='td'><input type='text' placeholder='???' v-model='gstore.edit_vol.td'><span>TD</span></span>
<hr v-show='gstore.edit_i==pi'>
<span v-show='gstore.edit_i==pi' class='tp'><input type='text' placeholder='???' v-model='gstore.edit_vol.tp'><span>TP</span></span>
<!-- endif -->
</div>
<!-- if VIEW MODE -->
<div class='footer' v-show='gstore.edit_i!=pi'>
@ -139,13 +114,6 @@
</div>
<!-- endif -->
<div class='info'>
<strong class='cm reflow'>{{ ue.volumeCours}}</strong> CM
<strong class='td reflow'>{{ ue.volumeTD}}</strong> TD
<strong class='tp reflow'>{{ ue.volumeTP }}</strong> TP
<strong>{{ ue.volumeCours + ue.volumeTD + ue.volumeTP }}h</strong>
</div>
</section>
</div>
@ -163,22 +131,7 @@
name: 'CONTAINER_VIEW',
data(){
return { gstore: gstore.get }
},
beforeMount(){
// set onblur to hide filter
window.onblur.link('ue.filter', (e) => {
// ignore [data-unblur-filter] elements
if( e.target.getAttribute('data-unblur-filter') !== null )
return;
// else: hide
gstore.get.show_fgroup(-1);
});
}
}
}
</script>s

View File

@ -19,7 +19,7 @@ gstore.add('URI', document.URL.replace(/^(?:\/\/|[^\/]+)*/, '').split('/').filte
gstore.add('is_local', document.URL.replace(/^https?:\/\/([^\/:]+).*$/, '$1') == 'ptut.com');
/* (4) API instance */
window.api = new APIClient(gstore.get.is_local ? 'http://ptut.com:8080/api/v/1.0/' : `${gstore.get.HOST}/api/v/1.0/`);
window.api = new APIClient(gstore.get.is_local ? 'http://ptut.com:8080/api/v/1.0/' : 'https://ptut.xdrm.io/api/v/1.0/');
/* (5) PopUp instance */
window.popup = new PopUp();
@ -49,6 +49,10 @@ gstore.add('menu_item', {
label: 'Gestion UE',
url: 'ue',
icon: 'ue'
}, fiche: {
label: 'Fiches',
url: 'fiche',
icon: 'fiche'
}, settings: {
label: 'Administration',
url: 'settings',

View File

@ -2,7 +2,7 @@
/* (1) Load statistics
---------------------------------------------------------*/
/* (1) Initialize list */
gstore.add('stats', null);
gstore.add('stats', []);
gstore.add('dimensions', null);
/* (2) Get statistics */
@ -46,7 +46,7 @@ api.call('GET department/stats', {}, function(rs) {
gstore.get.dimensions = {
padding: 5,
text: {
size: maxLabelLength * 8.5,
size: maxLabelLength * 9.5,
alignH: 5,
alignV: 20,
},
@ -71,8 +71,6 @@ api.call('GET department/stats', {}, function(rs) {
width: 500,
precision: 4
};
gstore.get.viewBox = `0 0 ${gstore.get.dimensions.axis.width + gstore.get.dimensions.text.size + 50} ${gstore.get.dimensions.axis.height + 30}`;
});
gstore.add('colors', ["blue", "yellow", "green", "red", "purple", "lightblue", "lightred", "lightyellow", "lightgreen", "lightpurple"]);

View File

@ -281,17 +281,7 @@ gstore.add('create_h', '');
gstore.add('create_err_valid', false);
gstore.add('create_err', '');
/* (4) Define reset handler */
gstore.add('ic_reset', function(){
gstore.add('create_cat', '-');
gstore.add('create_name', '');
gstore.add('create_cas', '');
gstore.add('create_h', '');
gstore.add('create_err_valid', false);
gstore.add('create_err', '');
});
/* (5) Define create handler */
/* (4) Define create handler */
gstore.add('ic_handler', function(prof_id){
/* (4.1) Trim text input */

View File

@ -1,14 +1,9 @@
/* (1) Load formations
---------------------------------------------------------*/
/* (1) Define global filter */
gstore.add('filters', {
formations: [{ visible: false, active: [] }]
});
/* (2) Initialize list */
/* (1) Initialize list */
gstore.add('formations', []);
/* (3) Get Formations */
/* (2) Get Formations */
api.call('GET formation', {}, function(rs){
// {1} If error -> abort //
@ -19,17 +14,9 @@ api.call('GET formation', {}, function(rs){
for( var i = 0 ; i < rs.formations.length ; i++ )
gstore.get.formations.push( rs.formations[i] );
// {3} Format formation list for filters
for( var i = 0 ; i < rs.formations.length ; i++ )
gstore.get.filters.formations.push({
code: rs.formations[i].idForm,
name: rs.formations[i].labelForm,
active: false
});
});
/* (4) Get Formation label */
/* (3) Get Formation label */
gstore.add('form_by_id', function(form_id){
/* (1) Abort if wrong form_id */
@ -50,6 +37,7 @@ gstore.add('form_by_id', function(form_id){
/* (2) Load ues
---------------------------------------------------------*/
/* (1) Initialize list */
@ -71,101 +59,10 @@ api.call('GET ue/', { vh: true }, function(rs){
/* (3) Define filters' callback
---------------------------------------------------------*/
/* (1) Define global callback */
gstore.add('filter_handler', function(){
let filter_ids = [];
// 1. Get each formation ID
for( let form of gstore.get.filters.formations ){
// 1.1. Ignore if 'code' not found
if( form.code === null )
continue;
// 1.2. If active -> add to list
( form.code != null && form.active ) && filter_ids.push(form.code);
}
// 3. For UE element
main_loop: for( let ue of gstore.get.ues ){
// 3.1. Show by default
let element = document.querySelector('section[data-id=\''+ue.code+'\']');
if( !(element instanceof Element) )
continue;
element.remClass('filter-hidden');
// 3.2. If no filter -> let all visible
if( filter_ids.length <= 0 )
continue;
// 3.3. If at least one matching formatiom id -> let visible
for( let fid of filter_ids )
if( ue.formations.indexOf(fid) > -1 )
continue main_loop;
// XXXXX. If did not match -> hide
element.addClass('filter-hidden');
}
});
/* (3) Get Filters
---------------------------------------------------------*/
/* (2) Define filter group show/hide */
gstore.add('show_fgroup', function(gname){
var opened = gstore.get.filters[gname] != null && gstore.get.filters[gname][0].visible;
// {1} hide all by default//
for( var f in gstore.get.filters )
gstore.get.filters[f][0].visible = false;
// {2} If wrong @gname -> abort //
if( gstore.get.filters[gname] == null )
return;
// {3} Show selected filter //
gstore.get.filters[gname][0].visible = !opened;
});
/* (3) Define filter item toggle */
gstore.add('toggle_filter', function(gname, i){
// {1} If wrong @gname -> abort //
if( gstore.get.filters[gname] == null )
return;
// {2} If wrong @i -> abort //
if( gstore.get.filters[gname][i] == null )
return;
// {3} Toggle filter activation //
gstore.get.filters[gname][i].active = !gstore.get.filters[gname][i].active;
// {4} Update active table //
gstore.get.filters[gname][0].active.splice(0);
for( var f = 1 ; f < gstore.get.filters[gname].length ; f++ )
if( gstore.get.filters[gname][f].active )
gstore.get.filters[gname][0].active.push(f);
});
/* (4) Manage Instant Search (IS)
/* (2) Manage Instant Search (IS)
---------------------------------------------------------*/
/* (1) Define global timeout index */
gstore.add('is_to', null);
@ -179,7 +76,8 @@ gstore.add('is_buf', null);
gstore.add('is_handler', function(e){
/* (1) Remove last timeout */
!isNaN(gstore.get.is_to) && clearTimeout(gstore.get.is_to);
if( gstore.get.is_to != null )
clearTimeout(gstore.get.is_to);
/* (2) Store value in buffer */
gstore.get.is_buf = e.target.value.trim().toLowerCase();
@ -188,55 +86,27 @@ gstore.add('is_handler', function(e){
gstore.get.is_to = setTimeout(function(){
// 1. Fetch elements
let local_ptr = gstore.get.ues;
let l = gstore.get.ues.length;
let buf = gstore.get.is_buf.replace(/[.?*+^$[\]\\(\){}|-]/g, "\\$&"); // Escape errorful regex characters
var local_ptr = gstore.get.ues;
var l = gstore.get.ues.length;
// 2. For each element
main_loop: for( let e = 0 ; e < l ; e++ ){
for( var e = 0 ; e < l ; e++ ){
// 2.1. Set visible by default
let element = document.querySelector('section[data-id=\''+local_ptr[e].code+'\']');
if( !(element instanceof Element) )
continue;
// 2.1. Show by default
var element = document.querySelector('section[data-id=\''+local_ptr[e].code+'\']');
if( !element ) continue;
element.remClass('search-hidden');
// 2.2. Empty text -> let visible
if( buf.length == 0 )
continue;
// 2.2. Extract name components
var code = local_ptr[e].code.trim().toLowerCase();
var label = local_ptr[e].label.trim().toLowerCase();
// 2.3. Extract name components
let code = local_ptr[e].code.toLowerCase();
let label = local_ptr[e].label.toLowerCase();
let forms = local_ptr[e].formations;
// 2.3. Hide if does not match
var match_offset = gstore.get.is_buf.length == 0 || code.search(gstore.get.is_buf) + label.search(gstore.get.is_buf);
// 2.4-1. Match code -> let visible
if( code.search(buf) >= 0 )
continue;
// 2.4-2. Match label -> let visible
if( label.search(buf) >= 0 )
continue;
// 2.4-3. Check for each formation
for( let f of forms ){
let fetched_form = gstore.get.form_by_id(f);
// if formation not found -> ignore
if( fetched_form == null )
continue;
// if formation LABEL matches -> let visible
if( fetched_form.labelForm.toLowerCase().search(buf) >= 0 )
continue main_loop;
}
// XXXX. Hide all that did not match
element.addClass('search-hidden');
if( match_offset <= -2 )
element.addClass('search-hidden');
}
@ -263,19 +133,7 @@ gstore.add('create_vol', { c: '', td: '', tp: ''});
gstore.add('create_err_valid', false);
gstore.add('create_err', '');
/* (4) Define reset handler */
gstore.add('ic_reset', function(){
gstore.add('create_form', '-');
gstore.add('create_label', '');
gstore.add('create_code', '');
gstore.add('create_vol', { c: '', td: '', tp: ''});
gstore.add('create_err_valid', false);
gstore.add('create_err', '');
});
/* (5) Define create handler */
/* (4) Define create handler */
gstore.add('ic_handler', function(){
/* (4.1) Trim text input */
@ -646,9 +504,9 @@ gstore.add('ie_handler', function(ue_i){
/* (8) Manage instant toggle
/* (8) Manage instant admin
---------------------------------------------------------*/
/* (1) Define 'disabled' handler */
/* (1) Define admin handler */
gstore.add('ia_handler', function(ue_i){
/* (1) Abort if wrong ue_i */
@ -674,32 +532,6 @@ gstore.add('ia_handler', function(ue_i){
});
/* (1) Define 'required' / 'optional handler */
gstore.add('io_handler', function(ue_i){
/* (1) Abort if wrong ue_i */
if( ue_i == null || isNaN(ue_i) || gstore.get.ues[ue_i] == null)
return;
/* (2) Toggle current value */
var local = gstore.get.ues[ue_i];
var is_required = local.required == '1' || local.required === true;
var new_state = !is_required;
/* (3.1) Update in database */
api.call('PUT ue/'+local.code, { required: new_state }, function(rs){
/* (3.1.1) Abort on error */
if( rs.error !== 0 || rs.updated !== true )
return console.log('Impossible de changer le status \'requis\' / \'optionnel\', erreur '+rs.error);
/* (3.1.2) Success */
gstore.get.ues[ue_i].required = new_state ? 1 : 0;
});
});
/* (9) Manage 'manage' sub-page
---------------------------------------------------------*/
@ -1078,66 +910,6 @@ gstore.add('upd_prof', function(type, res_i){
});
});
/* (4) Ordering filters */
gstore.add('order', {
available: ['prof', 'volume', 'forms'],
current: 0,
way: 1 // 1 ASC, -1 DESC
});
gstore.add('order_toggle', function(ord_i){
// 1. Check params types
if( isNaN(ord_i) || gstore.get.order.available[ord_i] == null )
return;
// 2. If new ordering field -> toggle it
if( ord_i !== gstore.get.order.current )
gstore.get.order.current = ord_i;
// 3. If already selected -> toggle way
else
gstore.get.order.way *= -1;
// 4. Get all elements to order
let els = document.querySelectorAll('section[data-prof][data-vol][data-form]');
// 5. Ordering by formations
if( gstore.get.order.current === 2 ){
return els.forEach((el) => {
el.style.order = el.getAttribute('data-form').split('|').length * gstore.get.order.way
});
}
// 6. Ordering by 'volume'
if( gstore.get.order.current === 1 )
return els.forEach((el) => {
el.style.order = parseInt( el.getAttribute('data-vol') ) * gstore.get.order.way
});
// 7. Ordering by 'prof'
els.forEach((el) => {
var profId = parseInt( el.getAttribute('data-prof') );
// outside by default if no prof set
el.style.order = - gstore.get.order.way;
// professors are already sorted
for( pi in gstore.get.manage.prof ) {
if( gstore.get.manage.prof[pi].idProfesseur == profId ){
el.style.order = pi * gstore.get.order.way;
break;
}
}
});
});
@ -1274,7 +1046,7 @@ gstore.add('ccreate', function(){
/* (4) Create request */
var rq = {
code: gstore.get.router.history.current.path.split('/').pop(),
code: gstore.get.URI[2],
volume: parseInt(vol)
};
@ -1310,7 +1082,7 @@ gstore.add('ccreate', function(){
newRes[`id${restyp2}`] = rs.created_id;
newRes[`idProf`] = prof;
newRes[`volume`] = vol;
newRes[`formations`] = rs.formations;
newRes[`formations`] = [];
newRes[`add_form`] = '-';
newRes[`new_prof`] = prof;

View File

@ -43,7 +43,7 @@ window.cas_callback = function(cas_login){
// re-activate button
gstore.add('popup_opened', false);
setTimeout(function(){ gstore.get.login_class = 'neutral'; }, 3000);
setTimeout(function(){ gstore.get.login_class = 'neutral'; }, 1500);
/* (4) If error code -> display error */
}else if( !isNaN(cas_login) ){
@ -71,7 +71,7 @@ window.cas_callback = function(cas_login){
// re-activate button
gstore.add('popup_opened', false);
setTimeout(function(){ gstore.get.login_class = 'neutral'; }, 3000);
setTimeout(function(){ gstore.get.login_class = 'neutral'; }, 1500);
/* (4) If login -> reload page */
}else{
@ -81,7 +81,7 @@ window.cas_callback = function(cas_login){
var redirect_url = `/${gstore.get.URI.join('/')}`;
setTimeout(function(){ document.location = redirect_url; }, 3000);
setTimeout(function(){ document.location = redirect_url; }, 1500);
}

View File

@ -23,7 +23,6 @@ $rd-form-valid-color: '20d696';
$rd-form-neutral-color: 'b8c0c8';
$rd-form-search-color: '1d74e5';
$rd-form-invalid-color: 'ea4b35';
$rd-form-primary-color: '54627c';
// Menu
$menu-bg: #333;

View File

@ -110,7 +110,7 @@
$btn-size: 1.8em;
$btn-space: 0em;
$nb-btn: 4;
$nb-btn: 3;
$nb-spc: $nb-btn - 1;
$cont-w: $btn-size * $nb-btn + $nb-spc * $btn-space;
@ -138,14 +138,12 @@
& > div.remove[data-remove],
& > div.edit[data-edit],
& > div.enabled[data-enabled],
& > div.required[data-required],
& > div.admin[data-admin]{
background-color: darken(#fff, 12%);
/* (2.1.2) displace all but 1st element */
&.edit[data-edit]{ left: calc( 100% - #{$btn-size * 2 + $btn-space} ); }
&.remove[data-remove]{ left: calc( 100% - #{$btn-size * 3 + $btn-space * 2} ); }
&.required[data-required]{ left: calc( 100% - #{$btn-size * 4 + $btn-space * 3} ); }
}
@ -155,7 +153,6 @@
& > div.remove[data-remove],
& > div.edit[data-edit],
& > div.enabled[data-enabled],
& > div.required[data-required],
& > div.admin[data-admin]{
display: inline-block;
position: absolute;
@ -209,16 +206,6 @@
&[data-active='1']:hover{ background-image: url('/asset/svg/bell.svg@f4a118'); }
}
/* (2.4) REQUIRED switch */
&.required[data-required]{
background-image: url('/asset/svg/pin-disabled.svg@aaaaaa');
z-index: 104;
&:hover{ background-image: url('/asset/svg/pin-disabled.svg@555555'); }
&[data-active='1']{ background-image: url('/asset/svg/pin.svg@f4bd18'); }
&[data-active='1']:hover{ background-image: url('/asset/svg/pin.svg@f4a118'); }
}
}
@ -284,8 +271,6 @@
color: darken($primary-color, 5%);
font-size: 1em;
margin: .4em 0;
// flex
flex-direction: row;
justify-content: flex-start;
@ -374,16 +359,9 @@
align-items: center;
flex-wrap: nowrap;
&.little{
font-size: .7em;
white-space: nowrap;
}
/* (6.1) Column */
& > div{
flex: 1;
display: flex;
position: relative;
height: 2.3em;
@ -398,7 +376,7 @@
// flex properties
flex-direction: row;
justify-content: center;
justify-content: space-between;
align-items: center;
flex-wrap: nowrap;
@ -430,7 +408,7 @@
}
/* (6.2) Column Emphasis */
& > span:last-child:not(.notlast){
& > span:last-child{
display: block;
position: relative;
min-width: 4em;
@ -603,26 +581,7 @@
}
/* (9) Infobar */
& > div.info{
display: block;
position: relative;
margin-top: 2.1em;
margin-left: -2.1em;
height: auto;
width: calc( 100% + 2*2.1em - 2*1em);
margin-bottom: -2.1em;
padding: .3em 1em;
border-top: 1px solid #f3f3f3;
background-color: darken(#fafbfd,1%);
font-size: .7em;
color: #bbb;
}
}

View File

@ -22,8 +22,6 @@
/* (1) List element */
& > section{
order: -100000;
flex: 1 1 90%;
display: flex;
@ -45,15 +43,11 @@
/* (1.1) Element item */
& > div:not(.icon),
& > select,
& > input{
flex: 0 1 30%;
flex: 1 1 0;
// fix
&.taglist{
margin: 0;
margin-left: 1.5em;
}
&.taglist{ margin: 0; }
}
/* (1.2) Hover animation */
@ -66,16 +60,16 @@
&.create:hover{ border-left-color: $form-valid-color; }
&:hover,
&.bcours:hover{ border-left-color: $form-search-color; }
&.btd:hover{ border-left-color: $form-valid-color; }
&.btp:hover{ border-left-color: $form-invalid-color; }
&.cours:hover{ border-left-color: $form-search-color; }
&.td:hover{ border-left-color: $form-valid-color; }
&.tp:hover{ border-left-color: $form-invalid-color; }
}
/* (1.3) Select elements*/
& > select{
display: block;
display: inline-block;
position: relative;
height: 1.8em;
@ -99,7 +93,7 @@
-moz-appearance: none;
appearance: none;
// flex: 0 1 15em;
flex: 0 1 20em;
&.min{ flex: 0 1 5em; }
@ -138,15 +132,13 @@
background-image: url('/asset/svg/cross.svg@aaaaaa');
&:hover{ background-image: url('/asset/svg/cross.svg@#{$rd-form-invalid-color}'); }
}
&.hidden{ background: transparent; }
}
}
/* (2) Filter */
& > section.filter{
// padding-bottom: 0;
background-color: transparent;
@ -157,38 +149,25 @@
font-weight: bold;
text-shadow: 1px 1px 2px #fff;
& > div{
& > div:after{
content: '';
display: inline-block;
position: absolute;
width: 1.5em;
height: 1.5em;
cursor: default;
margin-top: -.25em;
margin-left: .5em;
& > span.arrow{
display: inline-block;
position: relative;
width: 1.5em;
height: 1.5em;
margin-bottom: -.3em;
background: url() center center no-repeat;
background-size: auto 100%;
cursor: pointer;
}
// selected filter
&[data-filter='1']{
color: $primary-color;
& > span.arrow{
&[data-way='-1']{ background-image: url('/asset/svg/up_arrow.svg@555555'); }
&[data-way='1']{ background-image: url('/asset/svg/down_arrow.svg@555555'); }
}
}
background: url('/asset/svg/down_arrow.svg@aaaaaa') center center no-repeat;
background-size: auto 100%;
&[data-filter='up']{ background-image: url('/asset/svg/up_arrow.svg@aaaaaa'); }
}
& > div.null:after{ content: none; }
}
/* Tags color -> darker */

View File

@ -12,8 +12,8 @@ $svg-lightpurple: #994499;
svg {
width: 100%;
height: 30vh;
width: 50em;
height: 20em;
margin: 10px;
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */

View File

@ -13,8 +13,6 @@
@import 'global/tag';
.error, [data-error='1']{ font-weight: bold; color: $form-invalid-color; }
.success, [data-success='1']{ color: $form-valid-color; }
@ -56,98 +54,4 @@ a{
.mono{
font-family: 'Mono';
font-size: .9em;
}
/* (8) Striked text */
[data-strike='1'],
.strike{
text-decoration: line-through;
opacity: .5;
}
/* (9) Icons (1em wide) */
.user-icon, .time-icon{
display: block;
position: relative;
width: 1em;
height: 1em;
background: url('/asset/svg/teacher.svg@#{$rd-form-neutral-color}') center center no-repeat;
background-size: contain;
&.time-icon{
background-image: url('/asset/svg/time.svg@#{$rd-form-neutral-color}');
}
}
/* (10) Toggling tooltip on hover */
[data-tooltip]{
position: relative;
cursor: pointer;
&:before{
content: attr(data-tooltip);
display: block;
position: absolute;
top: calc( 100% + 1em );
left: 50%;
margin-left: -.1em;
padding: .4em .6em;
border-radius: 3px / 3px;
box-shadow: 0 0 .5em 0 #fff;
font-size: .7em;
color: #ddd;
letter-spacing: 0;
background-color: #444;
transform: translateX(-50%) translateY(-50%) scale(0);
transition: transform .1s ease-in-out;
z-index: 999;
}
&:hover:not([data-tooltip='']):before{
transform: translateX(-50%) translateY(0) scale(1);
}
&:after{
content: '';
display: block;
position: absolute;
top: calc( 100% + .6em );
left: 50%;
width: .4em;
height: .4em;
background-color: #444;
transform: translateX(-50%) rotate(45deg) scale(0);
transition: transform .1s ease-in-out;
z-index: 1000;
}
&:hover:not([data-tooltip='']):after{
transform: translateX(-50%) rotate(45deg) scale(1);
}
}

View File

@ -4,13 +4,7 @@
/* (1) Warning icon */
.warning,
.user,
.time,
.pin,
.cm,
.back,
.td,
.tp{
.pin{
// add icon before
&:before{
@ -27,30 +21,6 @@
background-size: auto 90%;
}
&.nospace:before{
margin-right: 0;
}
&.reflow{
white-space: nowrap;
&:before{
font-size: 1.2em;
background-position: center bottom;
background-size: auto 70%;
}
}
&.big{
&:before{
font-size: 1.5em;
}
}
// icon color variants
&.neutral:before{ background-image: url('/asset/svg/warning.svg@#{$rd-form-neutral-color}'); }
&.valid:before{ background-image: url('/asset/svg/warning.svg@#{$rd-form-valid-color}'); }
@ -70,140 +40,12 @@
// add icon before
&:before{
background-image: url('/asset/svg/pin.svg@f4bd18');
background-image: url('/asset/svg/pin.svg@#{$rd-form-invalid-color}');
}
// icon color variants
&.disabled:before{ background-image: url('/asset/svg/pin-disabled.svg@#{$rd-form-neutral-color}'); }
&.neutral:before{ background-image: url('/asset/svg/pin.svg@#{$rd-form-neutral-color}'); }
&.valid:before{ background-image: url('/asset/svg/pin.svg@#{$rd-form-valid-color}'); }
&.search:before{ background-image: url('/asset/svg/pin.svg@#{$rd-form-search-color}'); }
}
/* (3) CM */
.cm{
// add icon before
&:before{
background-image: url('/asset/svg/course.svg@#{$menu-item-inactive}');
}
&.active:before{ background-image: url('/asset/svg/course.svg@5bb8f0'); }
// icon color variants
&.neutral:before{ background-image: url('/asset/svg/course.svg@#{$rd-form-neutral-color}'); }
&.valid:before{ background-image: url('/asset/svg/course.svg@#{$rd-form-valid-color}'); }
&.search:before{ background-image: url('/asset/svg/course.svg@#{$rd-form-search-color}'); }
}
/* (4) TD */
.td{
// add icon before
&:before{
background-image: url('/asset/svg/td.svg@#{$menu-item-inactive}');
}
&.active:before{ background-image: url('/asset/svg/td.svg@20b565'); }
// icon color variants
&.neutral:before{ background-image: url('/asset/svg/td.svg@#{$rd-form-neutral-color}'); }
&.valid:before{ background-image: url('/asset/svg/td.svg@#{$rd-form-valid-color}'); }
&.search:before{ background-image: url('/asset/svg/td.svg@#{$rd-form-search-color}'); }
}
/* (5) TP */
.tp{
// add icon before
&:before{
background-image: url('/asset/svg/tp.svg@#{$menu-item-inactive}');
}
&.active:before{ background-image: url('/asset/svg/tp.svg@e85456'); }
// icon color variants
&.neutral:before{ background-image: url('/asset/svg/tp.svg@#{$rd-form-neutral-color}'); }
&.valid:before{ background-image: url('/asset/svg/tp.svg@#{$rd-form-valid-color}'); }
&.search:before{ background-image: url('/asset/svg/tp.svg@#{$rd-form-search-color}'); }
}
/* (6) USER */
.user{
// add icon before
&:before{
background-image: url('/asset/svg/teacher.svg@#{$menu-item-inactive}');
}
// icon color variants
&.neutral:before{ background-image: url('/asset/svg/teacher.svg@#{$rd-form-neutral-color}'); }
&.invalid:before{ background-image: url('/asset/svg/teacher.svg@#{$rd-form-invalid-color}'); }
&.valid:before{ background-image: url('/asset/svg/teacher.svg@#{$rd-form-valid-color}'); }
&.search:before{ background-image: url('/asset/svg/teacher.svg@#{$rd-form-search-color}'); }
}
/* (7) TIME */
.time{
// add icon before
&:before{
background-image: url('/asset/svg/time.svg@#{$menu-item-inactive}');
}
// icon color variants
&.neutral:before{ background-image: url('/asset/svg/time.svg@#{$rd-form-neutral-color}'); }
&.invalid:before{ background-image: url('/asset/svg/time.svg@#{$rd-form-invalid-color}'); }
&.valid:before{ background-image: url('/asset/svg/time.svg@#{$rd-form-valid-color}'); }
&.search:before{ background-image: url('/asset/svg/time.svg@#{$rd-form-search-color}'); }
}
/* (8) BACK */
.back{
// add icon before
&:before{
background-image: url('/asset/svg/back.svg@#{$menu-item-inactive}');
}
// icon color variants
&.neutral:before{ background-image: url('/asset/svg/back.svg@#{$rd-form-neutral-color}'); }
&.invalid:before{ background-image: url('/asset/svg/back.svg@#{$rd-form-invalid-color}'); }
&.valid:before{ background-image: url('/asset/svg/back.svg@#{$rd-form-valid-color}'); }
&.search:before{ background-image: url('/asset/svg/back.svg@#{$rd-form-search-color}'); }
// hover
&:hover:before{ background-image: url('/asset/svg/back.svg@ffffff'); }
}

View File

@ -22,12 +22,11 @@
z-index: 150;
/* (1) left-side managers */
/* (1) Version management */
& > div.departments,
& > div.versions,
& > div.global-export{
& > div.versions{
/* (1.1) Current status */
/* (1.1) Version status */
& > div.current{
display: block;
position: relative;
@ -35,7 +34,8 @@
margin-left: 1em;
padding: .5em 1em;
padding-left: .7em;
padding-left: 2em;
border: 1px solid #ddd;
border-radius: 3px;
@ -48,79 +48,26 @@
// hover animation
&:hover{ box-shadow: 0 2px 2px darken(#fff,10%); }
// current: EXPORT / CREATE / EDIT / REMOVE icons
span.export,
span.create,
span.edit,
span.remove{
display: inline-block;
position: relative;
top: .2em;
width: 1em;
height: 1em;
// color state
&:before{
content: '';
border-radius: 3px;
display: block;
position: absolute;
top: calc( 50% - .7em/2 );
left: calc( 1em/2 + .7em/2 );
width: .7em;
height: .7em;
background: center center no-repeat;
background-size: 80% auto;
border-radius: 50%;
&.export{
background-image: url('/asset/svg/fiche.svg@b8c0c8');
background-size: 100% auto;
&:hover{
background-image: url('/asset/svg/fiche.svg@#{$rd-form-invalid-color}');
}
}
&.create{
background-image: url('/asset/svg/plus.svg@b8c0c8');
&:hover{
background-image: url('/asset/svg/plus.svg@#{$rd-form-valid-color}');
}
}
&.edit{
background-image: url('/asset/svg/a.svg@b8c0c8');
&:hover{
background-image: url('/asset/svg/a.svg@#{$rd-form-search-color}');
}
}
&.remove{
background-image: url('/asset/svg/cross.svg@b8c0c8');
&:hover{
background-image: url('/asset/svg/cross.svg@#{$rd-form-invalid-color}');
}
}
&:last-child{
margin-right: .5em;
}
background-color: $form-invalid-color;
}
&:hover > span.export{
background-image: url('/asset/svg/fiche.svg@#{$rd-form-invalid-color}');
&[data-id='-1']:before{
background-color: $form-valid-color;
}
overflow: hidden;
// editable input
& > input[type='text']{
display: inline-block;
position: relative;
width: 100%;
max-width: 10em;
margin: 0;
padding: 0;
border: none;
border-radius: 0;
// background-color: #f00;
font-size: inherit;
color: inherit;
}
}
@ -140,8 +87,6 @@
border-top: 0;
border-radius: 0 0 3px 3px;
&>:first-child{ border-top: 1px solid #ddd; }
background-color: #fff;
// box-shadow: 0 2px 2px #ddd;
@ -166,29 +111,24 @@
// hover animation
&:hover{ background-color: darken(#fff, 5%); }
// switch+create icons
// color state
&:before{
content: '';
display: block;
position: absolute;
top: calc( 50% - 1em/2 );
left: calc( .5em/2 + 1em/2 );
width: 1em;
height: 1em;
top: calc( 50% - .7em/2 );
left: calc( 1em/2 + .7em/2 );
width: .7em;
height: .7em;
background: url('/asset/svg/switch.svg@#{$rd-form-primary-color}') center center no-repeat;
background-size: auto 80%;
border-radius: 50%;
background-color: $form-invalid-color;
}
// create icon specifications
&[data-id='-1']{
&:before{
background-image: url('/asset/svg/plus.svg@#{$rd-form-valid-color}');
background-size: auto 60%;
}
&[data-id='-1']:before{
background-color: $form-valid-color;
}
}
@ -196,20 +136,16 @@
}
/* (2) Department | Version | Export layout */
/* (2) Department | Version layout */
& > div.departments > div.current{
margin-right: 0;
padding-left: 1em;
border-radius: 3px 0 0 3px;
&:before{ content: none; }
}
& > div.versions > div.current{
margin-left: 0;
border-radius: 0;
border-left: 0;
}
& > div.global-export > div.current{
margin-left: 0;
border-radius: 0 3px 3px 0;
border-left: 0;

View File

@ -281,13 +281,5 @@ body.body404{
}
.downloadButton{
text-decoration: none;
color: #3f4a5e;
box-shadow: 0px 0px 1px 1px #7f8d9b;
background: whitesmoke;
padding: 10px;
border-radius: 2px;
}

View File

@ -5,15 +5,9 @@
<!-- Department management -->
<div class='departments' data-unblur-department>
<div class='current' data-unblur-department>
<span class='create' @click='!department.create?(department.newLabel="")+(department.create=true):d_create()'></span>
<span class='remove' @click='d_remove()'></span>
<input v-if='department.create' type='text' placeholder='Nouveau nom' v-model='department.newLabel' size=''>
<span v-if='!department.create' @click='department.dialog=!department.dialog' data-unblur-department>{{ get_dcurrent().label }}</span>
</div>
<div class='department-dialog' v-show='department.dialog' data-unblur-department>
<span v-for='d in department.list' v-show='d.id!=department.current' @click='d_switch(d.id)' data-unblur-department>{{ d.label }}</span>
<div class='current' @click='d_dialog=!d_dialog' data-unblur-department>{{ get_dcurrent().label || 'département à jour' }}</div>
<div class='department-dialog' v-show='d_dialog' data-unblur-department>
<span v-for='d in dpts' v-show='d.id!=dep_id' @click='d_switch(d.id)' data-unblur-department>{{ d.label }}</span>
</div>
</div>
@ -21,27 +15,14 @@
<!-- Version management -->
<div class='versions' data-unblur-version>
<div class='current' :data-id='get_vcurrent().id' data-unblur-version>
<span class='remove' @click='v_remove()'></span>
<span class='edit' @click='!version.edit?(version.newName="")+(version.edit=true):v_edit()'></span>
<input v-if='version.edit' type='text' :placeholder='get_vcurrent().name' v-model='version.newName' size=''>
<span v-if='!version.edit' @click='version.dialog=!version.dialog' data-unblur-version>{{ get_vcurrent().name }}</span>
</div>
<div class='version-dialog' v-show='version.dialog' data-unblur-version>
<span v-for='v in version.list' @click='v_switch(v.id)' v-show='v.id!=version.current' :data-id='v.id' data-unblur-version> {{ v.name }} </span>
<span @click='v_create()' data-unblur-version data-id='-1'>Créer</span>
<div class='current' @click='v_dialog=!v_dialog' :data-id='get_vcurrent().id' data-unblur-version>{{ get_vcurrent().date || 'version à jour' }}</div>
<div class='version-dialog' v-show='v_dialog' data-unblur-version>
<span v-for='v in vers' v-show='v.id!=ver_id' @click='v_switch(v.id)' :data-id='v.id' data-unblur-version>{{ v.date || 'version à jour' }}</span>
<span @click='v_create()' data-unblur-version data-id='-1'>Nouvelle version</span>
</div>
</div>
<!-- Export all -->
<div class='global-export'>
<div class='current export' @click='global_export()'>
<span class='export'></span>
exporter
</div>
</div>
<!-- <div class='header-title'>{{ gstore.header_title }}</div> -->
</div>
@ -56,21 +37,17 @@ export default {
gstore: gstore.get,
is_connected: _SERVER.session.connected,
department: {
dialog: false,
current: _SERVER.session.department_id,
list: _SERVER.session.departments,
create: false,
newLabel: ''
},
d_dialog: false,
dep_id: _SERVER.session.department_id,
dpts: _SERVER.session.departments,
version: {
dialog: false,
current: -1,
list: [],
edit: false,
newName: ''
}
v_dialog: false,
ver_id: -1,
vers: [
{ id: -1, date: null },
{ id: 0, date: '01-02-2017' },
{ id: 1, date: '23-03-2017' }
]
};
},
methods: {
@ -79,31 +56,31 @@ export default {
---------------------------------------------------------*/
get_dcurrent(id){
// use @current, if invalid argument @id
( isNaN(id) ) && ( id = this.department.current );
// use @dep_id, if invalid argument @id
( isNaN(id) ) && ( id = this.dep_id );
// search in @list where id is @current
for( var d in this.department.list )
if( this.department.list[d].id == id )
return this.department.list[d];
// search in @dpts where id is @dep_id
for( var d in this.dpts )
if( this.dpts[d].id == id )
return this.dpts[d];
return { id: -2, name: null };
return { id: null, label: null };
},
/* (2) Get current version data
/* (2) Get current versoin data
---------------------------------------------------------*/
get_vcurrent(id){
// use @version.current, if invalid argument @id
( isNaN(id) ) && ( id = this.version.current );
// use @dep_id, if invalid argument @id
( isNaN(id) ) && ( id = this.ver_id );
// search in @ist where id is @id
for( var v in this.version.list )
if( this.version.list[v].id == id )
return this.version.list[v];
// search in @vers where id is @ver_id
for( var v in this.vers )
if( this.vers[v].id == id )
return this.vers[v];
return { id: -2, name: '-' };
return { date: null };
},
@ -112,11 +89,11 @@ export default {
d_switch(id){
// 1. De-activate dialogs
this.department.dialog = false;
this.version.dialog = false;
this.d_dialog = false;
this.v_dialog = false;
// 2. Do nothing if no change
if( this.department.current == id )
if( this.dep_id == id )
return;
// 3. Ask for department change
@ -127,7 +104,7 @@ export default {
return;
// 2. Update GUI
this.department.current = id;
this.dep_id = id;
// 3. Reload page if needed
setTimeout(() => { document.location = ''; }, 200);
@ -141,22 +118,28 @@ export default {
v_switch(id){
// 1. De-activate dialogs
this.department.dialog = false;
this.version.dialog = false;
this.d_dialog = false;
this.v_dialog = false;
// 2. Do nothing if no change
if( this.version.current == id )
if( this.ver_id == id )
return;
// 3. Ask for department change
api.call(`GET department/version/switch/${id}`, {}, function(rs){
// 3. Get version date
var verdate = this.get_vcurrent(id).date;
// 4. If null date -> go to current version
( verdate === null ) && ( verdate = '' );
// 5. Ask for department change
api.call(`PUT department/version/0/${verdate}`, {}, function(rs){
// 1. error -> do nothing
if( rs.error !== 0 )
if( rs.error !== 0 || rs.updated !== true )
return;
// 2. Update GUI
this.version.current = id;
this.ver_id = id;
// 3. Reload page if needed
setTimeout(() => { document.location = ''; }, 200);
@ -165,67 +148,13 @@ export default {
},
/* (5) Create a new empty department
---------------------------------------------------------*/
d_create(){
// 1. De-activate dialogs
this.department.dialog = false;
this.version.dialog = false;
// get current department
var cur = this.get_dcurrent();
if( cur.id < 0 || this.department.newLabel.length < 1 ){
this.department.create = false;
return;
}
var newlabel = this.department.newLabel;
// 2. Popup confirm
(new Promise( (resolve, reject) => {
popup.ask({
title: 'Confirmation de création de département',
content: `Le nouveau département <b>${newlabel}</b> va être créé; il ne contiendra aucune donnée, il permet de gérer plusieurs départements ne partageant pas les mêmes UEs, enseignants, formations, etc<br><br>Voulez-vous créer un nouveau département vide ?`,
action: 'Créer',
type: 'valid'
}, (popup_rs) => { popup_rs && resolve() });
// 3. On popup confirm
})).then( () => {
// Call API to create a new department
api.call(`POST department/`, {name:newlabel}, function(rs){
// 1. error -> popup
if( rs.error !== 0 || !rs.hasOwnProperty('created_id') ){
return popup.ask({
title: 'Erreur ('+rs.error+')',
content: 'La création de département a échoué.',
action: 'OK',
type: 'neutral'
}, () => {});
}
// 3. Update GUI
this.department.list.push( { id: parseInt(rs.created_id), name: newlabel } );
}.bind(this));
});
},
/* (5) Create a new version from now
---------------------------------------------------------*/
v_create(){
// 1. De-activate dialogs
this.department.dialog = false;
this.version.dialog = false;
this.d_dialog = false;
this.v_dialog = false;
// 2. Popup confirm
(new Promise( (resolve, reject) => {
@ -240,224 +169,34 @@ export default {
// 3. On popup confirm
})).then( () => {
let newVersionName = `${this.get_vcurrent().name}*`;
// Call API to create a new version
api.call(`POST department/version/`, {label:newVersionName}, function(rs){
api.call(`POST department/version/`, {}, function(rs){
// 1. error -> popup
if( rs.error !== 0 || !rs.hasOwnProperty('created_id') ){
return popup.ask({
title: 'Erreur ('+rs.error+')',
content: 'La création de version à échoué.',
title: 'Error ('+err_code+')',
content: 'La création de sauvegarde à échoué.',
action: 'OK',
type: 'neutral'
}, () => {});
}
// 2. Get last version id
var last_id = this.vers[ this.vers.length-1 ].id;
// 3. Update GUI
this.version.list.push( { id: parseInt(rs.created_id), name: newVersionName } );
this.vers.push( { id: last_id+1, date: rs.created_id } );
}.bind(this));
});
},
/* (6) Rename a version
---------------------------------------------------------*/
v_edit(){
// get current version
var cur = this.get_vcurrent();
if( cur.id < 0 || this.version.newName.length < 1 ){
this.version.edit = false;
return;
}
var newname = this.version.newName;
// 2. Popup confirm
(new Promise( (resolve, reject) => {
popup.ask({
title: 'Confirmation de modification de version',
content: `La version <b>${cur.name}</b> va être renommée en <b>${newname}</b><br><br>Voulez-vous valider cette modification ?`,
action: 'Valider',
type: 'search'
}, (popup_rs) => { popup_rs && resolve() });
// 3. On popup confirm
})).then( () => {
// Call API to create a new version
api.call(`PUT department/version/${cur.id}`, {label:newname}, function(rs){
// 1. error -> popup
if( rs.error !== 0 || !rs.hasOwnProperty('updated') ){
return popup.ask({
title: 'Erreur ('+rs.error+')',
content: 'La modification a échoué.',
action: 'OK',
type: 'neutral'
}, () => {});
}
// 3. Update GUI
cur.name = newname;
}.bind(this));
}).finally( () => {
this.version.edit = false;
})
},
/* (7) Remove a department
---------------------------------------------------------*/
d_remove(){
// get current department
var cur = this.get_dcurrent();
if( cur.id < 0 )
return;
// if last department -> forbid
if( this.department.list.length < 2 ){
return popup.ask({
title: 'Dernier départment',
content: `Le département <b>${cur.label}</b> ne peut être supprimé car il est le dernier disponible`,
action: 'OK',
type: 'invalid'
});
}
// 2. Popup confirm
(new Promise( (resolve, reject) => {
popup.ask({
title: 'Confirmation de suppression',
content: `Le département <b>${cur.label}</b> va être supprimé. Toutes les données seront perdues de manière définitive</b><br><br>Voulez-vous supprimer ce département ?`,
action: 'Supprimer',
type: 'invalid'
}, (popup_rs) => { popup_rs && resolve() });
// 3. On popup confirm
})).then( () => {
// Call API to delete the current department
api.call(`DELETE department/${cur.id}`, {}, function(rs){
// 1. error -> popup
if( rs.error !== 0 || !rs.hasOwnProperty('deleted') ){
return popup.ask({
title: 'Erreur ('+rs.error+')',
content: 'La suppression a échoué.',
action: 'OK',
type: 'neutral'
}, () => {});
}
// 3. Reload page
document.location = '';
}.bind(this));
});
},
/* (7) Remove a version
---------------------------------------------------------*/
v_remove(){
// get current version
var cur = this.get_vcurrent();
if( cur.id < 0 )
return;
// if last version -> forbid
if( this.version.list.length < 2 ){
return popup.ask({
title: 'Dernière version',
content: `La version <b>${cur.name}</b> ne peut être supprimée car il ne reste aucune autre version pour ce département`,
action: 'OK',
type: 'invalid'
});
}
// 2. Popup confirm
(new Promise( (resolve, reject) => {
popup.ask({
title: 'Confirmation de suppression',
content: `La version <b>${cur.name}</b> va être supprimée. Toutes les données seront perdues de manière définitive</b><br><br>Voulez-vous supprimer cette version ?`,
action: 'Supprimer',
type: 'invalid'
}, (popup_rs) => { popup_rs && resolve() });
// 3. On popup confirm
})).then( () => {
// Call API to create a new version
api.call(`DELETE department/version/${cur.id}`, {}, function(rs){
// 1. error -> popup
if( rs.error !== 0 || !rs.hasOwnProperty('deleted') ){
return popup.ask({
title: 'Erreur ('+rs.error+')',
content: 'La suppression a échoué.',
action: 'OK',
type: 'neutral'
}, () => {});
}
// 3. Reload page
document.location = '';
}.bind(this));
});
},
/* (x) Exports all data about this department's version
---------------------------------------------------------*/
global_export(){
api.call(`GET department/export`, {}, function(rs){
// 1. error -> popup
if( rs.error !== 0 || !rs.hasOwnProperty('link') ){
return popup.ask({
title: 'Erreur ('+rs.error+')',
content: 'L\'export a échoué.',
action: 'OK',
type: 'neutral'
}, () => {});
}
// 2. Launch download
document.location = rs.link;
}.bind(this));
}
},
beforeMount(){
/* (1) Try to fetch versions from API */
@ -468,22 +207,21 @@ export default {
return;
// 2. Init version list
this.version.list = [];
this.vers = [ { id: -1, date: null } ];
var idv = 0;
// 3. Store versions
for( var ver of rs.versions ){
// if current version -> set @version.current
if( _SERVER.session.version.current === ver.iddatabase )
this.version.current = ver.iddatabase
// if current version -> set @ver_id
if( _SERVER.session.version === ver )
this.ver_id = idv
// add version to list
this.version.list.push( { id: ver.iddatabase, name: ver.label, new_name: ver.label } );
this.vers.push( { id: idv++, date: ver } );
}
this.version
}.bind(this) );
@ -493,11 +231,11 @@ export default {
// only hide not [data-unblur-department] elements
if( e.target.getAttribute('data-unblur-department') === null )
this.department.dialog = false;
this.d_dialog = false;
// only hide not [data-unblur-version] elements
if( e.target.getAttribute('data-unblur-version') === null )
this.version.dialog = false;
this.v_dialog = false;
});