Completed
Push — master ( 37f24f...c12d0a )
by cam
04:22
created

rubriques.php ➔ calcul_branche()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 1
rs 10
c 0
b 0
f 0
1
<?php
2
3
/***************************************************************************\
4
 *  SPIP, Système de publication pour l'internet                           *
5
 *                                                                         *
6
 *  Copyright © avec tendresse depuis 2001                                 *
7
 *  Arnaud Martin, Antoine Pitrou, Philippe Rivière, Emmanuel Saint-James  *
8
 *                                                                         *
9
 *  Ce programme est un logiciel libre distribué sous licence GNU/GPL.     *
10
 *  Pour plus de détails voir le fichier COPYING.txt ou l'aide en ligne.   *
11
\***************************************************************************/
12
13
/**
14
 * Fichier gérant l'actualisation et le suivi des rubriques, et de leurs branches
15
 *
16
 * @package SPIP\Core\Rubriques
17
 */
18
19
if (!defined('_ECRIRE_INC_VERSION')) {
20
	return;
21
}
22
23
24
/**
25
 * Recalcule les statuts d'une rubrique
26
 *
27
 * Fonction à appeler lorsque le statut d'un objet change dans une rubrique
28
 * ou que la rubrique est deplacée.
29
 *
30
 * Si le statut passe a "publie", la rubrique et ses parents y passent aussi
31
 * et les langues utilisees sont recalculées.
32
 * Conséquences symétriques s'il est depublié.
33
 *
34
 * S'il est deplacé alors qu'il était publiée, double conséquence.
35
 *
36
 * Tout cela devrait passer en SQL, sous forme de Cascade SQL.
37
 *
38
 * @uses depublier_branche_rubrique_if()
39
 * @uses calculer_prochain_postdate()
40
 * @uses publier_branche_rubrique()
41
 *
42
 * @param int $id_rubrique
43
 *     Identifiant de la rubrique
44
 * @param array $modifs
45
 *     Tableau de description des modifications.
46
 *     Peut avoir 2 index, 'statut' étant obligatoire :
47
 *     - statut : indique le nouveau statut de la rubrique
48
 *     - id_rubrique : indiquer la rubrique dans laquelle on déplace la rubrique (son nouveau parent donc)
49
 * @param array $infos
50
 *     Infos sur l'objet modifié : statut_ancien, objet, id_objet…
51
 * @param bool $postdate
52
 *     true pour recalculer aussi la date du prochain article post-daté
53
 * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
54
 *     true si le statut change effectivement
55
 **/
56
function calculer_rubriques_if($id_rubrique, $modifs, $infos = array(), $postdate = false) {
57
	$neuf = false;
58
	
59
	// Compat avec l'ancienne signature
60
	if (is_string($infos)) {
61
		$infos = array('statut_ancien' => $infos);
62
	}
63
	if (!isset($infos['statut_ancien'])) {
64
		$infos['statut_ancien'] = '';
65
	}
66
	
67
	// On recherche quels statuts tester
68
	if (
69
		isset($infos['objet'])
70
		and include_spip('inc/filtres')
71
		and $declaration_statut = objet_info($infos['objet'], 'statut')
72
		and is_array($declaration_statut)
73
	) {
74
		foreach ($declaration_statut as $champ_statut) {
75
			if ($champ_statut['champ'] == 'statut') {
76
				$statuts_publies = array_map('trim', explode(',', $champ_statut['publie']));
77
				break; // stop on a trouvé le bon champ
78
			}
79
		}
80
	}
81
	else {
82
		$statuts_publies = array('publie');
83
	}
84
	
85
	if (in_array($infos['statut_ancien'], $statuts_publies)) {
86
		if (isset($modifs['statut'])
87
			or isset($modifs['id_rubrique'])
88
			or ($postdate and strtotime($postdate) > time())
89
		) {
90
			$neuf |= depublier_branche_rubrique_if($id_rubrique);
91
		}
92
		// ne publier que si c'est pas un postdate, ou si la date n'est pas dans le futur
93 View Code Duplication
		if ($postdate) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
94
			calculer_prochain_postdate(true);
95
			$neuf |= (strtotime($postdate) <= time()); // par securite
96
		} elseif (isset($modifs['id_rubrique'])) {
97
			$neuf |= publier_branche_rubrique($modifs['id_rubrique']);
98
		}
99 View Code Duplication
	} elseif (isset($modifs['statut']) and in_array($modifs['statut'], $statuts_publies)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
Bug introduced by
The variable $statuts_publies does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
100
		if ($postdate) {
101
			calculer_prochain_postdate(true);
102
			$neuf |= (strtotime($postdate) <= time()); // par securite
103
		} else {
104
			$neuf |= publier_branche_rubrique($id_rubrique);
105
		}
106
	}
