<?php
/**
* Created by PhpStorm.
* User: rostandnj
* Date: 25/3/19
* Time: 11:42 AM
*/
namespace App\Controller;
use App\Entity\ActeurExterne;
use App\Entity\Article;
use App\Entity\Boite;
use App\Entity\Bordereau;
use App\Entity\CadreClassement;
use App\Entity\Comment;
use App\Entity\Courrier;
use App\Entity\CourrierCollaborateur;
use App\Entity\Courtier;
use App\Entity\DemandeArchive;
use App\Entity\Document;
use App\Entity\Dossier;
use App\Entity\Etagere;
use App\Entity\News;
use App\Entity\PieceJointe;
use App\Entity\Priorite;
use App\Entity\Rayon;
use App\Entity\RegisterItem;
use App\Entity\RegleConservation;
use App\Entity\Role;
use App\Entity\Salle;
use App\Entity\Save;
use App\Entity\Service;
use App\Entity\Statut;
use App\Entity\Tache;
use App\Entity\TypeConfidentialite;
use App\Entity\TypeCourrier;
use App\Entity\TypeDossier;
use App\Entity\TypeTache;
use App\Entity\User;
use App\Entity\Workgroup;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Knp\Component\Pager\PaginatorInterface;
use phpDocumentor\Reflection\Types\Null_;
use Psr\Log\LoggerInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
use App\Twig\AppExtension;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Matcher\UrlMatcherInterface;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\Security\Core\Security;
class WebController extends AbstractController implements LogginInterfaceController
{
private $mycontainer;
private $locale;
private $session;
private $extension;
private $currentUser;
private EntityManagerInterface $em;
private Security $security;
/**
* WebController constructor.
*/
public function __construct(ContainerInterface $c,SessionInterface $s,EntityManagerInterface $em, Security $security)
{
$this->currentUser =null;
$this->extension = new AppExtension($c);
$this->session = $s;
$this->mycontainer = $c;
$this->locale="fr";
$this->em=$em;
$this->security = $security;
$lang = $this->mycontainer->get("request_stack")->getCurrentRequest()->cookies->get("lang");
if(in_array($lang,["fr","en"])) {
$this->mycontainer->get('translator')->setLocale($lang);
$this->locale = $lang;
}else{
$this->mycontainer->get('translator')->setLocale($this->locale);
}
$this->currentUser = $this->security->getUser();
$tab=[];
if($this->currentUser !==NULL)
{
if($this->currentUser->getType() == 1)
{
$roles = $this->em->getRepository(Role::class)->findBy(array("isActive"=>true));
}
else
{
$roles = $this->currentUser->getUserRoles();
}
foreach ($roles as $r)
{
$tab[]=$r->getCode();
}
}
$res = $this->mycontainer->get("user_manager")->getSystemInfo();
$this->session->set('AppData',$res["data"]);
$this->session->set('roles',$tab);
}
private function testRole($role): bool
{
if ($this->currentUser === "anon.") {
return false;
}
if ($this->currentUser->getType() == 1) {
return true;
}
$userRoles = $this->currentUser->getUserRoles();
$serviceRoles = $this->currentUser->getService()->getRoles();
$roles = array_merge(
is_array($userRoles) ? $userRoles : $userRoles->toArray(),
is_array($serviceRoles) ? $serviceRoles : $serviceRoles->toArray()
);
foreach ($roles as $r) {
if ($r->getCode() === $role) {
return true;
}
}
return false;
}
public function home(Request $request)
{
if($this->currentUser !== NULL) return $this->redirectToRoute('web_app_dashboard');
return $this->render('login.html.twig');
}
public function welcome(Request $request)
{
if($this->currentUser!=NULL) return $this->redirectToRoute('web_app_dashboard');
return $this->render('welcome.html.twig');
}
public function dashboard(Request $request)
{
//var_dump($this->currentUser->getType());
if($this->currentUser === NULL){
return $this->redirectToRoute('api_home');
}
$save = $this->em->getRepository(Save::class)->findBy(array("isActive"=>true),array("id"=>"DESC"),1,0);
$req = $this->em->getRepository(DemandeArchive::class)->findBy(array("isActive"=>true));
$dossiers = $this->em->getRepository(Dossier::class)->findBy(array("isActive"=>true));
$simple=0;
foreach ($dossiers as $d)
{
if($d->getStatut()==1) {
$simple++;
}
}
$months=[];
$label=[];
$value=[];
for ($i = 0; $i <= 7; $i++)
{
$months[date("Y-m", strtotime( date( 'Y-m-01' )." -$i months"))] =0 ;
}
foreach ($dossiers as $d)
{
//var_dump($d->getDate()->format("Y-m"));
if(array_key_exists($d->getDate()->format("Y-m"),$months))
{
$months[$d->getDate()->format("Y-m")]++;
}
}
foreach ($months as $key=>$val)
{
$label[]=$key;
$value[]=$val;
}
$totalReq = count($req);
$totalDone=0;
$date=$this->mycontainer->get('translator')->trans("no_save");
foreach ($req as $r)
{
if($r->getStatut()>1)
{
$totalDone++;
}
}
if($totalDone>0)
{
$totalDone = round($totalDone/$totalReq,2)*100;
}
if(count($save)>0)
{
$date = $save[0]->getDate()->format("d/m/Y h:i:s");
}
return $this->render('dashboard.html.twig',["dossiers"=>$simple,"demande"=>$totalDone,"date"=>$date,"label"=>$label,"value"=>$value]);
}
public function organigramme(Request $request)
{
if(!$this->testRole('M_ORG')) return $this->redirectToRoute('web_app_dashboard');
$services = $this->em->getRepository(Service::class)->findBy(array("isActive"=>true,"service"=>null),array("id"=>"ASC"));
return $this->render('organigramme.html.twig',["services"=>$services]);
}
public function systeme(Request $request)
{
if(!$this->testRole('AD_S1')) return $this->redirectToRoute('web_app_dashboard');
$res = $this->mycontainer->get("user_manager")->getSystemInfo();
return $this->render('systeme.html.twig',["system"=>$res["data"]]);
}
public function service(Request $request)
{
if(!$this->testRole('AD_S2')) return $this->redirectToRoute('web_app_dashboard');
$services = $this->em->getRepository(Service::class)->findBy(array("isActive"=>true),array("id"=>"ASC"));
$roles = $this->em->getRepository(Role::class)->findBy(array("isActive"=>true),array("id"=>"ASC"));
$cadres = $this->em->getRepository(CadreClassement::class)->findBy(["isActive"=>true],["niveau"=>"ASC"]);
$cadresLevel = $this->em->getRepository(CadreClassement::class)->findOneExampleOfEachLevel();
$levels = [];
foreach ($cadresLevel as $c){
$levels[]= $c->getNiveau();
}
if(count($levels) === 0){
$levels[]=1;
}
else{
if(max($levels)<5){
$levels[]=max($levels) + 1;
}
}
return $this->render('service.html.twig',["services"=>$services,'roles'=>$roles,"cadres"=>$cadres, 'niveaux'=>$levels]);
}
public function utilisateur(Request $request)
{
if(!$this->testRole('AD_S3')) return $this->redirectToRoute('web_app_dashboard');
$services = $this->em->getRepository(Service::class)->findBy(array("isActive"=>true),array("id"=>"ASC"));
$users = $this->em->getRepository(User::class)->loadAllUsers(1000,0);
$roles = $this->em->getRepository(Role::class)->findBy(array("isActive"=>true),array("id"=>"ASC"));
return $this->render('utilisateur.html.twig',["services"=>$services,"users"=>$users,'roles'=>$roles]);
}
public function workgroup(Request $request)
{
if(!$this->testRole('AD_S4')) return $this->redirectToRoute('web_app_dashboard');
$wkgs = $this->em->getRepository(Workgroup::class)->findBy(array("isActive"=>true),array("id"=>"ASC"));
return $this->render('workgroup.html.twig',["wkgs"=>$wkgs,]);
}
public function journal(Request $request)
{
if(!$this->testRole('AD_H1')) return $this->redirectToRoute('web_app_dashboard');
//$this->session->getFlashBag()->add('notice','hello');
return $this->render('journal.html.twig');
}
public function sauvegarde(Request $request)
{
if(!$this->testRole('AD_S1')) return $this->redirectToRoute('web_app_dashboard');
$all = $this->em->getRepository(Save::class)->findBy(array("isActive"=>true),array("id"=>"DESC"));
$todaydb = $this->em->getRepository(Save::class)->findForTodayDb();
$todayar = $this->em->getRepository(Save::class)->findForTodayArchive();
$today=[];
foreach ($todaydb as $t)
{
$today[]=$t;
}
foreach ($todayar as $t)
{
$today[]=$t;
}
return $this->render('sauvegarde.html.twig',["all"=>$all,"today"=>$today]);
}
public function profile(Request $request): Response
{
return $this->render('profile.html.twig');
}
public function support(Request $request): Response
{
$news = $this->em->getRepository(News::class)->findBy(array("isActive"=>true,"status"=>true));
return $this->render('support.html.twig',["faq"=>$news]);
}
public function upload(Request $request)
{
$file = $request->files;
return new Response();
}
public function readFile(Request $request,$name)
{
if($this->currentUser === NULL) return $this->redirectToRoute('api_home');
$fu = $this->mycontainer->get('upload_service');
$res = $fu->getFileBinary($name);
if($res['error']==true) return new Response('file not found'.$name,401);
if(!in_array($name,['man.png', 'woman.png'])){
$original = $this->mycontainer->get('user_manager')->getFile(['name'=>$name]);
if($original !== null && !in_array(explode('/',$original['type'])[0],['image','pdf'])){
$resp = new BinaryFileResponse($res["data"]);
$resp->setContentDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
$original['nom']
);
return $resp;
}
}
return new BinaryFileResponse($res["data"]);
}
public function showNotif(Request $request,$id): \Symfony\Component\HttpFoundation\RedirectResponse
{
$status = $this->em->getRepository(Statut::class)->findOneBy(array('id' =>$id, 'user' =>$this->currentUser));
if($status === null) return $this->redirectToRoute("web_app_dashboard");
if($status->getStatut()==0)
{
$status->setStatut(1);
$this->em->persist($status);
$this->em->flush();
}
if(in_array($status->getNotification()->getCode(),[30,31,33,35,36,37,38,39])){
return $this->redirectToRoute("web_my_mail");
}
switch ($status->getNotification()->getCode())
{
case 0:
return $this->redirectToRoute("web_app_demandes_validation");
break;
case 2:
return $this->redirectToRoute("web_app_demandes");
break;
case 3:
return $this->redirectToRoute("web_app_demandes");
case 11:
case 12:
case 10:
return $this->redirectToRoute("web_my_drive_share");
case 32:
return $this->redirectToRoute("web_my_mail_internal");
case 40:
return $this->redirectToRoute("web_forum_display_subject",["slug"=>$status->getNotification()->getArticle()->getSlug()]);
default:
return $this->redirectToRoute("web_app_dashboard");
break;
}
}
public function news(Request $request)
{
if(!$this->testRole('AD_A3')) return $this->redirectToRoute('web_app_dashboard');
$news = $this->em->getRepository(News::class)->findBy(array("isActive"=>true),array("id"=>"ASC"));
return $this->render('news.html.twig',["news"=>$news]);
}
public function myWorkgroup(Request $request)
{
$groups= $this->mycontainer->get('user_manager')->userWorkgroups();
return $this->render('my_groups.html.twig',["wkgs"=>$groups]);
}
public function myDrive(Request $request)
{
if($this->currentUser ==='anon.') return $this->redirectToRoute('web_app_dashboard');
$res = $this->mycontainer->get('user_manager')->driveStat();
return $this->render('my_drive.html.twig',['stat'=>$res['data']]);
}
public function mySharedDrive(Request $request)
{
if($this->currentUser ==='anon.') return $this->redirectToRoute('web_app_dashboard');
$res = $this->mycontainer->get('user_manager')->driveSharedStat();
return $this->render('my_drive_share.html.twig',['stat'=>$res['data']]);
}
public function ocr(Request $request): Response
{
return $this->render('ocr.html.twig',[]);
}
public function priorite(Request $request)
{
if(!$this->testRole('ADMI4')) return $this->redirectToRoute('web_app_dashboard');
$priorites = $this->em->getRepository(Priorite::class)->findBy(array("isActive"=>true),array("id"=>"ASC"));
return $this->render('priorite.html.twig',["priorites"=>$priorites,]);
}
public function acteurExterne(Request $request)
{
if(!$this->testRole('AD_S3')) return $this->redirectToRoute('web_app_dashboard');
$acteurs = $this->em->getRepository(ActeurExterne::class)->findBy(array("isActive"=>true),array("nom"=>"ASC"));
return $this->render('acteur.html.twig',["acteurs"=>$acteurs]);
}
public function signature(Request $request)
{
if($this->currentUser === NULL) return $this->redirectToRoute('api_home');
$fu = $this->mycontainer->get('upload_service');
$res = $fu->userFolder($this->currentUser->getId());
return $this->render('signature.html.twig',["status"=>$fu->getSignature($this->currentUser->getId()),'files'=>$res['files'],'signed'=>$res['signed']]);
}
public function diffusion(Request $request)
{
if($this->currentUser === NULL) return $this->redirectToRoute('api_home');
if(!$this->testRole('M_LDD')) return $this->redirectToRoute('web_app_dashboard');
$users = $this->em->getRepository(User::class)->loadAllUsersExecptAdmin();
$services = $this->em->getRepository(Service::class)->findBy(array("isActive"=>true),array("id"=>"ASC"));
$userWithPhone = [];
foreach ($users as $u){
if($u->getTelephone()!== null && strlen($u->getTelephone())>=9 ){
$userWithPhone[] = $u->toArray();
}
}
return $this->render('diffusion.html.twig',["users"=>$userWithPhone,'services'=>$services]);
}
public function readFileUser(Request $request,$name)
{
if($this->currentUser === NULL) return $this->redirectToRoute('api_home');
$fu = $this->mycontainer->get('upload_service');
$res = $fu->getFileBinaryUser($name,$this->currentUser->getId());
if($res['error']) return new Response('file not found',404);
if(!in_array($name,['man.png', 'woman.png'])){
$original = $this->mycontainer->get('user_manager')->getFile(['name'=>$name]);
if($original !== null && !in_array(explode('/',$original['type'])[0],['image','pdf'])){
$resp = new BinaryFileResponse($res["data"]);
$resp->setContentDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
$original['nom']
);
return $resp;
}
}
return new BinaryFileResponse($res["data"]);
}
public function readFileUserSignature(Request $request,$name)
{
if($this->currentUser === NULL) return $this->redirectToRoute('api_home');
$fu = $this->mycontainer->get('upload_service');
$res = $fu->getFileBinaryUserSignature($name,$this->currentUser->getId());
if($res['error']) return new Response('file not found',404);
if(!in_array($name,['man.png', 'woman.png'])){
$original = $this->mycontainer->get('user_manager')->getFile(['name'=>$name]);
if($original !== null && !in_array(explode('/',$original['type'])[0],['image','pdf'])){
$resp = new BinaryFileResponse($res["data"]);
$resp->setContentDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
$original['nom']
);
return $resp;
}
}
return new BinaryFileResponse($res["data"]);
}
public function forum(Request $request, PaginatorInterface $paginator){
$query = $request->query->get('keywords');
$page = $request->query->getInt('page', 1);
$limit = 20;
if($query !== null && strlen($query)>0){
$res = $this->mycontainer->get("user_manager")->simpleSearchArticle( ["keywords"=>$query]);
$entities2 = $res["data"];
}else{
$entities2 = $this->em->getRepository(Article::class)->findBy(['isActive'=>true],['id'=>'DESC']);
}
$articles = $paginator->paginate(
$entities2,$page, $limit
);
//$this->siteInfo['date_now'] =new \DateTime();
return $this->render('forum.html.twig', ['articles'=>$articles]);
}
public function displaySubject(Request $request, $slug){
$article = $this->em->getRepository(Article::class)->findOneBy(['slug'=>$slug]);
if($article === null){
$this->addFlash('notice',$this->mycontainer->get('translator')->trans("subject not found"));
return $this->redirectToRoute('web_forum');
}
$comments = $this->em->getRepository(Comment::class)->findBy(['article'=>$article,'isActive'=>true],['id'=>'DESC']);
$commentsArray = [];
foreach ($comments as $n){
$commentsArray[] = $n->toArray();
}
$recents = $this->em->getRepository(Article::class)->findRecentExcept($article->getId());
return $this->render('forum_display_subject.html.twig', ['article'=>$article,'comments'=>$commentsArray,'recents'=>$recents]);
}
public function webmail(Request $request): Response
{
return $this->render('webmail.html.twig');
}
public function videoConference(Request $request): Response
{
return $this->render('video_conference_modal.html.twig');
}
public function editWord(string $filename): Response
{
$payload = [
"document" => [
"fileType" => "docx",
"key" => uniqid(),
"title" => $filename,
"url" => "http://symfony/files/".$filename
],
"editorConfig" => [
"mode" => "edit",
"user" => [
"id" => $this->getUser()->getId(),
"name" => $this->getUser()->getUserIdentifier()
],
"callbackUrl" => "http://symfony/onlyoffice/callback"
]
];
$token = JWT::encode($payload, $this->mycontainer->getParameter('app.only_office_jwt_secret'), 'HS256');
return $this->render('office/edit.html.twig', [
'onlyofficeUrl' => $this->mycontainer->getParameter('app.only_office_url'),
'config' => json_encode($payload),
'token' => $token
]);
}
public function tempFileOLDNOSECURE(string $name): Response
{
$path = sys_get_temp_dir().'/'.$name;
if (!file_exists($path)) {
throw $this->createNotFoundException();
}
return new BinaryFileResponse($path);
}
public function tempFile(string $name, Request $request): ?BinaryFileResponse
{
// 1️⃣ Extract JWT from Authorization header
$authHeader = $request->headers->get('Authorization');
if (!$authHeader || !str_starts_with($authHeader, 'Bearer ')) {
throw new AccessDeniedHttpException('Missing JWT token');
}
$jwt = substr($authHeader, 7);
try {
$payload = JWT::decode(
$jwt,
new Key($this->getParameter('app.only_office_jwt_secret'), 'HS256')
);
} catch (\Exception $e) {
throw new AccessDeniedHttpException('Invalid JWT token');
}
;
/*if (
empty($payload->file->tmp_name)
|| $payload->file->tmp_name !== $name
) {
throw new AccessDeniedHttpException(json_encode($payload));
}*/
/*return new BinaryFileResponse(
$filePath,
200,
[
'Content-Type' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'Content-Disposition' => 'inline; filename="' . basename($name) . '"',
]
);*/
$filePath = sys_get_temp_dir() . '/' . $name;
if (!is_file($filePath)) {
throw new NotFoundHttpException('File not found');
}
return new BinaryFileResponse($filePath);
}
public function onlyOfficeCallbackOLD(Request $request, $id,$tmp_file, LoggerInterface $logger): JsonResponse
{
$data = json_decode($request->getContent(), true);
// Status 2 = document closed, ready to save
// Status 6 = forcesave while editing
if (!in_array($data['status'], [2, 6])) {
$logger->info('OnlyOffice callback: ignoring status ' . $data['status']);
return new JsonResponse(['error' => 0]);
}
$logger->info("OnlyOffice callback: start callback");
$logger->info("OnlyOffice callback: file url ". $data['url']);
$doc = $this->em->getRepository(Document::class)->findOneBy(['id'=>$id,'isActive'=>true]);
if($doc === null){
//document_not_found
$logger->info('OnlyOffice callback: document not found');
return new JsonResponse(['error' => 2]);
}
/*$filePath = sys_get_temp_dir() . '/' . $tmp_file;
if (!is_file($filePath)) {
throw new NotFoundHttpException('File not found');
}*/
// 1️⃣ Télécharger fichier modifié
$updatedContent = file_get_contents($data['url']);
$logger->info("OnlyOffice callback: file downloaded ");
// 2️⃣ Chiffrement
$encrypted = $this->mycontainer->get('hash_service')->encrypt($updatedContent);
$logger->info("OnlyOffice callback: file crypted ");
$fic = $doc->getFichier();
// 3️⃣ Sauvegarde
$path = $fic->getPath();
// Separate filename and extension
$info = pathinfo($path);
$filename = $info['filename']; // without extension
$extension = $info['extension'];
// Check if filename already has a version (_v<number>)
/* if (preg_match('/^(.*)_v(\d+)$/', $filename, $matches)) {
$baseName = $matches[1];
$version = (int)$matches[2] + 1;
} else {
$baseName = $filename;
$version = 1;
}
$newName = $baseName . '_v' . $version . '.' . $extension;
*/
$newName = $path;
$encryptedPath = $this->mycontainer->get('upload_service')->getTargetDirectory().'/'.$newName;
$logger->info("OnlyOffice callback: file encryptedPath ".$encryptedPath);
file_put_contents($encryptedPath, $encrypted);
// 4️⃣ Nettoyage temp (only when document is closed, not on forcesave)
if ($data['status'] === 2) {
@unlink(sys_get_temp_dir().'/'.$tmp_file);
}
$fic->setPath($newName);
$this->em->persist($fic);
$this->em->flush();
return new JsonResponse(['error' => 0],200);
}
public function onlyOfficeCallback(
Request $request,
int $id,
string $tmp_file,
LoggerInterface $logger
): JsonResponse
{
$rawBody = $request->getContent();
$data = json_decode($rawBody, true);
// 1️⃣ Verify JWT token from OnlyOffice
$jwtSecret = $this->mycontainer->getParameter('app.only_office_jwt_secret');
$authHeader = $request->headers->get('Authorization');
if ($authHeader && str_starts_with($authHeader, 'Bearer ')) {
// JWT in Authorization header
$token = substr($authHeader, 7);
try {
$decoded = JWT::decode($token, new Key($jwtSecret, 'HS256'));
// Deep convert stdClass to array
$data = json_decode(json_encode($decoded), true);
// OnlyOffice sends the actual callback data inside 'payload' field
if (isset($data['payload'])) {
$data = $data['payload'];
}
$logger->info('OnlyOffice callback: JWT decoded from Authorization header', ['data' => $data]);
} catch (\Exception $e) {
$logger->error('OnlyOffice callback: JWT verification failed', ['error' => $e->getMessage()]);
return new JsonResponse(['error' => 1], 403);
}
} elseif (isset($data['token'])) {
// JWT in request body
try {
$decoded = JWT::decode($data['token'], new Key($jwtSecret, 'HS256'));
$data = json_decode(json_encode($decoded), true);
// OnlyOffice sends the actual callback data inside 'payload' field
if (isset($data['payload'])) {
$data = $data['payload'];
}
$logger->info('OnlyOffice callback: JWT decoded from request body', ['data' => $data]);
} catch (\Exception $e) {
$logger->error('OnlyOffice callback: JWT verification failed', ['error' => $e->getMessage()]);
return new JsonResponse(['error' => 1], 403);
}
} else {
$logger->warning('OnlyOffice callback: No JWT token provided');
// If no JWT is sent, proceed without verification (for testing only)
}
$logger->info('OnlyOffice callback received', [
'status' => $data['status'] ?? 'unknown',
'doc_id' => $id,
'has_url' => !empty($data['url']),
'users' => $data['users'] ?? [],
'key' => $data['key'] ?? 'no-key'
]);
// Status codes:
// 0 - no document with the key identifier could be found
// 1 - document is being edited
// 2 - document is ready for saving (final save on close)
// 3 - document saving error has occurred
// 4 - document is closed with no changes
// 6 - document is being edited, but the current user is not editing (forcesave)
// 7 - error has occurred while force saving
$status = $data['status'] ?? 0;
// Handle status 1, 4: no action needed
if (in_array($status, [1, 4])) {
$logger->info('OnlyOffice callback: no save needed for status ' . $status);
$response = ['error' => 0];
$responseToken = JWT::encode($response, $jwtSecret, 'HS256');
return new JsonResponse(['error' => 0, 'token' => $responseToken]);
}
// Handle errors
if (in_array($status, [0, 3])) {
$logger->error('OnlyOffice callback: error status ' . $status, $data);
$response = ['error' => 1];
$responseToken = JWT::encode($response, $jwtSecret, 'HS256');
return new JsonResponse(['error' => 1, 'token' => $responseToken]);
}
// Handle forcesave error (status 7) - acknowledge but don't treat as critical
if ($status === 7) {
$logger->warning('OnlyOffice callback: forcesave error (status 7)', [
'doc_id' => $id,
'forcesavetype' => $data['forcesavetype'] ?? 'unknown',
'users' => $data['users'] ?? []
]);
// Return success to prevent OnlyOffice from repeatedly retrying
$response = ['error' => 0];
$responseToken = JWT::encode($response, $jwtSecret, 'HS256');
return new JsonResponse(['error' => 0, 'token' => $responseToken]);
}
// Handle saves (status 2 = final save, status 6 = forcesave during co-editing)
if (!in_array($status, [2, 6])) {
$logger->info('OnlyOffice callback: ignoring status ' . $status);
$response = ['error' => 0];
$responseToken = JWT::encode($response, $jwtSecret, 'HS256');
return new JsonResponse(['error' => 0, 'token' => $responseToken]);
}
$doc = $this->em->getRepository(Document::class)
->findOneBy(['id' => $id, 'isActive' => true]);
if (!$doc) {
$logger->warning('OnlyOffice callback: document not found');
return new JsonResponse(['error' => 2]);
}
if (empty($data['url'])) {
$logger->error('OnlyOffice callback: no URL provided in callback data', ['data' => $data]);
$response = ['error' => 1];
$responseToken = JWT::encode($response, $jwtSecret, 'HS256');
return new JsonResponse(['error' => 1, 'token' => $responseToken]);
}
$logger->info("OnlyOffice callback: downloading file from URL " . $data['url']);
// 1️⃣ Download the content from OnlyOffice
$finalContent = @file_get_contents($data['url']);
if ($finalContent === false) {
$error = error_get_last();
$logger->error('OnlyOffice callback: failed to download file', [
'url' => $data['url'],
'error' => $error['message'] ?? 'unknown error',
'doc_id' => $id
]);
$response = ['error' => 1];
$responseToken = JWT::encode($response, $jwtSecret, 'HS256');
return new JsonResponse(['error' => 1, 'token' => $responseToken]);
}
// 2️⃣ Encrypt the content
$encrypted = $this->mycontainer->get('hash_service')->encrypt($finalContent);
// 3️⃣ Save encrypted content atomically
$fic = $doc->getFichier();
$encryptedPath = $this->mycontainer->get('upload_service')->getTargetDirectory() . '/' . $fic->getPath();
$tmpEncrypted = $encryptedPath . '.tmp';
file_put_contents($tmpEncrypted, $encrypted);
rename($tmpEncrypted, $encryptedPath);
// 4️⃣ Update file size
$newSize = filesize($encryptedPath);
$fic->setTaille($newSize);
// 5️⃣ Only update dateCreation on FINAL save (status 2), not on forcesave (status 6)
// This ensures all users share the same document key during co-editing
if ($status === 2) {
$d = new \DateTime();
$fic->setDateCreation($d); // Update to trigger new document key on next open
$doc->setDateModification($d);
$doc->setFichier($fic);
@unlink(sys_get_temp_dir() . '/' . $tmp_file);
$logger->info("OnlyOffice callback: temp file removed and version updated");
}
$this->em->persist($doc);
$logger->info("OnlyOffice callback: file encrypted and saved successfully (status: {$status}, size: {$newSize})");
$this->em->flush();
// 5️⃣ Return JWT-signed response (required when JWT is enabled)
$response = ['error' => 0];
$responseToken = JWT::encode($response, $jwtSecret, 'HS256');
return new JsonResponse([
'error' => 0,
'token' => $responseToken
]);
}
public function openProject(Request $request)
{
if($this->currentUser === 'anon.') return $this->redirectToRoute('api_home');
return $this->render('open_project.html.twig',[
]);
}
}