[PLUGINS] ~maj globale
[lhc/web/www.git] / www / plugins / formidable / formulaires / formidable.php
1 <?php
2
3 /**
4 * Gestion de l'affichage et traitement d'un formulaire Formidable
5 *
6 * @package SPIP\Formidable\Formulaires
7 **/
8
9 // Sécurité
10 if (!defined('_ECRIRE_INC_VERSION')) {
11 return;
12 }
13
14 include_spip('inc/formidable');
15 include_spip('inc/saisies');
16 include_spip('base/abstract_sql');
17 include_spip('inc/autoriser');
18
19 function formidable_id_formulaire($id) {
20 // on utilise une static pour etre sur que si l'appel dans verifier() passe, celui dans traiter() passera aussi
21 // meme si entre temps on perds la base
22 static $id_formulaires = array();
23 if (isset($id_formulaires[$id])) {
24 return $id_formulaires[$id];
25 }
26
27 if (is_numeric($id)) {
28 $where = 'id_formulaire = ' . intval($id);
29 } elseif (is_string($id)) {
30 $where = 'identifiant = ' . sql_quote($id);
31 } else {
32 return 0;
33 }
34
35 $id_formulaire = intval(sql_getfetsel('id_formulaire', 'spip_formulaires', $where));
36
37 if ($id_formulaire
38 and !test_espace_prive()
39 and !objet_test_si_publie('formulaire', $id_formulaire)) {
40 return $id_formulaires[$id] = 0;
41 }
42
43 return $id_formulaires[$id] = $id_formulaire;
44 }
45
46 /**
47 * Chargement du formulaire CVT de Formidable.
48 *
49 * Genere le formulaire dont l'identifiant (numerique ou texte est indique)
50 *
51 * @param int|string $id
52 * Identifiant numerique ou textuel du formulaire formidable
53 * @param array $valeurs
54 * Valeurs par défauts passées au contexte du formulaire
55 * Exemple : array('hidden_1' => 3) pour que champ identifie "@hidden_1@" soit prerempli
56 * @param int|bool $id_formulaires_reponse
57 * Identifiant d'une réponse pour forcer la reedition de cette reponse spécifique
58 *
59 * @return array
60 * Contexte envoyé au squelette HTML du formulaire.
61 **/
62 function formulaires_formidable_charger($id, $valeurs = array(), $id_formulaires_reponse = false) {
63 $contexte = array();
64
65 // On peut donner soit un id soit un identifiant
66 if (!$id_formulaire = formidable_id_formulaire($id)) {
67 return;
68 }
69
70 // On cherche si le formulaire existe
71 if ($formulaire = sql_fetsel('*', 'spip_formulaires', 'id_formulaire = ' . intval($id_formulaire))) {
72 // On ajoute un point d'entrée avec les infos de ce formulaire
73 // pour d'eventuels plugins qui en ont l'utilité
74 $contexte['_formidable'] = $formulaire;
75
76 // Classes CSS
77 $contexte['_css'] = $formulaire['css'];
78
79 // Est-ce que la personne a le droit de répondre ?
80 if (autoriser('repondre', 'formulaire', $formulaire['id_formulaire'], null, array('formulaire' => $formulaire))) {
81 $saisies = unserialize($formulaire['saisies']);
82 $traitements = unserialize($formulaire['traitements']);
83
84 // Si on est en train de réafficher les valeurs postées,
85 // ne pas afficher les saisies hidden
86 if ($formulaire['apres'] == 'valeurs'
87 and _request('formidable_afficher_apres') == 'valeurs'
88 ) {
89 foreach ($saisies as $k => $saisie) {
90 if (isset($saisie['saisie'])
91 and $saisie['saisie'] == 'hidden'
92 ) {
93 unset($saisies[$k]);
94 }
95 }
96 }
97
98 // On déclare les champs avec les valeurs par défaut
99 $contexte = array_merge(saisies_lister_valeurs_defaut($saisies), $contexte);
100 $contexte['mechantrobot'] = '';
101 // On ajoute le formulaire complet
102 $contexte['_saisies'] = $saisies;
103
104 $contexte['id'] = $formulaire['id_formulaire'];
105 $contexte['_hidden'] = '<input type="hidden" name="id_formulaire" value="' . $contexte['id'] . '"/>';
106
107 // S'il y a des valeurs par défaut dans l'appel, alors on pré-remplit
108 if ($valeurs) {
109 // Si c'est une chaine on essaye de la parser
110 if (is_string($valeurs)) {
111 $liste = explode(',', $valeurs);
112 $liste = array_map('trim', $liste);
113 $valeurs = array();
114 foreach ($liste as $i => $cle_ou_valeur) {
115 if ($i%2 == 0) {
116 $valeurs[$liste[$i]] = $liste[$i+1];
117 }
118 }
119 }
120
121 // Si on a un tableau,
122 // alors on écrase avec les valeurs données depuis l'appel
123 if ($valeurs and is_array($valeurs)) {
124 $contexte = array_merge($contexte, $valeurs);
125 }
126 }
127
128 // Si on passe un identifiant de reponse, on edite cette reponse si elle existe
129 if ($id_formulaires_reponse = intval($id_formulaires_reponse)) {
130 $contexte = formidable_definir_contexte_avec_reponse($contexte, $id_formulaires_reponse, $ok);
131 if ($ok == false) {
132 $contexte['editable'] = false;
133 $contexte['message_erreur'] = _T('formidable:traiter_enregistrement_erreur_edition_reponse_inexistante');
134 }
135 } else {
136 // calcul des paramètres d'anonymisation
137 $options = isset($traitements['enregistrement']) ? $traitements['enregistrement'] : null;
138
139 $anonymisation = (isset($options['anonymiser']) && $options['anonymiser']==true)
140 ? isset($options['anonymiser_variable']) ? $options['anonymiser_variable'] : ''
141 : '';
142
143 // Si multiple = non mais que c'est modifiable, alors on va chercher
144 // la dernière réponse si elle existe
145 if ($options
146 and !$options['multiple']
147 and $options['modifiable']
148 and $reponses = formidable_verifier_reponse_formulaire($formulaire['id_formulaire'], $options['identification'], $anonymisation)
149 ) {
150 $id_formulaires_reponse = array_pop($reponses);
151 $contexte = formidable_definir_contexte_avec_reponse($contexte, $id_formulaires_reponse, $ok);
152 }
153 }
154 } else {
155 $contexte['editable'] = false;
156 // le formulaire a déjà été répondu.
157 // peut être faut il afficher les statistiques des réponses
158 if ($formulaire['apres']=='stats') {
159 // Nous sommes face à un sondage auquel on a déjà répondu !
160 // On remplace complètement l'affichage du formulaire
161 // par un affichage du résultat de sondage !
162 $contexte['_remplacer_formulaire'] = recuperer_fond('modeles/formulaire_analyse', array(
163 'id_formulaire' => $formulaire['id_formulaire'],
164 ));
165 } else {
166 $contexte['message_erreur'] = _T('formidable:traiter_enregistrement_erreur_deja_repondu');
167 }
168 }
169 } else {
170 $contexte['editable'] = false;
171 $contexte['message_erreur'] = _T('formidable:erreur_inexistant');
172 }
173 if (!isset($contexte['_hidden'])) {
174 $contexte['_hidden'] = '';
175 }
176 $contexte['_hidden'] .= "\n" . '<input type="hidden" name="formidable_afficher_apres' /*.$formulaire['id_formulaire']*/ . '" value="' . $formulaire['apres'] . '"/>'; // marche pas
177
178 $contexte['formidable_afficher_apres'] = $formulaire['apres'];
179
180 return $contexte;
181 }
182
183
184 /**
185 * Vérification du formulaire CVT de Formidable.
186 *
187 * Pour chaque champ posté, effectue les vérifications demandées par
188 * les saisies et retourne éventuellement les erreurs de saisie.
189 *
190 * @param int|string $id
191 * Identifiant numerique ou textuel du formulaire formidable
192 * @param array $valeurs
193 * Valeurs par défauts passées au contexte du formulaire
194 * Exemple : array('hidden_1' => 3) pour que champ identifie "@hidden_1@" soit prerempli
195 * @param int|bool $id_formulaires_reponse
196 * Identifiant d'une réponse pour forcer la reedition de cette reponse spécifique
197 *
198 * @return array
199 * Tableau des erreurs
200 **/
201 function formulaires_formidable_verifier($id, $valeurs = array(), $id_formulaires_reponse = false) {
202 $erreurs = array();
203
204 // On peut donner soit un id soit un identifiant
205 if (!$id_formulaire = formidable_id_formulaire($id)) {
206 $erreurs['message_erreur'] = _T('formidable:erreur_base');
207 } else {
208 // Sale bête !
209 if (_request('mechantrobot')!='') {
210 $erreurs['hahahaha'] = 'hahahaha';
211 return $erreurs;
212 }
213
214 $formulaire = sql_fetsel('*', 'spip_formulaires', 'id_formulaire = ' . intval($id_formulaire));
215 $saisies = unserialize($formulaire['saisies']);
216
217 $erreurs = saisies_verifier($saisies);
218
219 // Si on a pas déjà une erreur sur le champ unicite, on lance une verification
220 if ($formulaire['unicite'] != '') {
221 if (!$erreurs[$formulaire['unicite']]) {
222 $reponses = sql_allfetsel(
223 'R.id_formulaire AS id',
224 'spip_formulaires_reponses AS R
225 LEFT JOIN spip_formulaires AS F
226 ON R.id_formulaire=F.id_formulaire
227 LEFT JOIN spip_formulaires_reponses_champs AS C
228 ON R.id_formulaires_reponse=C.id_formulaires_reponse',
229 'R.id_formulaire = ' . $id_formulaire . '
230 AND C.nom='.sql_quote($formulaire['unicite']).'
231 AND C.valeur='.sql_quote(_request($formulaire['unicite'])).'
232 AND R.statut = "publie"'
233 );
234 if (is_array($reponses) && count($reponses) > 0) {
235 $erreurs[$formulaire['unicite']] = $formulaire['message_erreur_unicite'] ? _T($formulaire['message_erreur_unicite']) : _T('formidable:erreur_unicite');
236 }
237 }
238 }
239
240 if ($erreurs and !isset($erreurs['message_erreur'])) {
241 $erreurs['message_erreur'] = _T('formidable:erreur_generique');
242 }
243 }
244 return $erreurs;
245 }
246
247
248 /**
249 * Traitement du formulaire CVT de Formidable.
250 *
251 * Exécute les traitements qui sont indiqués dans la configuration des
252 * traitements de ce formulaire formidable.
253 *
254 * Une fois fait, gère le retour après traitements des saisies en fonction
255 * de ce qui a été configuré dans le formulaire, par exemple :
256 * - faire réafficher le formulaire,
257 * - faire afficher les saisies
258 * - rediriger sur une autre page...
259 *
260 * @param int|string $id
261 * Identifiant numerique ou textuel du formulaire formidable
262 * @param array $valeurs
263 * Valeurs par défauts passées au contexte du formulaire
264 * Exemple : array('hidden_1' => 3) pour que champ identifie "@hidden_1@" soit prerempli
265 * @param int|bool $id_formulaires_reponse
266 * Identifiant d'une réponse pour forcer la reedition de cette reponse spécifique
267 *
268 * @return array
269 * Tableau des erreurs
270 **/
271 function formulaires_formidable_traiter($id, $valeurs = array(), $id_formulaires_reponse = false) {
272 $retours = array();
273
274 // POST Mortem de securite : on log le $_POST pour ne pas le perdre si quelque chose se passe mal
275 include_spip('inc/json');
276 $post = json_encode($_POST);
277 spip_log($post, 'formidable_post'._LOG_INFO_IMPORTANTE);
278
279 // On peut donner soit un id soit un identifiant
280 if (!$id_formulaire = formidable_id_formulaire($id)) {
281 return array('message_erreur'=>_T('formidable:erreur_base'));
282 }
283
284 $formulaire = sql_fetsel('*', 'spip_formulaires', 'id_formulaire = ' . $id_formulaire);
285 $traitements = unserialize($formulaire['traitements']);
286
287 // selon le choix, le formulaire se remet en route à la fin ou non
288 $retours['editable'] = ($formulaire['apres']=='formulaire');
289 $retours['formidable_afficher_apres'] = $formulaire['apres'];
290 $retours['id_formulaire'] = $id_formulaire;
291
292 // Si on a une redirection valide
293 if (($formulaire['apres'] == 'redirige') and ($formulaire['url_redirect'] != '')) {
294 refuser_traiter_formulaire_ajax();
295 // traiter les raccourcis artX, brX
296 include_spip('inc/lien');
297 $url_redirect = typer_raccourci($formulaire['url_redirect']);
298 if (count($url_redirect) > 2) {
299 $url_redirect = $url_redirect[0] . $url_redirect[2];
300 } else {
301 $url_redirect = $formulaire['url_redirect']; // URL classique
302 }
303
304 $retours['redirect'] = $url_redirect;
305 }
306
307 // les traitements deja faits se notent ici
308 // pour etre sur de ne pas etre appeles 2 fois
309 // ainsi si un traitement A a besoin d'un traitement B,
310 // et que B n'est pas fait quand il est appele, il peut rendre la main sans rien faire au premier coup
311 // et sera rappele au second tour
312 $retours['traitements'] = array();
313 $erreur_texte = '';
314
315 // Si on a des traitements
316 if (is_array($traitements) and !empty($traitements)) {
317 $maxiter = 5;
318 do {
319 foreach ($traitements as $type_traitement => $options) {
320 // si traitement deja appele, ne pas le relancer
321 if (!isset($retours['traitements'][$type_traitement])) {
322 if ($appliquer_traitement = charger_fonction($type_traitement, 'traiter/', true)) {
323 $retours = $appliquer_traitement(
324 array(
325 'formulaire' => $formulaire,
326 'options' => $options,
327 'id_formulaire' => $id_formulaire,
328 'valeurs' => $valeurs,
329 'id_formulaires_reponse' => $id_formulaires_reponse,
330 ),
331 $retours
332 );
333 } else {
334 // traitement introuvable, ne pas retenter
335 $retours['traitements'][$type_traitement] = true;
336 }
337 }
338 }
339 } while (count($retours['traitements']) < count($traitements) and $maxiter--);
340
341 // si on ne peut pas traiter correctement, alerter le webmestre
342 if (count($retours['traitements']) < count($traitements)) {
343 $erreur_texte = "Impossible de traiter correctement le formulaire $id\n"
344 . 'Traitements attendus :'.implode(',', array_keys($traitements))."\n"
345 . 'Traitements realises :'.implode(',', array_keys($retours['traitements']))."\n";
346 }
347
348 // Si on a personnalisé le message de retour, c'est lui qui est affiché uniquement
349 if ($formulaire['message_retour']) {
350 $retours['message_ok'] = _T_ou_typo($formulaire['message_retour']);
351 }
352 } else {
353 $retours['message_erreur'] = _T('formidable:retour_aucun_traitement');
354 }
355
356 // si aucun traitement, alerter le webmestre pour ne pas perdre les donnees
357 if (!$erreur_texte and !count($retours['traitements'])) {
358 $erreur_texte = "Aucun traitement pour le formulaire $id\n";
359 }
360
361 if ($erreur_texte) {
362 $erreur_sujet = "[ERREUR] Traitement Formulaire $id";
363 // dumper la saisie pour ne pas la perdre
364 $erreur_texte .= "\n".var_export($_REQUEST, true);
365 $envoyer_mail = charger_fonction('envoyer_mail', 'inc');
366 $envoyer_mail($GLOBALS['meta']['email_webmaster'], $erreur_sujet, $erreur_texte);
367 }
368 unset($retours['traitements']);
369 return $retours;
370 }
371
372
373 /**
374 * Ajoute dans le contexte les elements
375 * donnés par une reponse de formulaire indiquée
376 *
377 * @param array $contexte
378 * Contexte pour le squelette HTML du formulaire
379 * @param int $id_formulaires_reponse
380 * Identifiant de réponse
381 * @param bool $ok
382 * La reponse existe bien ?
383 * @return array $contexte
384 * Contexte complète des nouvelles informations
385 *
386 **/
387 function formidable_definir_contexte_avec_reponse($contexte, $id_formulaires_reponse, &$ok) {
388 // On va chercher tous les champs
389 $champs = sql_allfetsel(
390 'nom, valeur',
391 'spip_formulaires_reponses_champs',
392 'id_formulaires_reponse = ' . $id_formulaires_reponse
393 );
394 $ok = count($champs) ? true : false;
395
396 // On remplit le contexte avec
397 foreach ($champs as $champ) {
398 $test_array = unserialize($champ['valeur']);
399 $contexte[$champ['nom']] = is_array($test_array) ? $test_array : $champ['valeur'];
400 }
401
402 return $contexte;
403 }