107
108
	if ($neuf) // Sauver la date de la derniere mise a jour (pour menu_rubriques)
109
	{
110
		ecrire_meta("date_calcul_rubriques", date("U"));
111
	}
112
113
	$langues = calculer_langues_utilisees();
114
	ecrire_meta('langues_utilisees', $langues);
115
}
116
117
118
/**
119
 * Publie une rubrique et sa hiérarchie de rubriques
120
 *
121
 * Fonction à appeler lorsqu'on dépublie ou supprime quelque chose
122
 * dans une rubrique.
123
 *
124
 * @todo Le nom de la fonction est trompeur, vu que la fonction remonte dans la hierarchie !
125
 *
126
 * @param int $id_rubrique
127
 *     Identifiant de la rubrique
128
 * @return bool
129
 *     true si le statut change effectivement
130
 */
131
function publier_branche_rubrique($id_rubrique) {
132
	$id_pred = $id_rubrique;
133
	while (true) {
134
		sql_updateq('spip_rubriques', array('statut' => 'publie', 'date' => date('Y-m-d H:i:s')),
135
			"id_rubrique=" . intval($id_rubrique));
136
		$id_parent = sql_getfetsel('id_parent', 'spip_rubriques AS R', "R.id_rubrique=" . intval($id_rubrique));
137
		if (!$id_parent) {
138
			break;
139
		}
140
		$id_rubrique = $id_parent;
141
	}
142
143
#	spip_log(" publier_branche_rubrique($id_rubrique $id_pred");
144
	return $id_pred != $id_rubrique;
145
}
146
147
/**
148
 * Dépublie si nécessaire des éléments d'une hiérarchie de rubriques
149
 *
150
 * Fonction à appeler lorsqu'on dépublie ou supprime quelque chose
151
 * dans une rubrique.
152
 *
153
 * @uses depublier_rubrique_if()
154
 * @todo Le nom de la fonction est trompeur, vu que la fonction remonte dans la hierarchie !
155
 *
156
 * @param int $id_rubrique
157
 *     Identifiant de la rubrique
158
 * @return bool
159
 *     true si le statut change effectivement
160
 */
161
function depublier_branche_rubrique_if($id_rubrique) {
162
	$date = date('Y-m-d H:i:s'); // figer la date
163
164
	#	spip_log("depublier_branche_rubrique($id_rubrique ?");
165
	$id_pred = $id_rubrique;
166
	while ($id_pred) {
167
168
		if (!depublier_rubrique_if($id_pred, $date)) {
169
			return $id_pred != $id_rubrique;
170
		}
171
		// passer au parent si on a depublie
172
		$r = sql_fetsel("id_parent", "spip_rubriques", "id_rubrique=" . intval($id_pred));
173
		$id_pred = $r['id_parent'];
174
	}
175
176
	return $id_pred != $id_rubrique;
177
}
178
179
/**
180
 * Dépublier une rubrique si aucun contenu publié connu n'est trouvé dedans
181
 *
182
 * @pipeline_appel objet_compte_enfants
183
 *
184
 * @param int $id_rubrique
185
 *     Identifiant de la rubrique à tester
186
 * @param string|null $date
187
 *     Date pour le calcul des éléments post-datés.
188
 *     null = date actuelle.
189
 * @return bool
190
 *    true si la rubrique a été dépubliée
191
 */
