[SPIP][PLUGINS] v3.0-->v3.2
[lhc/web/www.git] / www / plugins-dist / forum / public / forum.php
1 <?php
2
3 /***************************************************************************\
4 * SPIP, Systeme de publication pour l'internet *
5 * *
6 * Copyright (c) 2001-2016 *
7 * Arnaud Martin, Antoine Pitrou, Philippe Riviere, Emmanuel Saint-James *
8 * *
9 * Ce programme est un logiciel libre distribue sous licence GNU/GPL. *
10 * Pour plus de details voir le fichier COPYING.txt ou l'aide en ligne. *
11 \***************************************************************************/
12
13 if (!defined("_ECRIRE_INC_VERSION")) {
14 return;
15 }
16
17 //
18 // <BOUCLE(FORUMS)>
19 //
20 // https://code.spip.net/@boucle_FORUMS_dist
21 function boucle_FORUMS_dist($id_boucle, &$boucles) {
22 $boucle = &$boucles[$id_boucle];
23 $id_table = $boucle->id_table;
24
25 // Par defaut, selectionner uniquement les forums sans mere
26 // Les criteres {tout} et {plat} inversent ce choix
27 // de meme qu'un critere sur {id_forum} ou {id_parent}
28 if (!isset($boucle->modificateur['tout'])
29 and !isset($boucle->modificateur['plat'])
30 and !isset($boucle->modificateur['criteres']['id_forum'])
31 and !isset($boucle->modificateur['criteres']['id_parent'])
32 ) {
33 array_unshift($boucle->where, array("'='", "'$id_table." . "id_parent'", 0));
34 }
35
36 return calculer_boucle($id_boucle, $boucles);
37 }
38
39 // {meme_parent}
40 // https://www.spip.net/@meme_parent
41 // https://code.spip.net/@critere_meme_parent_dist
42 function critere_FORUMS_meme_parent_dist($idb, &$boucles, $crit) {
43 global $exceptions_des_tables;
44 $boucle = &$boucles[$idb];
45 $arg = kwote(calculer_argument_precedent($idb, 'id_parent', $boucles));
46 $id_parent = isset($exceptions_des_tables[$boucle->id_table]['id_parent']) ?
47 $exceptions_des_tables[$boucle->id_table]['id_parent'] :
48 'id_parent';
49 $mparent = $boucle->id_table . '.' . $id_parent;
50
51 $boucle->where[] = array("'='", "'$mparent'", $arg);
52 $boucle->where[] = array("'>'", "'$mparent'", 0);
53 $boucle->modificateur['plat'] = true;
54 }
55
56
57 /**
58 * Compile le critère `{compter_reponses}`
59 *
60 * Ce critère compte le nombre de messages en réponse à un message donné.
61 * Il stocke l’information dans le champ `nombre_reponses`.
62 * On peut le récupérer en squelette avec `#FORUM_NOMBRE_REPONSES`
63 *
64 * Le calcul se fait par une jointure LEFT :
65 * les éléments avec aucune réponse sont retournés.
66 *
67 * On peut passer un opérateur optionnel tel que :
68 * `{compter_reponses nombre_reponses = 0}`
69 * Ce qui fera un test sur le résultat du calcul (HAVING).
70 *
71 * @example
72 * ```
73 * <BOUCLE_(FORUMS){!par date_thread}{compter_reponses}> #FORUM_NOMBRE_REPONSES ...
74 * <BOUCLE_(FORUMS){compter_reponses}{!par nombre_reponse}> les plus commentés ...
75 * <BOUCLE_(FORUMS){!par date_thread}{compter_reponses nombre_reponse = 0}> sans réponse ...
76 * <BOUCLE_(FORUMS){!par date_thread}{compter_reponses nombre_reponse > 10}> + de 10 réponses ...
77 * ```
78 *
79 * @param string $idb Identifiant de la boucle
80 * @param array $boucles AST du squelette
81 * @param Critere $crit Paramètres du critère dans cette boucle
82 * @return void
83 */
84 function critere_FORUMS_compter_reponses($idb, &$boucles, $crit) {
85 $boucle = &$boucles[$idb];
86
87 $id_parent = isset($GLOBALS['exceptions_des_tables'][$boucle->id_table]['id_parent']) ?
88 $GLOBALS['exceptions_des_tables'][$boucle->id_table]['id_parent'] :
89 'id_parent';
90
91 $id_table = $boucle->id_table;
92
93 $boucle->from['fils'] = 'spip_forum';
94 $boucle->from_type['fils'] = 'left';
95 $boucle->join["fils"] = array("'$id_table'", "'$id_parent'", "'id_forum'", "'fils.statut='.sql_quote('publie')");
96
97 $boucle->select[]= 'COUNT(fils.id_forum) AS nombre_reponses';
98
99 // Gestion du having
100 if (count($crit->param)) {
101 $champ = $crit->param[0][0]->texte;
102 if (preg_match(',^(\w+)\s*([<>=])\s*([0-9]+)$,', $champ, $r)) {
103 $champ = $r[1];
104 $op = $r[2];
105 $op_val = $r[3];
106 $boucle->having[]= array("'".$op."'", "'" . $champ . "'", $op_val);
107 }
108 }
109 }
110
111 /**
112 * Retourne le nombre de vote sur un objet de SPIP.
113 *
114 * Nécessite le critere `{compter_reponses}` sur la boucle FORUMS
115 *
116 * `<BOUCLE_(FORUMS){compter_reponses}>#FORUM_NOMBRE_REPONSES ...`
117 *
118 * @param Champ $p
119 * @return Champ
120 */
121 function balise_FORUM_NOMBRE_REPONSES_dist($p) {
122 return rindex_pile($p, 'nombre_reponses', 'compter_reponses');
123 }
124
125
126
127 /**
128 * Faute de copie du champ id_secteur dans la table des forums,
129 * faut le retrouver par jointure
130 * Pour chaque Row il faudrait tester si le forum est
131 * d'article, de breve, de rubrique, ou de syndication.
132 * Pour le moment on ne traite que les articles,
133 * les 3 autres cas ne marcheront donc pas: ca ferait 4 jointures
134 * qu'il faut traiter optimalement ou alors pas du tout.
135 *
136 * @param string $idb
137 * @param object $boucles
138 * @param $val
139 * @param $crit
140 * @return mixed|string
141 */
142 function public_critere_secteur_forums_dist($idb, &$boucles, $val, $crit) {
143 return calculer_critere_externe_init($boucles[$idb], array('spip_articles'), 'id_secteur', $boucles[$idb]->show,
144 $crit->cond, true);
145 }
146
147
148 //
149 // Parametres de reponse a un forum
150 //
151 // Cette balise peut etre executee en dehors de toute boucle,
152 // par exemple en tete de inc-forums.html ; impossible donc de
153 // savoir a quel objet elle va s'appliquer, ca dependra du contexte
154 //
155 // https://code.spip.net/@balise_PARAMETRES_FORUM_dist
156 function balise_PARAMETRES_FORUM_dist($p) {
157
158 // s'il y a un id_article dans le contexte, regarder le statut
159 // accepter_forum de cet article
160 $_id_article = champ_sql('id_article', $p);
161 $p->code = '
162 // refus des forums ?
163 (quete_accepter_forum(' . $_id_article . ')=="non" OR
164 ($GLOBALS["meta"]["forums_publics"] == "non"
165 AND quete_accepter_forum(' . $_id_article . ') == ""))
166 ? "" : // sinon:
167 ';
168
169 // pas de calculs superflus si le site est monolingue
170 $lang = strpos($GLOBALS['meta']['langues_utilisees'], ',');
171
172 // si on est dans une boucle de forums, c'est une reponse
173 if ($p->type_requete == 'forums') {
174 $_id_reponse = champ_sql('id_forum', $p);
175 } else {
176 $_id_reponse = "null";
177 }
178
179 // objet et id_objet principaux sont a determiner
180 // dans le contexte ; on demande en tout etat de cause
181 // a la boucle mere de reserver son id_primary
182 if ($p->id_boucle
183 and isset($p->boucles[$p->id_boucle])
184 and $primary = $p->boucles[$p->id_boucle]->primary
185 ) {
186 $_type = _q($p->boucles[$p->id_boucle]->type_requete);
187 $_primary = champ_sql($primary, $p);
188 } else {
189 $_type = "null";
190 $_primary = "null";
191 }
192
193 // le code de base des parametres
194 $c = 'calcul_parametres_forum($Pile[0],'
195 . $_id_reponse . ',' . $_type . ',' . $_primary . ')';
196
197 // ajouter la lang, eventuellement donnee par le contexte
198 if ($lang) {
199 $_lang = champ_sql('lang', $p);
200 $c = "lang_parametres_forum($c,$_lang)";
201 }
202
203 // Syntaxe [(#PARAMETRES_FORUM{#SELF})] pour fixer le retour du forum
204 # note : ce bloc qui sert a recuperer des arguments calcules pourrait
205 # porter un nom et faire partie de l'API.
206 $retour = interprete_argument_balise(1, $p);
207 if ($retour === null) {
208 $retour = "''";
209 }
210
211 // Attention un eventuel &retour=xxx dans l'URL est prioritaire
212 $c .= '.
213 (($lien = (_request("retour") ? _request("retour") : str_replace("&amp;", "&", ' . $retour . '))) ? "&retour=".rawurlencode($lien) : "")';
214
215 $c = '(' . $c . ')';
216 // Ajouter le code d'invalideur specifique a cette balise
217 include_spip('inc/invalideur');
218 if ($i = charger_fonction('code_invalideur_forums', '', true)) {
219 $p->code .= $i($p, $c);
220 } else {
221 $p->code .= $c;
222 }
223
224 $p->interdire_scripts = false;
225
226 return $p;
227 }
228
229 // Cette fonction est appellee avec le contexte + trois parametres optionnels :
230 // 1. $reponse = l'id_forum auquel on repond
231 // 2. $type = le type de boucle dans lequel on se trouve, le cas echeant
232 // 3. $primary = l'id_objet de la boucle dans laquelle on se trouve
233 // elle doit renvoyer '', 'id_article=5' ou 'id_article=5&id_forum=12'
234 // selon les cas
235 function calcul_parametres_forum(&$env, $reponse, $type, $primary) {
236
237 // si c'est une reponse, on peut esperer que (objet,id_objet) sont dans
238 // la boucle mere, mais il est possible que non (forums imbriques etc)
239 // dans ce cas on va chercher dans la base.
240 if ($id_parent = intval($reponse)) {
241 if ($type
242 and $type != 'forums'
243 and $primary
244 ) {
245 $forum = array('objet' => $type, 'id_objet' => $primary);
246 } else {
247 $forum = sql_fetsel('objet, id_objet', 'spip_forum', 'id_forum=' . $id_parent);
248 }
249
250 if ($forum) {
251 return id_table_objet($forum['objet']) . '=' . $forum['id_objet']
252 . '&id_forum=' . $id_parent;
253 } else {
254 return '';
255 }
256 }
257
258 // Ce n'est pas une reponse, on prend la boucle mere
259 if ($type and $primary) {
260 return id_table_objet($type) . '=' . intval($primary);
261 }
262
263 // dernier recours, on regarde pour chacun des objets forumables
264 // ce que nous propose le contexte #ENV
265 foreach ($env as $k => $v) {
266 if (preg_match(',^id_([a-z_]+)$,S', $k)
267 and $id = intval($v)
268 ) {
269 return id_table_objet($k) . '=' . $v;
270 }
271 }
272
273 return '';
274 }
275
276 # retourne le champ 'accepter_forum' d'un article
277 function quete_accepter_forum($id_article) {
278 // si la fonction est appelee en dehors d'une boucle
279 // article (forum de breves), $id_article est nul
280 // mais il faut neanmoins accepter l'affichage du forum
281 // d'ou le 0=>'' (et pas 0=>'non').
282 static $cache = array(0 => '');
283
284 $id_article = intval($id_article);
285
286 if (isset($cache[$id_article])) {
287 return $cache[$id_article];
288 }
289
290 return $cache[$id_article] = sql_getfetsel('accepter_forum', 'spip_articles', "id_article=$id_article");
291 }
292
293 // Ajouter "&lang=..." si la langue du forum n'est pas celle du site.
294 // Si le 2e parametre n'est pas une chaine, c'est qu'on n'a pas pu
295 // determiner la table a la compil, on le fait maintenant.
296 // Il faudrait encore completer: on ne connait pas la langue
297 // pour une boucle forum sans id_article ou id_rubrique donne par le contexte
298 // et c'est signale par un message d'erreur abscons: "table inconnue forum".
299 //
300 // https://code.spip.net/@lang_parametres_forum
301 function lang_parametres_forum($qs, $lang) {
302 if (is_array($lang) and preg_match(',id_([a-z_]+)=([0-9]+),', $qs, $r)) {
303 $id = 'id_' . $r[1];
304 if ($t = $lang[$id]) {
305 $lang = sql_getfetsel('lang', $t, "$id=" . $r[2]);
306 }
307 }
308 // Si ce n'est pas la meme que celle du site, l'ajouter aux parametres
309
310 if ($lang and $lang <> $GLOBALS['meta']['langue_site']) {
311 return $qs . "&lang=" . $lang;
312 }
313
314 return $qs;
315 }
316
317 // Pour que le compilo ajoute un invalideur a la balise #PARAMETRES_FORUM
318 // Noter l'invalideur de la page contenant ces parametres,
319 // en cas de premier post sur le forum
320 // https://code.spip.net/@code_invalideur_forums
321 function code_invalideur_forums_dist($p, $code) {
322 return $code;
323 }