<?php
// +-------------------------------------------------+
// © 2002-2004 PMB Services / www.sigb.net pmb@sigb.net et contributeurs (voir www.sigb.net)
// +-------------------------------------------------+
// $Id: ajax_demandes.inc.php,v 1.17.6.1.2.6 2026/08/14 08:40:20 dbellamy Exp $

if (stristr($_SERVER['REQUEST_URI'], ".inc.php")) die("no access");

global $base_path, $class_path, $quoifaire;
global $id_demande, $id_action;

require_once($base_path."/classes/demandes.class.php");
require_once($base_path."/classes/demandes_actions.class.php");
require_once($base_path."/classes/demandes_notes.class.php");
require_once($base_path."/classes/demandes/demandes_note.class.php");
require_once($base_path."/includes/templates/demandes.tpl.php");
require_once($base_path."/includes/templates/demandes_actions.tpl.php");
require_once($base_path."/includes/templates/demandes_notes.tpl.php");
require_once($base_path."/includes/mail.inc.php");
require_once($class_path.'/audit.class.php');
require_once($class_path.'/explnum_doc.class.php');

switch($quoifaire){
	case 'show_list_action':
		global $id_empr;
		$id_demande = intval($id_demande);
		$demande=new demandes($id_demande,false);
		if(!$demande->id_demande || $demande->num_demandeur != $id_empr){
			ajax_http_send_response('');
			break;
		}
		ajax_http_send_response(demandes_actions::show_list_actions($demande->actions,$id_demande,0,false));
		break;
	case 'show_dialog':
		global $id_empr;
		$id_action = intval($id_action);
		$action=new demandes_actions($id_action,false);
		if(!$action->id_action || $action->num_demandeur != $id_empr){
			ajax_http_send_response('');
			break;
		}
		ajax_http_send_response(demandes_notes::show_dialog($action->notes, $action->id_action,$action->num_demande,"demandes-show_consult_form"));
		break;
	case "get_pperso_form":
		global $type_demande, $id_empr;
		$type_demande = intval($type_demande);
		$id_demande = intval($id_demande);
		//id_demande=0 est le cas legitime de rafraichissement du formulaire
		//de creation (aucune demande a posseder pour l'instant). Sinon, la
		//demande doit appartenir a l'emprunteur courant : show_editable_fields()
		//pre-remplit le formulaire avec les valeurs deja enregistrees des
		//champs personnalises (ex: priorite), qui ne doivent pas fuiter.
		if($id_demande){
			$demande_proprietaire = new demandes($id_demande, false);
			if(!$demande_proprietaire->id_demande || $demande_proprietaire->num_demandeur != $id_empr){
				ajax_http_send_response('');
				break;
			}
		}
		$demande = new demandes();
		ajax_http_send_response($demande->get_pperso_form_content($id_demande, $type_demande));
		break;
	case 'change_read':
		global $id_demande, $id_action, $id_empr;
		if($id_demande) {
			$id_demande = intval($id_demande);
			$demande = new demandes($id_demande, false);
			if(!$demande->id_demande || $demande->num_demandeur != $id_empr){
				ajax_http_send_response(0);
				break;
			}
			//$change = demandes::change_read($demande);
		} elseif($id_action) {
			$id_action = intval($id_action);
			$action = new demandes_actions($id_action);
			if(!$action->id_action || $action->num_demandeur != $id_empr){
				ajax_http_send_response(0);
				break;
			}
			$id_demande = intval($action->num_demande);

			foreach($action->notes as $note){
				demandes_note::note_read($note->id_note,true,"_opac");
			}
			$change = demandes_actions::change_read($action);
			$demande = new demandes($id_demande, false);
		}
		//On passe la demande en lue si toutes les actions sont lues (ou qu'il n'y en a pas encore)
		$all_read = true;
		foreach($demande->actions as $action){
			if($action->actions_read_opac == 1){
				$all_read = false;
				break;
			}
		}
		if($all_read){
			demandes::demande_read($id_demande, true, "_opac");
		}
		if($change) {
			ajax_http_send_response(1);
		} else {
			ajax_http_send_response(0);
		}
		break;
	case "get_allowed_types":
	    global $id_theme;
	    $id_theme = intval($id_theme);
	    $allowed_types = [];
	    $requete = 'SELECT * FROM demandes_theme WHERE id_theme='.$id_theme;
	    $result = pmb_mysql_query($requete);
	    if(pmb_mysql_num_rows($result)) {
	        $data = pmb_mysql_fetch_object($result);
	        $allowed_types = encoding_normalize::json_decode($data->allowed_types, true);
	    }
	    ajax_http_send_response($allowed_types);
	    break;
	case 'ajax_list_demandes':
		global $demandes_list_page, $idetat, $demandes_list_theme, $demandes_list_type, $demandes_list_q, $demandes_list_nb_per_page, $custom_filters;
		$demande = new demandes();
		ajax_http_send_response($demande->render_list_fragment(array(
			'page' => $demandes_list_page ?? 1,
			'etat' => $idetat ?? 0,
			'theme' => $demandes_list_theme ?? 0,
			'type' => $demandes_list_type ?? 0,
			'q' => $demandes_list_q ?? '',
			'nb_per_page' => $demandes_list_nb_per_page ?? 10,
			'custom_filters' => is_array($custom_filters ?? null) ? $custom_filters : array(),
		)));
		break;
	case 'ajax_add_note':
		global $contenu_note, $idaction, $idnote, $iddemande, $id_empr;
		$iddemande = intval($iddemande);
		$idaction = intval($idaction);
		$demande = new demandes($iddemande, false);
		if($demande->id_demande && $demande->num_demandeur == $id_empr && trim(strval($contenu_note)) !== ''){
			$note = new demandes_note($idnote, $idaction);
			$note->set_properties_from_form();
			$note->save();
			$note->fetch_data();
			$note->note_majParent($iddemande, "_gestion");
			$note->note_majParent($iddemande, "_opac");
			ajax_http_send_response(array(
				'ok' => 1,
				'id_note' => $note->id_note,
				'createur' => $note->createur_note,
				'date' => $note->date_note,
				'contenu' => $note->contenu,
				'notes_type_user' => $note->notes_type_user,
			));
		} else {
			ajax_http_send_response(array('ok' => 0));
		}
		break;
	case 'ajax_suppr_demande':
		global $iddemande, $id_empr;
		$iddemande = intval($iddemande);
		$demande = new demandes($iddemande, false);
		if($demande->id_demande && $demande->num_demandeur == $id_empr){
			demandes::delete($demande);
			ajax_http_send_response(array('ok' => 1));
		} else {
			ajax_http_send_response(array('ok' => 0));
		}
		break;
	case 'ajax_add_demande':
		global $id_theme, $id_type, $sujet, $id_empr;
		global $titre, $iddemande, $idetat, $date_debut, $date_fin, $date_prevue, $idempr, $linked_record_id, $progression;

		$id_theme = intval($id_theme);
		$id_type = intval($id_type);
		$sujet = trim(strval($sujet));

		//Le theme et le type doivent correspondre a des lignes reelles : la
		//liste OPAC fait un INNER JOIN dessus, une demande avec theme/type
		//invalide (0 ou inexistant) disparaitrait silencieusement de la liste.
		$theme_ok = false;
		if($id_theme){
			$r = pmb_mysql_query("select id_theme from demandes_theme where id_theme=".$id_theme);
			$theme_ok = pmb_mysql_num_rows($r) > 0;
		}
		$type_ok = false;
		if($id_type){
			$r = pmb_mysql_query("select id_type from demandes_type where id_type=".$id_type);
			$type_ok = pmb_mysql_num_rows($r) > 0;
		}

		//Le theme peut restreindre les types autorises (demandes_theme.allowed_types,
		//meme colonne que "get_allowed_types" ci-dessus). Un theme sans restriction
		//configuree (colonne vide ou JSON invalide) reste permissif, comme avant ce
		//controle.
		$type_allowed = true;
		if($theme_ok && $type_ok){
			$r = pmb_mysql_query("select allowed_types from demandes_theme where id_theme=".$id_theme);
			$theme_row = pmb_mysql_fetch_object($r);
			$allowed_types = encoding_normalize::json_decode($theme_row->allowed_types, true);
			if(is_array($allowed_types) && count($allowed_types)){
				$type_allowed = !empty($allowed_types[$id_type]);
			}
		}

		if($sujet === '' || !$theme_ok || !$type_ok || !$type_allowed){
			ajax_http_send_response(array('ok' => 0));
			break;
		}

		//Titre derive de la premiere ligne non vide de la description :
		//100 caracteres maximum, suffixe "[...]" si tronque (inclus dans
		//la limite de 100).
		$premiere_ligne = '';
		foreach(preg_split('/\r\n|\r|\n/', $sujet) as $ligne){
			if(trim($ligne) !== ''){
				$premiere_ligne = trim($ligne);
				break;
			}
		}
		$max_len = 100;
		$suffix = '[...]';
		$ligne_len = function_exists('mb_strlen') ? mb_strlen($premiere_ligne) : strlen($premiere_ligne);
		if($ligne_len <= $max_len){
			$titre = $premiere_ligne;
		} elseif(function_exists('mb_substr')){
			$titre = mb_substr($premiere_ligne, 0, $max_len - mb_strlen($suffix)) . $suffix;
		} else {
			$titre = substr($premiere_ligne, 0, $max_len - strlen($suffix)) . $suffix;
		}

		$iddemande = 0;
		$idetat = 1;
		$today = date('Y-m-d');
		$date_debut = $today;
		$date_fin = $today;
		$date_prevue = $today;
		//idempr force cote serveur (jamais depuis le client) : evite qu'un
		//appel malveillant cree une demande pour un autre emprunteur.
		$idempr = intval($id_empr);
		$linked_record_id = 0;
		$progression = 0;

		$demande = new demandes();
		$demande->set_properties_from_form();
		$demande->save();

		//Creation d'une action initiale
		//sans elle, une nouvelle demande n'a aucune action a laquelle rattacher une eventuelle piece jointe deposee des la creation. 
		//Necessite opac_demandes_no_action = 0
		$action = new demandes_actions();
		$action->num_demande = $demande->id_demande;
		$action->type_action = 1;
		$action->statut_action = 1;
		$action->date_action = $today;
		$action->deadline_action = $today;
		$action->actions_num_user = $idempr;
		$action->actions_type_user = 1;
		$action->save();

		ajax_http_send_response(array('ok' => 1, 'id_demande' => $demande->id_demande, 'id_action' => $action->id_action));
		break;
	case 'ajax_confirm_demande':
		//Stub : confirme l'envoi de la demande. A terme, envoie un email de
		//confirmation au demandeur ; pour l'instant, se contente de verifier
		//que la demande existe bien et appartient au demandeur courant.
		global $iddemande, $id_empr;
		$iddemande = intval($iddemande);
		$demande = new demandes($iddemande, false);
		if($demande->id_demande && $demande->num_demandeur == $id_empr){
			ajax_http_send_response(array('ok' => 1));
		} else {
			ajax_http_send_response(array('ok' => 0));
		}
		break;
	case 'ajax_upload_attachment':
		//Piece jointe liee a une action de demande, sur le meme principe que
		//demandes_actions::save_docnum() cote Gestion (explnum_doc +
		//explnum_doc_actions), mais en AJAX et avec verification
		//d'appartenance (l'action est fournie par le client).
		global $idaction, $id_empr;
		$idaction = intval($idaction);

		$action = new demandes_actions($idaction);
		if(!$action->id_action){
			ajax_http_send_response(array('ok' => 0));
			break;
		}
		$demande_proprietaire = new demandes($action->num_demande, false);
		if(!$demande_proprietaire->id_demande || $demande_proprietaire->num_demandeur != $id_empr){
			ajax_http_send_response(array('ok' => 0));
			break;
		}

		if(empty($_FILES['f_fichier']) || $_FILES['f_fichier']['error'] !== UPLOAD_ERR_OK || !is_uploaded_file($_FILES['f_fichier']['tmp_name'])){
			ajax_http_send_response(array('ok' => 0));
			break;
		}

		//Validation via Pmb/Common/Library/Upload avant toute creation
		//d'explnum_doc : extension declaree ET extension reelle (analyse du
		//contenu binaire), pour se proteger d'un fichier renomme.
		$ext_declaree = \Pmb\Common\Library\Upload\UploadFileValidator::findExtensionWithFile($_FILES['f_fichier']['name']);
		$contenu_fichier = file_get_contents($_FILES['f_fichier']['tmp_name']);
		if(
			$contenu_fichier === false
			|| !\Pmb\Common\Library\Upload\UploadFileValidator::isExtensionAllowed($ext_declaree)
			|| !\Pmb\Common\Library\Upload\UploadFileValidator::isExtensionAllowed(\Pmb\Common\Library\Upload\UploadFileValidator::findExtensionWithContent($contenu_fichier))
		){
			ajax_http_send_response(array('ok' => 0));
			break;
		}

		$explnum_doc = new explnum_doc();
		$explnum_doc->load_file($_FILES['f_fichier']);
		$explnum_doc->analyse_file();
		//Depot par le demandeur : l'appartenance de la demande a $id_empr vient
		//d'etre verifiee ci-dessus. Le fil de discussion s'appuie sur ces deux
		//colonnes pour designer l'auteur d'une piece jointe, en Opac comme en
		//Gestion.
		$explnum_doc->explnum_doc_num_user = intval($id_empr);
		$explnum_doc->explnum_doc_type_user = 1;
		$explnum_doc->save();
		//Sans identifiant, l'insertion a echoue : on s'arrete plutot que de
		//rattacher a l'action une piece jointe inexistante. Cas possible si le
		//code est deploye avant la mise a jour de la base, l'Opac ne declenchant
		//pas l'addon (cf main.php).
		if(!$explnum_doc->explnum_doc_id){
			ajax_http_send_response(array('ok' => 0));
			break;
		}

		$query = "REPLACE INTO explnum_doc_actions SET
			num_explnum_doc = '".$explnum_doc->explnum_doc_id."',
			num_action = ".$idaction.",
			prive = 0,
			rapport = 0";
		pmb_mysql_query($query);

		ajax_http_send_response(array(
			'ok' => 1,
			'filename' => $explnum_doc->explnum_doc_nomfichier,
			'id_explnum_doc' => $explnum_doc->explnum_doc_id,
			'createur' => $action->getFileCreateur($explnum_doc),
			'date' => $explnum_doc->explnum_doc_create_date,
		));
		break;
	case 'ajax_update_custom_field':
		//Edition inline d'un champ personnalise "demandes" de type liste
		//(ex: cp_priorite) depuis l'accordeon de la liste, sans passer par
		//rec_fields_perso() (qui reecrirait TOUS les champs personnalises
		//de la demande) : mise a jour ciblee du seul champ demande par le
		//client, apres verification qu'il est bien configure pour
		//"demandes" et de type liste (nom valide via get_field_id_from_name,
		//jamais un id de champ arbitraire).
		global $iddemande, $id_empr, $custom_field_name, $custom_field_value;
		$iddemande = intval($iddemande);
		$custom_field_name = trim(strval($custom_field_name ?? ''));

		$demande = new demandes($iddemande, false);
		if(!$demande->id_demande || $demande->num_demandeur != $id_empr){
			ajax_http_send_response(array('ok' => 0));
			break;
		}

		//La priorite ne peut etre modifiee que tant que la demande est
		//encore "A valider" (1) ou "Validee" (2) : au-dela (refusee, finie,
		//abandonnee, classee), le champ redevient en lecture seule cote
		//serveur, quel que soit l'etat de l'UI cote client.
		if($custom_field_name === 'cp_priorite' && $demande->etat_demande != 1 && $demande->etat_demande != 2){
			ajax_http_send_response(array('ok' => 0));
			break;
		}

		$p_perso = new parametres_perso("demandes");
		$idchamp = $custom_field_name !== '' ? $p_perso->get_field_id_from_name($custom_field_name) : 0;
		if(!$idchamp || ($p_perso->t_fields[$idchamp]['TYPE'] ?? '') !== 'list'){
			ajax_http_send_response(array('ok' => 0));
			break;
		}

		//Colonne de stockage derivee du DATATYPE du champ (valeur issue de
		//notre propre configuration, jamais du client) : meme mecanisme que
		//parametres_perso::get_values(). Liste blanche par securite, au cas
		//ou une valeur de DATATYPE inattendue se retrouverait en base.
		$datatype = $p_perso->t_fields[$idchamp]['DATATYPE'];
		if(!in_array($datatype, array('small_text', 'text', 'integer', 'date', 'float'), true)){
			ajax_http_send_response(array('ok' => 0));
			break;
		}
		$colonne = 'demandes_custom_'.$datatype;

		$valeur = trim(strval($custom_field_value ?? ''));
		pmb_mysql_query("DELETE FROM demandes_custom_values WHERE demandes_custom_champ=".$idchamp." AND demandes_custom_origine=".$iddemande);
		if($valeur !== ''){
			if($datatype === 'integer'){
				$valeur_sql = intval($valeur);
			} elseif($datatype === 'float'){
				$valeur_sql = floatval($valeur);
			} else {
				$valeur_sql = "'".addslashes($valeur)."'";
			}
			pmb_mysql_query("INSERT INTO demandes_custom_values (demandes_custom_champ, demandes_custom_origine, ".$colonne.", demandes_custom_order) VALUES (".$idchamp.", ".$iddemande.", ".$valeur_sql.", 0)");
		}

		ajax_http_send_response(array('ok' => 1));
		break;
	case 'ajax_delete_note':
		//Suppression d'une note OPAC (jamais un message de la Gestion,
		//cf notes_type_user != 1). demandes_note::fetch_data() peuple deja
		//num_demande via sa jointure interne, pas besoin de reconstruire
		//l'action separement pour la verification d'appartenance.
		global $idnote, $id_empr;
		$idnote = intval($idnote);

		$note = new demandes_note($idnote);
		if(!$note->id_note || $note->notes_type_user != 1){
			ajax_http_send_response(array('ok' => 0));
			break;
		}
		$demande_note = new demandes($note->num_demande, false);
		if(!$demande_note->id_demande || $demande_note->num_demandeur != $id_empr){
			ajax_http_send_response(array('ok' => 0));
			break;
		}

		demandes_note::delete($note);
		ajax_http_send_response(array('ok' => 1));
		break;
	case 'ajax_delete_attachment':
		//Suppression d'une piece jointe : meme jointure d'appartenance que
		//explnum_demandes.php (le fichier doit appartenir a une demande
		//du demandeur courant).
		//La condition sur explnum_doc_type_user ecarte en plus les depots des
		//gestionnaires : un lecteur ne supprime que ses propres pieces jointes.
		//Le bouton de suppression est deja masque sur ces bulles cote template,
		//mais l'identifiant vient du client : le controle doit etre ici.
		global $id_explnum_doc, $id_empr;
		$id_explnum_doc = intval($id_explnum_doc);

		$req_check = "SELECT eda.num_explnum_doc FROM explnum_doc_actions eda
			JOIN demandes_actions da ON da.id_action = eda.num_action
			JOIN demandes d ON d.id_demande = da.num_demande
			JOIN explnum_doc ed ON ed.id_explnum_doc = eda.num_explnum_doc
			WHERE eda.num_explnum_doc = '".$id_explnum_doc."' AND d.num_demandeur = '".intval($id_empr)."'
			AND ed.explnum_doc_type_user = 1";
		$res_check = pmb_mysql_query($req_check);
		if(!pmb_mysql_num_rows($res_check)){
			ajax_http_send_response(array('ok' => 0));
			break;
		}

		pmb_mysql_query("DELETE FROM explnum_doc_actions WHERE num_explnum_doc='".$id_explnum_doc."'");
		pmb_mysql_query("DELETE FROM explnum_doc WHERE id_explnum_doc='".$id_explnum_doc."'");
		ajax_http_send_response(array('ok' => 1));
		break;

}