192
function depublier_rubrique_if($id_rubrique, $date = null) {
193
	if (is_null($date)) {
194
		$date = date('Y-m-d H:i:s');
195
	}
196
	$postdates = ($GLOBALS['meta']["post_dates"] == "non") ?
197
		" AND date <= " . sql_quote($date) : '';
198
199
	if (!$id_rubrique = intval($id_rubrique)) {
200
		return false;
201
	}
202
203
	// verifier qu'elle existe et est bien publiee
204
	$r = sql_fetsel('id_rubrique,statut', 'spip_rubriques', "id_rubrique=" . intval($id_rubrique));
205
	if (!$r or $r['statut'] !== 'publie') {
206
		return false;
207
	}
208
209
	// On met le nombre de chaque type d'enfants dans un tableau
210
	// Le type de l'objet est au pluriel
211
	$compte = array(
212
		'articles' => sql_countsel("spip_articles",
213
			"id_rubrique=" . intval($id_rubrique) . " AND statut='publie'$postdates"),
214
		'rubriques' => sql_countsel("spip_rubriques", "id_parent=" . intval($id_rubrique) . " AND statut='publie'"),
215
		'documents' => sql_countsel(
216
			"spip_documents AS D JOIN spip_documents_liens AS L ON D.id_document=L.id_document", 
217
			"L.id_objet=" . intval($id_rubrique) . " AND L.objet='rubrique' and D.mode NOT IN('logoon', 'logooff') ")
218
	);
219
220
	// On passe le tableau des comptes dans un pipeline pour que les plugins puissent ajouter (ou retirer) des enfants
221
	$compte = pipeline('objet_compte_enfants',
222
		array(
223
			'args' => array(
224
				'objet' => 'rubrique',
225
				'id_objet' => $id_rubrique,
226
				'statut' => 'publie',
227
				'date' => $date
228
			),
229
			'data' => $compte
230
		)
231
	);
232
233
	// S'il y a au moins un enfant de n'importe quoi, on ne dépublie pas
234
	foreach ($compte as $objet => $n) {
235
		if ($n) {
236
			return false;
237
		}
238
	}
239
240
	sql_updateq("spip_rubriques", array("statut" => 'prepa'), "id_rubrique=" . intval($id_rubrique));
241
242
#		spip_log("depublier_rubrique $id_pred");
243
	return true;
244
}
245
246
247
/**
248
 * Recalcule des héritages de rubriques
249
 *
250
 * Recalcule le statut des rubriques, les langues héritées et la date
251
 * du prochain article post-daté
252
 *
253
 * Cette fonction est appelée après importation: elle calcule les meta-donnes
254
 * resultantes et remet de la coherence au cas où la base importée en manquait
255
 *
256
 * Cette fonction doit etre invoquée sans processus concurrent potentiel.
257
 *
258
 * @uses calculer_rubriques_publiees()
259
 * @uses calculer_langues_utilisees()
260
 * @uses calculer_prochain_postdate()
261
 *
262
 * @return void
263
 **/
264
function calculer_rubriques() {
265
266
	calculer_rubriques_publiees();
267
268
	// Apres chaque (de)publication 
269
	// recalculer les langues utilisees sur le site
270
	$langues = calculer_langues_utilisees();
271
	ecrire_meta('langues_utilisees', $langues);
272
273
	// Sauver la date de la derniere mise a jour (pour menu_rubriques)
274
	ecrire_meta("date_calcul_rubriques", date("U"));
275
276
	// on calcule la date du prochain article post-date
277
	calculer_prochain_postdate();
278
}
279
280
281
/**
282
 * Recalcule l'ensemble des données associées à l'arborescence des rubriques
283
 *
284
 * Attention, faute de SQL transactionnel on travaille sur
285
 * des champs temporaires afin de ne pas casser la base
286
 * pendant la demi seconde de recalculs
287
 *
288
 * @pipeline_appel calculer_rubriques
289
 *
290
 * @return void
291
 **/
292
function calculer_rubriques_publiees() {
293
294
	// Mettre les compteurs a zero
295
	sql_updateq('spip_rubriques', array('date_tmp' => '0000-00-00 00:00:00', 'statut_tmp' => 'prepa'));
296
297
	//
298
	// Publier et dater les rubriques qui ont un article publie
299
	//
300
301
	// Afficher les articles post-dates ?
302
	$postdates = ($GLOBALS['meta']["post_dates"] == "non") ?
303
		"AND A.date <= " . sql_quote(date('Y-m-d H:i:s')) : '';
304
305
	$r = sql_select(
306
		"R.id_rubrique AS id, max(A.date) AS date_h",
307
		"spip_rubriques AS R JOIN spip_articles AS A ON R.id_rubrique = A.id_rubrique",
308
		"A.date>R.date_tmp AND A.statut='publie' $postdates ", "R.id_rubrique");
309 View Code Duplication
	while ($row = sql_fetch($r)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
310
		sql_updateq("spip_rubriques", array("statut_tmp" => 'publie', "date_tmp" => $row['date_h']),
311
			"id_rubrique=" . intval($row['id']));
312
	}
313
314
	// point d'entree pour permettre a des plugins de gerer le statut
315
	// autrement (par ex: toute rubrique est publiee des sa creation)
316
	// Ce pipeline fait ce qu'il veut, mais s'il touche aux statuts/dates
317
	// c'est statut_tmp/date_tmp qu'il doit modifier
318
	// [C'est un trigger... a renommer en trig_calculer_rubriques ?]
319
	pipeline('calculer_rubriques', null);
320
321
322
	// Les rubriques qui ont une rubrique fille plus recente
323
	// on tourne tant que les donnees remontent vers la racine.
324
	do {
325
		$continuer = false;
326
		$r = sql_select(
327
			"R.id_rubrique AS id, max(SR.date_tmp) AS date_h",
328
			"spip_rubriques AS R JOIN spip_rubriques AS SR ON R.id_rubrique = SR.id_parent",
329
			"(SR.date_tmp>R.date_tmp OR R.statut_tmp<>'publie') AND SR.statut_tmp='publie' ", "R.id_rubrique");
330 View Code Duplication
		while ($row = sql_fetch($r)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
331
			sql_updateq('spip_rubriques', array('statut_tmp' => 'publie', 'date_tmp' => $row['date_h']),
332
				"id_rubrique=" . intval($row['id']));
333
			$continuer = true;
334
		}
335
	} while ($continuer);
336
337
	// Enregistrement des modifs
338
	sql_update('spip_rubriques', array('date' => 'date_tmp', 'statut' => 'statut_tmp'));
339
}
340
341
/**
342
 * Recalcule les secteurs et les profondeurs des rubriques (et articles)
343
 *
344
 * Cherche les rubriques ayant des id_secteur ou profondeurs ne correspondant pas
345
 * avec leur parent, et les met à jour. De même avec les articles et leur id_secteur
346
 * On procede en iterant la profondeur de 1 en 1 pour ne pas risquer une boucle infinie sur reference circulaire
347
 *
348
 * @pipeline_appel trig_propager_les_secteurs
349
 *
350
 * @return void
351
 **/
