Verified Commit ccfb8e20 authored by David Beniamine's avatar David Beniamine
Browse files

Merge branch '491-authoring_tools' of gricad-gitlab.univ-grenoble-alpes.fr:labnbook/labnbook

parents 15e404c3 2ca74cd0
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class AdminController
{
/**
* Display a list of existing and potential reports for a student.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function index(Request $request)
{
$user = Auth::user();
return view('admin/index')->with([
'user' => $user,
]);
}
}
......@@ -375,4 +375,25 @@ class ConversationController extends Controller
$conversation->remove(Auth::user());
return $this->index($request);
}
/**
* Retrieve the new messages for the current user
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function fetchMessages(Request $request)
{
$user = Auth::user();
$user->last_synchro = time();
$user->save();
$XML_doc = new \App\XMLDoc();
$XML_doc->loadXML("<modification><message></message></modification>");
$result = \App\Conversation::getConvWithNewMsg($user->id_user);
foreach ($result as $conv) {
$snapshot = \App\Message::getSnapshotFromMsg($conv->msg_content);
$XML_doc->addXMLNode("message", "conv", $conv->id_conversation, $snapshot);
}
return response($XML_doc->saveXML());
}
}
......@@ -42,59 +42,6 @@ class ResourceController extends Controller
return response()->json();
}
/**
* Add document to the report's resources.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function addDoc(Request $request)
{
$user = Auth::user();
$report = \App\Report::find((int)$request->input('id_report'));
abort_if($user->cannot('update', $report), Helper::pullPolicyErrorCode());
$name = $request->input('name');
$file = $request->file('file_document');
if ($file == null) {
return response()->json(__("Upload impossible - fichier non reçu"));
}
$error = $file->getError();
if ($error) {
return response()->json(__("Upload impossible - code erreur ").$error);
}
// vérification du type
$allowed_exts = array("gif", "jpeg", "jpg", "png", "pdf");
$allowed_types = array("image/gif", "image/jpeg", "image/jpg", "image/pjpeg", "image/x-png", "image/png", "application/pdf");
$extension = $file->getClientOriginalExtension();
$finfo = \finfo_open(FILEINFO_MIME_TYPE);
$type = strtolower(\finfo_file($finfo, $file->path()));
\finfo_close($finfo);
if (!in_array($type, $allowed_types) || !in_array($extension, $allowed_exts)) {
unlink($file->path()) ;
return response()->json(__("Vous ne pouvez uploader que des fichiers pdf ou image (jpg, gif, png)."));
}
// tout va bien
// création du nom de fichier (pour eviter des doublons malheureux)
$now = new \DateTime("now");
$time = $now->format('Y-m-d_H-i-s');
$fileid = $user->id_user.'_'.$time.'.'.$extension ;
// on checke les dossiers
$content_dir = storage_path('app/public/reports/'.$report->id_report); // dossier où sera placé le fichier
if (!is_dir($content_dir)) {
@mkdir($content_dir, 0777, true) ;
}
// copie du fichier dans le dossier
if (!move_uploaded_file($file->path(), $content_dir.'/'.$fileid)) {
return response()->json(__("Impossible de copier le fichier dans le dossier de destination."));
}
chmod($content_dir.'/'.$fileid, 0777);
Resource::addDoc($report->id_report, $fileid, $name, 'file');
Trace::logAction(Trace::ADD_RESOURCE, ['id_report' => $report->id_report, 'url' => "$content_dir/$fileid"]);
return response()->json('');
}
/**
* Deletes a document to the report's resources.
*
......@@ -125,4 +72,48 @@ class ResourceController extends Controller
$resource->save();
return response()->json();
}
/**
* Deletes a document from the mission's resources.
*
* @param \Illuminate\Http\Request $request
* @param \App\Resource $resource
* @return \Illuminate\Http\Response
*/
public function removeFromMission(Request $request, Resource $resource)
{
$user = Auth::user();
$mission = $resource->mission;
abort_if($user->cannot('update', $mission), Helper::pullPolicyErrorCode());
$position = $resource->position;
$type = $resource->type;
if ($resource->delete()) {
$mission->traceUpdate();
}
if ($type != 'assignment') {
$mission->updateResourcesPosition((int)$position);
}
}
/**
* Update's a missions document
* @param \Illuminate\Http\Request $request
* @param \App\Resource $resource
* @return \Illuminate\Http\Response
*/
public function updateFromMission(Request $request, Resource $resource)
{
$user = Auth::user();
$mission = $resource->mission;
abort_if($user->cannot('update', $mission), Helper::pullPolicyErrorCode());
$resource->name = strip_tags($request->input('name'));
$resource->res_path = strip_tags($request->input('res_path', $resource->res_path));
if ($resource->save()) {
$mission->traceUpdate();
}
}
}
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use \App\Mission;
use \App\Report;
use \App\Resource;
use \App\Trace;
class StorageController
{
/**
* @param string $ressourceType
* @param string $fileExtension
* @param string $fileMimeType
* @param boolean $isTeacher
* @return array ['valid' => boolean, 'message' => string]
*/
private function validateFileType($ressourceType, $fileExtension, $fileMimeType, $is_teacher)
{
if ($ressourceType === "assignment" || $ressourceType === "tmce_pdf") {
if (! ($fileExtension === "pdf" && $fileMimeType === "application/pdf")) {
return [
'valid' => false,
'message' => __("Vous ne pouvez uploader que des fichiers pdf.")
];
}
} elseif ($ressourceType === "tmce_img" || $ressourceType == 'res_doc' && !$is_teacher) {
$allowedExts = [
"pdf",
"gif", "jpeg", "jpg", "png", // image
];
$allowedMimeTypes = [
"application/pdf",
"image/gif", "image/jpeg", "image/jpg", "image/pjpeg", "image/x-png", "image/png",
];
if (!in_array($fileMimeType, $allowedMimeTypes) || !in_array($fileExtension, $allowedExts)) {
return [
'valid' => false,
'message' => __("Vous ne pouvez uploader que des fichiers pdf ou image (jpg, gif, png).")
];
}
}
return ['valid' => true];
}
/**
* @param int $bytes
* @return int
*/
private function toMegabytes($bytes)
{
return round($bytes / 1024 / 1024);
}
/**
* Convert a PHP-ini human size (4M) to a number of bytes.
*
* @param string $hsize
* @return int
*/
private function parseHumanSize($hsize)
{
$matches = [];
if (preg_match('/^(\d+)([bkmgtpezy]?)$/i', $hsize, $matches)) {
if ($matches[2]) {
$power = pow(1024, strpos('bkmgtpezy', strtolower($matches[2])));
return round($matches[1] * $power);
} else {
return round($matches[1]);
}
} else {
return null;
}
}
/**
* @param int $error
* @return \Illuminate\Http\Response
*/
private function handleFileError($error)
{
switch ($error) {
case 1: // UPLOAD_ERR_INI_SIZE
header("Content-type: application/json", true, 413);
$max = $this->toMegaBytes(min(
array_map([$this, 'parseHumanSize'], [ini_get('post_max_size'), ini_get('upload_max_filesize')])
));
$out = __("La taille maximale autorisée par la configuration PHP du serveur est :max Mo.", ['max' => $max]);
break;
case 2: // UPLOAD_ERR_FORM_SIZE
$out = __("Erreur : Le fichier dépasse la limite autorisée (html form)");
break;
case 3: // UPLOAD_ERR_PARTIAL
$out = __("Erreur : L'envoi du fichier a été interrompu pendant le transfert");
break;
case 4: // UPLOAD_ERR_NO_FILE
$out = __("Erreur : Le fichier que vous avez envoyé a une taille nulle");
break;
default:
$out = __("Erreur inconnue lors de l'evoi");
break;
}
return response()->json($out, 409);
}
/**
* Returns a uniq filename for a user with a given suffix
* @param int id_user
* @param string $suffix
* @return string
*/
private function getUniqueName($id_user, $suffix)
{
$now = new \DateTime("now");
$time = $now->format('Y-m-d_H-i-s');
return $id_user.'_'.$time.$suffix ;
}
/**
* Sanitize a fileName
* @param string name
* @return string
*/
private function sanitize($name)
{
$fileid = Str::ascii(trim($name));
$fileid = preg_replace("/[ _]+/", "-", $fileid); // remplacement des espaces et "_" par un seul tiret
return preg_replace("/[^a-z0-9-\.]/i", "", $fileid); // suppression des caracteres speciaux
}
/**
* uploads a file.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function upload(Request $request)
{
$data = $request->validate([
'id_context' => 'int|required',
'context' => 'in:mission,report|required',
'type' => 'string|required',
'name' => 'string|nullable',
'id_resource' => 'int|nullable',
]);
$file = $request->file('file_document');
if ($file == null) {
return response()->json(__("Upload impossible - fichier non reçu"), 409);
}
if (!$file->isValid()) {
return $this->handleFileError($file->getError());
}
$extension = $file->getClientOriginalExtension();
$mime_type = $file->getClientMimeType();
$is_teacher = $data['context'] == 'mission';
$status = $this->validateFileType($data['type'], $extension, $mime_type, $is_teacher);
if (!$status['valid']) {
unlink($file->path()) ;
return response()->json($status['message'], 409);
}
$user = Auth::user();
if ($data['context'] == 'report') {
// Report resource
$object = Report::find($data['id_context']);
$base_dir = 'reports/'.$object->id_report;
// création du nom de fichier (pour eviter des doublons malheureux)
$fileid = $this->getUniqueName($user->id_user, ".".$extension);
$name = strip_tags($data['name']);
} else {
$object = Mission::find($data['id_context']);
$base_dir = "/missions/". $object->id_mission;
$fileid = $this->sanitize($file->getClientOriginalName());
if ($data['name'] != null) {
$name = strip_tags($data['name']);
} else {
$name = '';
}
if ($data['type'] === "tmce_img" || $data['type'] === "tmce_pdf") {
// Mission tinymce
$base_dir .= "/tinymce";
$name = $fileid;
$fileid = $this->getUniqueName($user->id_user, "_".$fileid);
} else {
// Mission resource
$base_dir .= "/resources";
}
}
$url = "/storage/$base_dir/$fileid";
$dir_dest = storage_path('app/public/'.$base_dir);
// Last security check
abort_if($user->cannot('update', $object), 403);
if (!is_dir($dir_dest)) {
@mkdir($dir_dest, 0777, true);
}
// copie du fichier dans le dossier
if (!move_uploaded_file($file->path(), $dir_dest.'/'.$fileid)) {
return response()->json(__("Impossible de copier le fichier dans le dossier de destination."), 500);
}
chmod($dir_dest.'/'.$fileid, 0777);
// Copie reussie
if ($data['context'] == 'report') {
// Report resource
Resource::addDoc($object->id_report, $fileid, $name, 'file');
Trace::logAction(Trace::ADD_RESOURCE, ['id_report' => $object->id_report, 'url' => $url]);
} else {
// Mission
// Gestion des resources
$id_resource = $data['id_resource'];
$last_rd_position = "" ;
if ($data['type'] === "assignment") {
$resource = Resource::where('id_mission', $object->id_mission)
->where('res_type', $data['type'])
->first();
if ($resource) { // remplacement d'un assignment
$resource->replaceFile($name, $fileid);
} else {
$id_resource = Resource::addDoc(null, $fileid, $name, "assignment", $object->id_mission);
Trace::logAction(Trace::TEACHER_ADD_RESOURCE, ['id_mission' => $object->id_mission, 'name' => $fileid]);
}
} elseif ($data['type'] == "res_doc") { // traitement des ressources
if ($id_resource) { // on est dans le cas d'une modification
// suppression de l'ancien fichier
$resource = Resource::find($id_resource);
$resource->replaceFile($name, $fileid);
} else { // on est dans le cas d'un ajout
$last_rd_position = $object->nextRDPos() ;
$id_resource = Resource::addDoc(null, $fileid, $name, "file", $object->id_mission, $last_rd_position);
Trace::logAction(Trace::TEACHER_ADD_RESOURCE, ['id_mission' => $object->id_mission, 'name' => $name]);
}
}
if ($id_resource) { // modification du nom de la ressource pour éviter les doublons
$new_path = $id_resource."-".$fileid;
if (rename($dir_dest."/".$fileid, $dir_dest."/".$new_path)) {
$resource = Resource::find($id_resource);
$resource->res_path = $new_path;
$resource->save();
// Update fileid and url
$fileid = $new_path;
$url = "/storage/$base_dir/$fileid";
}
}
return response()->json([
"res_type" => $data['type'],
"file_name" => $fileid,
"name" => $name,
"pos" => $last_rd_position,
"id_res" => $id_resource,
"file_ext" => $extension,
"res_path" => $url,
]);
}
}
}
......@@ -2,14 +2,39 @@
namespace App\Http\Controllers\Teacher;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Http\Controllers\Controller;
use App\Mission;
use App\Trace;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Processes\ExportLabdocs;
class MissionController extends Controller
{
/**
* Checks that the labdoc exists, and belongs to the mission and the user
* can edit / view the mission
* Return the labdoc or abort if the access is not legal
* @param \App\Mission $mission
* @param int $id_labdoc
* @param \App\User $user
* @param string $action view|update
* @return \App\Labdoc
*/
private function checkLabdocAccess($mission, $id_labdoc, $user, $action)
{
// Can we act on the mission ?
abort_if($user->cannot($action, $mission), 403);
// Does the LD exists
$labdoc = \App\Labdoc::find($id_labdoc);
abort_if($labdoc == null, 404, __("Le labdoc n'existe pas"));
// Is the ld part of the mission ?
abort_if($labdoc->reportPart->id_mission != $mission->id_mission, 409, __("Le labdoc ne fait pas parti de la mission courante"));
return $labdoc;
}
/**
* Display a list of existing and potential reports for a student.
*
......@@ -180,4 +205,517 @@ class MissionController extends Controller
return response()->json($missions);
}
/**
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function edit(Request $request)
{
$user = Auth::user();
$id_mission = (int)$request->input('idm');
if (!$id_mission) {
return redirect('/teacher/missions');
}
$mission = \App\Mission::find($id_mission);
$error = "";
if (!$mission || $user->cannot('view', $mission)) {
$error = __("Mission non trouvée");
} elseif ($user->cannot('manage', $mission)) {
$error = __('Vous ne pouvez pas modifier la mission car vous êtes simplement "tuteur" de cette mission sur LabNbook. Pour devenir "concepteur" demandez à un concepteur de la mission de vous attribuer ce statut.');
}
if ($error) {
\App\Helper::AddAlert('danger', $error);
return redirect('/teacher/missions');
}
return view('teacher/missions/edit')->with([
'current_mission' => $mission,
'user' => $user,
]);
}
/**
* Met a jour un champ de la mission donne
*
* @param \Illuminate\Http\Request $request
* @param \App\Mission $mission
* @return \Illuminate\Http\Response
*/
public function updateField(Request $request, Mission $mission)
{
abort_if(Auth::user()->cannot('update', $mission), 403);
$data = $request->validate([
'field' => 'string|required',
'value' => 'string|required',
]);
$value = $data['value'];
$field = $data['field'];
if ($field == 'code') {
if (\App\Mission::where('code', $value)->exists()) {
return response()->json(__("Ce code de mission est utilisé par une autre mission. Veuillez en choisir un nouveau."));
}
} elseif ($field === 'description') {
$value = \App\HtmlFilter::clean($value);
} elseif (!preg_match('/^\w+$/', $field)) {
return response()->json(__("Champ incorrect"), 409);
}
$mission->$field = $value;
$mission->save();
}
private function teacherLinkModified($mission, $user) {
$mission->traceUpdate();
return view('teacher/missions/_linkedTeachers')->with([
'teachers' => $mission->getLinkedTeachers(),
'id_teacher' => $user->id_user,
'id_mission' => $mission->id_mission,
]);
}
/**
* lie un enseignant à une mission à partir de son nom
* n'affiche rien si le nom n'est pas trouvé
*
* @param \Illuminate\Http\Request $request
* @param \App\Mission $mission
* @return \Illuminate\Http\Response
*/
public function linkTeacherFromName(Request $request, Mission $mission)
{
$user = Auth::user();
abort_if($user->cannot('update', $mission), 403);
$name = $request->input('name', '');
$num = 0 ;
if (substr($name, -1) == ")") { // cas de comptes multiples
$name = substr($name, 0, -1) ;
$pos = strpos($name, "(") ;
$num = substr($name, $pos+1) ;
$name = substr($name, 0, $pos-1) ;
}
$users = \App\Teacher::getFromNameForUser($name, $user->id_user);
$i = 0 ;
foreach ($users as $u) {
if ($i == $num) { // gestion des comptes multiples
if (!$mission->teachers()->where('teacher.id_teacher', $u->id_user)->exists()) {
if ($mission->linkTeacher($u->id_user, 'teacher')) {
return $this->teacherLinkModified($mission, $user);
}
}
}
$i++;
}
}
/**
* Met a jour le type de l enseignant associe
*
* @param \Illuminate\Http\Request $request
* @param \App\Mission $mission
* @return \Illuminate\Http\Response
*/
public function updateLinkedTeacher(Request $request, Mission $mission)
{