352
function propager_les_secteurs() {
353
	// Profondeur 0
354
	// Toutes les rubriques racines sont de profondeur 0
355
	// et fixer les id_secteur des rubriques racines
356
	sql_update('spip_rubriques', array('id_secteur' => 'id_rubrique', 'profondeur' => 0), "id_parent=0");
357
	// Toute rubrique non racine est de profondeur >0
358
	sql_updateq('spip_rubriques', array('profondeur' => 1), "id_parent<>0 AND profondeur=0");
359
360
	// securite : pas plus d'iteration que de rubriques dans la base
361
	$maxiter = sql_countsel("spip_rubriques");
362
363
	// reparer les rubriques qui n'ont pas l'id_secteur de leur parent
364
	// on fait profondeur par profondeur
365
366
	$prof = 0;
367
	do {
368
		$continuer = false;
369
370
		// Par recursivite : si toutes les rubriques de profondeur $prof sont bonnes
371
		// on fixe le profondeur $prof+1
372
373
		// Toutes les rubriques dont le parent est de profondeur $prof ont une profondeur $prof+1
374
		// on teste A.profondeur > $prof+1 car :
375
		// - toutes les rubriques de profondeur 0 à $prof sont bonnes
376
		// - si A.profondeur = $prof+1 c'est bon
377
		// - cela nous protege de la boucle infinie en cas de reference circulaire dans les rubriques
378
		$maxiter2 = $maxiter;
379
		while ($maxiter2--
380
			and $rows = sql_allfetsel(
381
				"A.id_rubrique AS id, R.id_secteur AS id_secteur, R.profondeur+1 as profondeur",
382
				"spip_rubriques AS A JOIN spip_rubriques AS R ON A.id_parent = R.id_rubrique",
383
				"R.profondeur=" . intval($prof) . " AND (A.id_secteur <> R.id_secteur OR A.profondeur > R.profondeur+1)",
384
				"", "R.id_secteur", "0,100")) {
385
386
			$id_secteur = null;
387
			$ids = array();
388
			while ($row = array_shift($rows)) {
389
				if ($row['id_secteur'] !== $id_secteur) {
390 View Code Duplication
					if (count($ids)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
391
						sql_updateq("spip_rubriques", array("id_secteur" => $id_secteur, 'profondeur' => $prof + 1),
392
							sql_in('id_rubrique', $ids));
393
					}
394
					$id_secteur = $row['id_secteur'];
395
					$ids = array();
396
				}
397
				$ids[] = $row['id'];
398
			}
399 View Code Duplication
			if (count($ids)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
400
				sql_updateq("spip_rubriques", array("id_secteur" => $id_secteur, 'profondeur' => $prof + 1),
401
					sql_in('id_rubrique', $ids));
402
			}
403
		}
404
405
406
		// Toutes les rubriques de profondeur $prof+1 qui n'ont pas un parent de profondeur $prof sont decalees
407
		$maxiter2 = $maxiter;
408
		while ($maxiter2--
409
			and $rows = sql_allfetsel(
410
				"id_rubrique as id",
411
				"spip_rubriques",
412
				"profondeur=" . intval($prof + 1) . " AND id_parent NOT IN (" . sql_get_select("zzz.id_rubrique",
413
					"spip_rubriques AS zzz", "zzz.profondeur=" . intval($prof)) . ")", '', '', '0,100')) {
414
			$rows = array_column($rows, 'id');
415
			sql_updateq("spip_rubriques", array('profondeur' => $prof + 2), sql_in("id_rubrique", $rows));
416
		}
417
418
		// ici on a fini de valider $prof+1, toutes les rubriques de prondeur 0 a $prof+1 sont OK
419
		// si pas de rubrique a profondeur $prof+1 pas la peine de continuer
420
		// si il reste des rubriques non vues, c'est une branche morte ou reference circulaire (base foireuse)
421
		// on arrete les frais
422
		if (sql_countsel("spip_rubriques", "profondeur=" . intval($prof + 1))) {
423
			$prof++;
424
			$continuer = true;
425
		}
426
	} while ($continuer and $maxiter--);
427
428
	// loger si la table des rubriques semble foireuse
429
	// et mettre un id_secteur=0 sur ces rubriques pour eviter toute selection par les boucles
430
	if (sql_countsel("spip_rubriques", "profondeur>" . intval($prof + 1))) {
431
		spip_log("Les rubriques de profondeur>" . ($prof + 1) . " semblent suspectes (branches morte ou reference circulaire dans les parents)",
432
			_LOG_CRITIQUE);
433
		sql_update("spip_rubriques", array('id_secteur' => 0), "profondeur>" . intval($prof + 1));
434
	}
435
436
	// reparer les articles
437
	$r = sql_select("A.id_article AS id, R.id_secteur AS secteur", "spip_articles AS A, spip_rubriques AS R",
438
		"A.id_rubrique = R.id_rubrique AND A.id_secteur <> R.id_secteur");
439
440
	while ($row = sql_fetch($r)) {
441
		sql_update("spip_articles", array("id_secteur" => $row['secteur']), "id_article=" . intval($row['id']));
442
	}
443
444
	// avertir les plugins qui peuvent faire leur mises a jour egalement
445
	pipeline('trig_propager_les_secteurs', '');
446
}
447
448
449
/**
450
 * Recalcule les langues héritées des sous-rubriques
451
 *
452
 * Cherche les langues incorrectes de sous rubriques, qui doivent hériter
453
 * de la rubrique parente lorsque langue_choisie est différent de oui,
454
 * et les corrige.
455
 *
456
 * @return bool
0 ignored issues
show
Documentation introduced by
Should the return type not be boolean|string?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
457
 *     true si un changement a eu lieu
458
 **/
459
function calculer_langues_rubriques_etape() {
460
	$s = sql_select("A.id_rubrique AS id_rubrique, R.lang AS lang", "spip_rubriques AS A, spip_rubriques AS R",
461
		"A.id_parent = R.id_rubrique AND A.langue_choisie != 'oui' AND R.lang<>'' AND R.lang<>A.lang");
462
463
	$t = false;
464 View Code Duplication
	while ($row = sql_fetch($s)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
465
		$id_rubrique = $row['id_rubrique'];
466
		$t = sql_updateq('spip_rubriques', array('lang' => $row['lang'], 'langue_choisie' => 'non'),
467
			"id_rubrique=" . intval($id_rubrique));
468
	}
469
470
	return $t;
471
}
472
473
/**
474
 * Recalcule les langues des rubriques et articles
475
 *
476
 * Redéfinit la langue du site sur les rubriques sans langue spécifiée
477
 * (langue_choisie différent de 'oui')
478
 *
479
 * Redéfinit les langues des articles sans langue spécifiée
480
 * (langue_choisie différent de 'oui') en les rebasant sur la langue
481
 * de la rubrique parente lorsque ce n'est pas le cas.
482
 *
483
 * @uses calculer_langues_rubriques_etape()
484
 * @pipeline_appel trig_calculer_langues_rubriques
485
 *
486
 * @return void
487
 **/
488
function calculer_langues_rubriques() {
489
490
	// rubriques (recursivite)
491
	sql_updateq("spip_rubriques", array("lang" => $GLOBALS['meta']['langue_site'], "langue_choisie" => 'non'),
492
		"id_parent=0 AND langue_choisie != 'oui'");
493
	while (calculer_langues_rubriques_etape()) {
494
		;
495
	}
496
497
	// articles
498
	$s = sql_select("A.id_article AS id_article, R.lang AS lang", "spip_articles AS A, spip_rubriques AS R",
499
		"A.id_rubrique = R.id_rubrique AND A.langue_choisie != 'oui' AND (length(A.lang)=0 OR length(R.lang)>0) AND R.lang<>A.lang");
500 View Code Duplication
	while ($row = sql_fetch($s)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
501
		$id_article = $row['id_article'];
502
		sql_updateq('spip_articles', array("lang" => $row['lang'], 'langue_choisie' => 'non'),
503
			"id_article=" . intval($id_article));
504
	}
505
506
	if ($GLOBALS['meta']['multi_rubriques'] == 'oui') {
507
508
		$langues = calculer_langues_utilisees();
509
		ecrire_meta('langues_utilisees', $langues);
510
	}
511
512
	// avertir les plugins qui peuvent faire leur mises a jour egalement
513
	pipeline('trig_calculer_langues_rubriques', '');
514
}
515
516
517
/**
518
 * Calcule la liste des langues réellement utilisées dans le site public
519
 *
520
 * La recherche de langue est effectuée en recréant une boucle pour chaque
521
 * objet éditorial gérant des langues de sorte que les éléments non publiés
522
 * ne sont pas pris en compte.
523
 *
524
 * @param string $serveur
525
 *    Nom du connecteur à la base de données
526
 * @return string
527
 *    Liste des langues utilisées séparées par des virgules
528
 **/
529
function calculer_langues_utilisees($serveur = '') {
530
	include_spip('public/interfaces');
531
	include_spip('public/compiler');
532
	include_spip('public/composer');
533
	include_spip('public/phraser_html');
534
	$langues = array();
535
536
	$langues[$GLOBALS['meta']['langue_site']] = 1;
537
538
	include_spip('base/objets');
539
	$tables = lister_tables_objets_sql();
540
	$trouver_table = charger_fonction('trouver_table', 'base');
541
542
	foreach (array_keys($tables) as $t) {
543
		$desc = $trouver_table($t, $serveur);
544
		// c'est une table avec des langues
545
		if ($desc['exist']
546
			and isset($desc['field']['lang'])
547
			and isset($desc['field']['langue_choisie'])
548
		) {
549
550
			$boucle = new Boucle();
551
			$boucle->show = $desc;
552
			$boucle->nom = 'calculer_langues_utilisees';
0 ignored issues
show
Bug introduced by
The property nom does not seem to exist in Boucle.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
553
			$boucle->id_boucle = $desc['table_objet'];
554
			$boucle->id_table = $desc['table_objet'];
555
			$boucle->sql_serveur = $serveur;
556
			$boucle->select[] = "DISTINCT lang";
557
			$boucle->from[$desc['table_objet']] = $t;
558
			$boucle->separateur[] = ',';
559
			$boucle->return = '$Pile[$SP][\'lang\']';
560
			$boucle->iterateur = 'sql';
561
562
			$boucle->descr['nom'] = 'calculer_langues_utilisees'; // eviter notice php
563
			$boucle->descr['sourcefile'] = 'internal';
564
			$boucle->descr['gram'] = 'html';
565
566
			$boucle = pipeline('pre_boucle', $boucle);
567
568
			if (isset($desc['statut'])
569
				and $desc['statut']
570
			) {
571
				$boucles = array(
572
					'calculer_langues_utilisees' => $boucle,
573
				);
574
				// generer un nom de fonction "anonyme" unique
575
				do {
576
					$functionname = 'f_calculer_langues_utilisees_' . $boucle->id_table . '_' . time() . '_' . rand();
577
				} while (function_exists($functionname));
578
				$code = calculer_boucle('calculer_langues_utilisees', $boucles);
579
				$code = '$SP=0; $command=array();$command["connect"] = $connect = "' . $serveur . '"; $Pile=array(0=>array());' . "\n" . $code;
580
				$code = 'function ' . $functionname . '(){' . $code . '};$res = ' . $functionname . '();';
581
				$res = '';
582
				eval($code);
583
				$res = explode(',', $res);
584
				foreach ($res as $lang) {
585
					$langues[$lang] = 1;
586
				}
587
			} else {
588
				$res = sql_select(implode(',', $boucle->select), $boucle->from);
589
				while ($row = sql_fetch($res)) {
590
					$langues[$row['lang']] = 1;
591
				}
592
			}
593
		}
594
	}
595
596
	$langues = array_filter(array_keys($langues));
597
	sort($langues);
598
	$langues = join(',', $langues);
599
	spip_log("langues utilisees: $langues");
600
601
	return $langues;
602
}
603
604
/**
605
 * Calcul d'une branche de rubrique
606
 *
607
 * Liste des id_rubrique contenues dans une rubrique donnée
608
 *
609
 * @see inc_calcul_branche_in_dist()
610
 *
611
 * @param string|int|array $id
612
 *     Identifiant de la, ou des rubriques noeuds
613
 * @return string
614
 *     Liste des identifiants séparés par des virgules,
615
 *     incluant les rubriques noeuds et toutes leurs descendances
616
 */
617
function calcul_branche_in($id) {
618
	$calcul_branche_in = charger_fonction('calcul_branche_in', 'inc');
619
620
	return $calcul_branche_in($id);
621
}
622
623
/**
624
 * Calcul d'une hiérarchie
625
 *
626
 * Liste des id_rubrique contenant une rubrique donnée
627
 *
628
 * @see inc_calcul_hierarchie_in_dist()
629
 * @param string|int|array $id
630
 *     Identifiant de la, ou des rubriques dont on veut obtenir les hierarchies
631
 * @param bool $tout
632
 *     inclure la rubrique de depart dans la hierarchie ou non
633
 * @return string
634
 *     Liste des identifiants séparés par des virgules,
635
 *     incluant les rubriques transmises et toutes leurs parentées
636
 */
637
function calcul_hierarchie_in($id, $tout = true) {
638
	$calcul_hierarchie_in = charger_fonction('calcul_hierarchie_in', 'inc');
639
640
	return $calcul_hierarchie_in($id, $tout);
641
}
642
643
644
/**
645
 * Calcul d'une branche de rubriques
646
 *
647
 * Liste des id_rubrique contenues dans une rubrique donnée
648
 * pour le critere {branche}
649
 *
650
 * Fonction surchargeable pour optimisation
651
 *
652
 * @see inc_calcul_hierarchie_in_dist() pour la hierarchie
653
 *
654
 * @param string|int|array $id
655
 *     Identifiant de la, ou des rubriques noeuds
656
 * @return string
657
 *     Liste des identifiants séparés par des virgules,
658
 *     incluant les rubriques noeuds et toutes leurs descendances
659
 */
660
function inc_calcul_branche_in_dist($id) {
661
	static $b = array();
662
663
	// normaliser $id qui a pu arriver comme un array, comme un entier, ou comme une chaine NN,NN,NN
664
	if (!is_array($id)) {
665
		$id = explode(',', $id);
666
	}
667
	$id = join(',', array_map('intval', $id));
668
	if (isset($b[$id])) {
669
		return $b[$id];
670
	}
671
672
	// Notre branche commence par la rubrique de depart
673
	$branche = $r = $id;
674
675
	// On ajoute une generation (les filles de la generation precedente)
676
	// jusqu'a epuisement, en se protegeant des references circulaires
677
	$maxiter = 10000;
678
	while ($maxiter-- and $filles = sql_allfetsel(
679
			'id_rubrique',
680
			'spip_rubriques',
681
			sql_in('id_parent', $r) . " AND " . sql_in('id_rubrique', $r, 'NOT')
682
		)) {
683
		$r = join(',', array_column($filles, 'id_rubrique'));
684
		$branche .= ',' . $r;
685
	}
686
687
	# securite pour ne pas plomber la conso memoire sur les sites prolifiques
688
	if (strlen($branche) < 10000) {
689
		$b[$id] = $branche;
690
	}
691
692
	return $branche;
693
}
694
695
696
/**
697
 * Calcul d'une hiérarchie
698
 *
699
 * Liste des id_rubrique contenant une rubrique donnée,
700
 * contrairement à la fonction calcul_branche_in() qui calcule les
701
 * rubriques contenues
702
 *
703
 * @see inc_calcul_branche_in_dist() pour la descendence
704
 *
705
 * @param string|int|array $id
706
 *     Identifiant de la, ou des rubriques dont on veut obtenir les hierarchies
707
 * @param bool $tout
708
 *     inclure la rubrique de depart dans la hierarchie ou non
709
 * @return string
710
 *     Liste des identifiants séparés par des virgules,
711
 *     incluant les rubriques transmises et toutes leurs parentées
712
 */
713
function inc_calcul_hierarchie_in_dist($id, $tout = true) {
714
	static $b = array();
715
716
	// normaliser $id qui a pu arriver comme un array, comme un entier, ou comme une chaine NN,NN,NN
717
	if (!is_array($id)) {
718
		$id = explode(',', $id);
719
	}
720
	$id = join(',', array_map('intval', $id));
721
722
	if (isset($b[$id])) {
723
		// Notre branche commence par la rubrique de depart si $tout=true
724
		return $tout ? (strlen($b[$id]) ? $b[$id] . ",$id" : $id) : $b[$id];
725
	}
726
727
	$hier = "";
728
729
	// On ajoute une generation (les filles de la generation precedente)
730
	// jusqu'a epuisement, en se protegeant des references circulaires
731
	$ids_nouveaux_parents = $id;
732
	$maxiter = 10000;
733
	while ($maxiter-- and $parents = sql_allfetsel(
734
			'id_parent',
735
			'spip_rubriques',
736
			sql_in('id_rubrique', $ids_nouveaux_parents) . " AND " . sql_in('id_parent', $hier, 'NOT')
737
		)) {
738
		$ids_nouveaux_parents = join(',', array_column($parents, 'id_parent'));
739
		$hier = $ids_nouveaux_parents . (strlen($hier) ? ',' . $hier : '');
740
	}
741
742
	# securite pour ne pas plomber la conso memoire sur les sites prolifiques
743
	if (strlen($hier) < 10000) {
744
		$b[$id] = $hier;
745
	}
746
747
	// Notre branche commence par la rubrique de depart si $tout=true
748
	$hier = $tout ? (strlen($hier) ? "$hier,$id" : $id) : $hier;
749
750
	return $hier;
751
}
752
753
754
/**
755
 * Calcule la date du prochain article post-daté
756
 *
757
 * Appelée lorsqu'un (ou plusieurs) article post-daté arrive à terme
758
 * ou est redaté
759
 *
760
 * @uses publier_branche_rubrique()
761
 * @pipeline_appel trig_calculer_prochain_postdate
762
 *
763
 * @param bool $check
764
 *     true pour affecter le statut des rubriques concernées.
765
 * @return void
766
 **/
767
function calculer_prochain_postdate($check = false) {
768
	include_spip('base/abstract_sql');
769
	if ($check) {
770
		$postdates = ($GLOBALS['meta']["post_dates"] == "non") ?
771
			"AND A.date <= " . sql_quote(date('Y-m-d H:i:s')) : '';
772
773
		$r = sql_select("DISTINCT A.id_rubrique AS id",
774
			"spip_articles AS A LEFT JOIN spip_rubriques AS R ON A.id_rubrique=R.id_rubrique",
775
			"R.statut != 'publie' AND A.statut='publie'$postdates");
776
		while ($row = sql_fetch($r)) {
777
			publier_branche_rubrique($row['id']);
778
		}
779
780
		pipeline('trig_calculer_prochain_postdate', '');
781
	}
782
783
	$t = sql_fetsel("date", "spip_articles", "statut='publie' AND date > " . sql_quote(date('Y-m-d H:i:s')), "", "date",
784
		"1");
785
786
	if ($t) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $t of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
787
		$t = $t['date'];
788
		if (!isset($GLOBALS['meta']['date_prochain_postdate'])
789
			or $t <> $GLOBALS['meta']['date_prochain_postdate']
790
		) {
791
			ecrire_meta('date_prochain_postdate', strtotime($t));
792
			ecrire_meta('derniere_modif', time());
793
		}
794
	} else {
795
		effacer_meta('date_prochain_postdate');
796
		ecrire_meta('derniere_modif', time());
797
	}
798
799
	spip_log("prochain postdate: $t");
800
}
801
802
/**
803
 * Crée une arborescence de rubrique
804
 *
805
 * creer_rubrique_nommee('truc/machin/chose') va créer
806
 * une rubrique truc, une sous-rubrique machin, et une sous-sous-rubrique
807
 * chose, sans créer de rubrique si elle existe déjà
808
 * à partir de $id_parent (par défaut, à partir de la racine)
809
 *
810
 * NB: cette fonction est très pratique, mais pas utilisée dans le core
811
 * pour rester légère elle n'appelle pas calculer_rubriques()
812
 *
813
 * @param string $titre
814
 *     Titre des rubriques, séparés par des /
815
 * @param int $id_parent
816
 *     Identifiant de la rubrique parente
817
 * @param string $serveur
818
 *     Nom du connecteur à la base de données
819
 * @return int
820
 *     Identifiant de la rubrique la plus profonde.
821
 */
822
function creer_rubrique_nommee($titre, $id_parent = 0, $serveur = '') {
823
824
	// eclater l'arborescence demandee
825
	// echapper les </multi> et autres balises fermantes html
826
	$titre = preg_replace(",</([a-z][^>]*)>,ims", "<@\\1>", $titre);
827
	$arbo = explode('/', preg_replace(',^/,', '', $titre));
828
	include_spip('base/abstract_sql');
829
	foreach ($arbo as $titre) {
830
		// retablir les </multi> et autres balises fermantes html
831
		$titre = preg_replace(",<@([a-z][^>]*)>,ims", "</\\1>", $titre);
832
		$r = sql_getfetsel("id_rubrique", "spip_rubriques",
833
			"titre = " . sql_quote($titre) . " AND id_parent=" . intval($id_parent),
834
			$groupby = array(), $orderby = array(), $limit = '', $having = array(), $serveur);
835
		if ($r !== null) {
836
			$id_parent = $r;
837
		} else {
838
			$id_rubrique = sql_insertq('spip_rubriques', array(
839
					'titre' => $titre,
840
					'id_parent' => $id_parent,
841
					'statut' => 'prepa'
842
				), $desc = array(), $serveur);
843
			if ($id_parent > 0) {
844
				$data = sql_fetsel("id_secteur,lang", "spip_rubriques", "id_rubrique=$id_parent",
845
					$groupby = array(), $orderby = array(), $limit = '', $having = array(), $serveur);
846
				$id_secteur = $data['id_secteur'];
847
				$lang = $data['lang'];
848
			} else {
849
				$id_secteur = $id_rubrique;
850
				$lang = $GLOBALS['meta']['langue_site'];
851
			}
852
853
			sql_updateq('spip_rubriques', array('id_secteur' => $id_secteur, "lang" => $lang),
854
				"id_rubrique=" . intval($id_rubrique), $desc = '', $serveur);
855
856
			// pour la recursion
857
			$id_parent = $id_rubrique;
858
		}
859
	}
860
861
	return intval($id_parent);
862
}
863