Completed
Push — master ( 117d82...bd6ae7 )
by cam
04:12
created

editer_article.php ➔ revision_article()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 2
dl 0
loc 3
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
 * Gestion de l'action editer_article et de l'API d'édition d'un article
15
 *
16
 * @package SPIP\Core\Articles\Edition
17
 */
18
19
if (!defined('_ECRIRE_INC_VERSION')) {
20
	return;
21
}
22
23
/**
24
 * Action d'édition d'un article dans la base de données dont
25
 * l'identifiant est donné en paramètre de cette fonction ou
26
 * en argument de l'action sécurisée
27
 *
28
 * Si aucun identifiant n'est donné, on crée alors un nouvel article,
29
 * à condition que la rubrique parente (id_rubrique) puisse être obtenue
30
 * (avec _request())
31
 *
32
 * @link https://code.spip.net/@action_editer_article_dist
33
 * @uses article_inserer()
34
 * @uses article_modifier()
35
 *
36
 * @param null|int $arg
37
 *     Identifiant de l'article. En absence utilise l'argument
38
 *     de l'action sécurisée.
39
 * @return array
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use array<boolean|string|integer|null>.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
40
 *     Liste (identifiant de l'article, Texte d'erreur éventuel)
41
 */
42
function action_editer_article_dist($arg = null) {
43
	include_spip('inc/autoriser');
44
	$err = "";
45
	if (is_null($arg)) {
46
		$securiser_action = charger_fonction('securiser_action', 'inc');
47
		$arg = $securiser_action();
48
	}
49
50
	// si id_article n'est pas un nombre, c'est une creation 
51
	// mais on verifie qu'on a toutes les donnees qu'il faut.
52
	if (!$id_article = intval($arg)) {
53
		$id_parent = _request('id_parent');
54
		if (!$id_parent) {
55
			$err = _L("creation interdite d'un article sans rubrique");
56
		} elseif (!autoriser('creerarticledans', 'rubrique', $id_parent)) {
57
			$err = _T("info_creerdansrubrique_non_autorise");
58
		} else {
59
			$id_article = article_inserer($id_parent);
60
		}
61
	}
62
63
	// Enregistre l'envoi dans la BD
64
	if ($id_article > 0) {
65
		$err = article_modifier($id_article);
66
	}
67
68
	if ($err) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $err of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
69
		spip_log("echec editeur article: $err", _LOG_ERREUR);
70
	}
71
72
	return array($id_article, $err);
73
}
74
75
/**
76
 * Modifier un article
77
 *
78
 * Appelle toutes les fonctions de modification d'un article
79
 *
80
 * @param int $id_article
81
 *     Identifiant de l'article à modifier
82
 * @param array|null $set
83
 *     Couples (colonne => valeur) de données à modifier.
84
 *     En leur absence, on cherche les données dans les champs éditables
85
 *     qui ont été postés (via collecter_requests())
86
 * @return string|null
87
 *
88
 *     - Chaîne vide si aucune erreur,
89
 *     - Null si aucun champ à modifier,
90
 *     - Chaîne contenant un texte d'erreur sinon.
91
 */
92
function article_modifier($id_article, $set = null) {
93
94
	// unifier $texte en cas de texte trop long
95
	trop_longs_articles();
96
97
	include_spip('inc/modifier');
98
	include_spip('inc/filtres');
99
	$c = collecter_requests(
100
	// white list
101
		objet_info('article', 'champs_editables'),
0 ignored issues
show
Bug introduced by
It seems like objet_info('article', 'champs_editables') targeting objet_info() can also be of type string; however, collecter_requests() does only seem to accept array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
102
		// black list
103
		array('date', 'statut', 'id_parent'),
104
		// donnees eventuellement fournies
105
		$set
106
	);
107
108
	// Si l'article est publie, invalider les caches et demander sa reindexation
109
	$t = sql_getfetsel("statut", "spip_articles", "id_article=" . intval($id_article));
110
	$invalideur = $indexation = false;
111
	if ($t == 'publie') {
112
		$invalideur = "id='article/$id_article'";
113
		$indexation = true;
114
	}
115
116
	if ($err = objet_modifier_champs('article', $id_article,
117
		array(
118
			'data' => $set,
119
			'nonvide' => array('titre' => _T('info_nouvel_article') . " " . _T('info_numero_abbreviation') . $id_article),
120
			'invalideur' => $invalideur,
121
			'indexation' => $indexation,
122
			'date_modif' => 'date_modif' // champ a mettre a date('Y-m-d H:i:s') s'il y a modif
123
		),
124
		$c)
125
	) {
126
		return $err;
127
	}
128
129
	// Modification de statut, changement de rubrique ?
130
	$c = collecter_requests(array('date', 'statut', 'id_parent'), array(), $set);
131
	$err = article_instituer($id_article, $c);
132
133
	return $err;
134
}
135
136
/**
137
 * Insérer un nouvel article en base de données
138
 *
139
 * En plus des données enregistrées par défaut, la fonction :
140
 *
141
 * - retrouve un identifiant de rubrique pour stocker l'article (la
142
 *   première rubrique racine) si l'identifiant de rubrique transmis est
143
 *   nul.
144
 * - calcule la langue de l'article, soit
145
 *   - d'après la langue de la rubrique si les articles ne sont pas
146
 *     configurés comme pouvant être traduits,
147
 *   - d'après la langue de l'auteur en cours si les articles peuvent être traduits et
148
 *     si la langue de l'auteur est acceptée en tant que langue de traduction
149
 * - crée une liaison automatiquement entre l'auteur connecté et l'article
150
 *   créé, de sorte que la personne devient par défaut auteur de l'article
151
 *   qu'elle crée.
152
 *
153
 * @pipeline_appel pre_insertion
154
 * @pipeline_appel post_insertion
155
 *
156
 * @global array meta
157
 * @global array visiteur_session
158
 * @global string spip_lang
159
 *
160
 * @param int $id_rubrique
161
 *     Identifiant de la rubrique parente
162
 * @param array|null $set
163
 * @return int
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...
164
 *     Identifiant du nouvel article
165
 *
166
 */
167
function article_inserer($id_rubrique, $set = null) {
168
169
	// Si id_rubrique vaut 0 ou n'est pas definie, creer l'article
170
	// dans la premiere rubrique racine
171 View Code Duplication
	if (!$id_rubrique = intval($id_rubrique)) {
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...
172
		$row = sql_fetsel("id_rubrique, id_secteur, lang", "spip_rubriques", "id_parent=0", '', '0+titre,titre', "1");
173
		$id_rubrique = $row['id_rubrique'];
174
	} else {
175
		$row = sql_fetsel("lang, id_secteur", "spip_rubriques", "id_rubrique=$id_rubrique");
176
	}
177
178
	// eviter $id_secteur = NULL (erreur sqlite) si la requete precedente echoue 
179
	// cas de id_rubrique = -1 par exemple avec plugin "pages"
180
	$id_secteur = isset($row['id_secteur']) ? $row['id_secteur'] : 0;
181
	$lang_rub = isset($row['lang']) ? $row['lang'] : '';
182
183
	$lang = "";
184
	$choisie = 'non';
185
	// La langue a la creation : si les liens de traduction sont autorises
186
	// dans les rubriques, on essaie avec la langue de l'auteur,
187
	// ou a defaut celle de la rubrique
188
	// Sinon c'est la langue de la rubrique qui est choisie + heritee
189
	if (!empty($GLOBALS['meta']['multi_objets']) and in_array('spip_articles',
190
			explode(',', $GLOBALS['meta']['multi_objets']))
191
	) {
192
		lang_select($GLOBALS['visiteur_session']['lang']);
193
		if (in_array($GLOBALS['spip_lang'],
194
			explode(',', $GLOBALS['meta']['langues_multilingue']))) {
195
			$lang = $GLOBALS['spip_lang'];
196
			$choisie = 'oui';
197
		}
198
	}
199
200
	if (!$lang) {
201
		$choisie = 'non';
202
		$lang = $lang_rub ? $lang_rub : $GLOBALS['meta']['langue_site'];
203
	}
204
205
	$champs = array(
206
		'id_rubrique' => $id_rubrique,
207
		'id_secteur' => $id_secteur,
208
		'statut' => 'prepa',
209
		'date' => date('Y-m-d H:i:s'),
210
		'lang' => $lang,
211
		'langue_choisie' => $choisie
212
	);
213
214
	if ($set) {
215
		$champs = array_merge($champs, $set);
216
	}
217
218
	// Envoyer aux plugins
219
	$champs = pipeline('pre_insertion',
220
		array(
221
			'args' => array(
222
				'table' => 'spip_articles',
223
			),
224
			'data' => $champs
225
		)
226
	);
227
228
	$id_article = sql_insertq("spip_articles", $champs);
229
230
	// controler si le serveur n'a pas renvoye une erreur
231
	if ($id_article > 0) {
232
		$id_auteur = ((is_null(_request('id_auteur')) and isset($GLOBALS['visiteur_session']['id_auteur'])) ?
233
			$GLOBALS['visiteur_session']['id_auteur']
234
			: _request('id_auteur'));
235
		if ($id_auteur) {
236
			include_spip('action/editer_auteur');
237
			auteur_associer($id_auteur, array('article' => $id_article));
238
		}
239
	}
240
241
	pipeline('post_insertion',
242
		array(
243
			'args' => array(
244
				'table' => 'spip_articles',
245
				'id_objet' => $id_article
246
			),
247
			'data' => $champs
248
		)
249
	);
250
251
	return $id_article;
252
}
253
254
255
/**
256
 * Modification des statuts d'un article
257
 *
258
 * Modifie la langue, la rubrique ou les statuts d'un article.
259
 *
260
 * @global array $GLOBALS ['meta']
261
 *
262
 * @pipeline_appel pre_edition
263
 * @pipeline_appel post_edition
264
 *
265
 * @param int $id_article
266
 *     Identifiant de l'article
267
 * @param array $c
268
 *     Couples (colonne => valeur) des données à instituer
269
 *     Les colonnes 'statut' et 'id_parent' sont liées, car un admin restreint
270
 *     peut deplacer un article publié vers une rubrique qu'il n'administre pas
271
 * @param bool $calcul_rub
272
 *     True pour changer le statut des rubriques concernées si un article
273
 *     change de statut ou est déplacé dans une autre rubrique
274
 * @return string
275
 *     Chaîne vide
276
 */
277
function article_instituer($id_article, $c, $calcul_rub = true) {
278
279
	include_spip('inc/autoriser');
280
	include_spip('inc/rubriques');
281
	include_spip('inc/modifier');
282
283
	$row = sql_fetsel("statut, date, id_rubrique", "spip_articles", "id_article=$id_article");
284
	$id_rubrique = $row['id_rubrique'];
285
	$statut_ancien = $statut = $row['statut'];
286
	$date_ancienne = $date = $row['date'];
287
	$champs = array();
288
289
	$d = isset($c['date']) ? $c['date'] : null;
290
	$s = isset($c['statut']) ? $c['statut'] : $statut;
291
292
	// cf autorisations dans inc/instituer_article
293
	if ($s != $statut or ($d and $d != $date)) {
294
		if (autoriser('publierdans', 'rubrique', $id_rubrique)) {
295
			$statut = $champs['statut'] = $s;
296 View Code Duplication
		} else {
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...
297
			if (autoriser('modifier', 'article', $id_article) and $s != 'publie') {
298
				$statut = $champs['statut'] = $s;
299
			} else {
300
				spip_log("editer_article $id_article refus " . join(' ', $c));
301
			}
302
		}
303
304
		// En cas de publication, fixer la date a "maintenant"
305
		// sauf si $c commande autre chose
306
		// ou si l'article est deja date dans le futur
307
		// En cas de proposition d'un article (mais pas depublication), idem
308 View Code Duplication
		if ($champs['statut'] == 'publie'
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...
309
			or ($champs['statut'] == 'prop' and ($d or !in_array($statut_ancien, array('publie', 'prop'))))
310
		) {
311
			if ($d or strtotime($d = $date) > time()) {
312
				$champs['date'] = $date = $d;
313
			} else {
314
				$champs['date'] = $date = date('Y-m-d H:i:s');
315
			}
316
		}
317
	}
318
319
	// Verifier que la rubrique demandee existe et est differente
320
	// de la rubrique actuelle
321 View Code Duplication
	if (isset($c['id_parent'])
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...
322
		and $id_parent = $c['id_parent']
323
		and $id_parent != $id_rubrique
324
		and (sql_fetsel('1', "spip_rubriques", "id_rubrique=" . intval($id_parent)))
325
	) {
326
		$champs['id_rubrique'] = $id_parent;
327
328
		// si l'article etait publie
329
		// et que le demandeur n'est pas admin de la rubrique de destination
330
		// repasser l'article en statut 'propose'.
331
		if ($statut == 'publie'
332
			and !autoriser('publierdans', 'rubrique', $id_parent)
333
		) {
334
			$champs['statut'] = 'prop';
335
		}
336
	}
337
338
	// Envoyer aux plugins
339
	$champs = pipeline('pre_edition',
340
		array(
341
			'args' => array(
342
				'table' => 'spip_articles',
343
				'id_objet' => $id_article,
344
				'action' => 'instituer',
345
				'statut_ancien' => $statut_ancien,
346
				'date_ancienne' => $date_ancienne,
347
			),
348
			'data' => $champs
349
		)
350
	);
351
352
	if (!count($champs)) {
353
		return '';
354
	}
355
356
	// Envoyer les modifs.
357
	editer_article_heritage($id_article, $id_rubrique, $statut_ancien, $champs, $calcul_rub);
358
359
	// Invalider les caches
360
	include_spip('inc/invalideur');
361
	suivre_invalideur("id='article/$id_article'");
362
363
	if ($date) {
364
		$t = strtotime($date);
365
		$p = @$GLOBALS['meta']['date_prochain_postdate'];
366
		if ($t > time() and (!$p or ($t < $p))) {
367
			ecrire_meta('date_prochain_postdate', $t);
368
		}
369
	}
370
371
	// Pipeline
372
	pipeline('post_edition',
373
		array(
374
			'args' => array(
375
				'table' => 'spip_articles',
376
				'id_objet' => $id_article,
377
				'action' => 'instituer',
378
				'statut_ancien' => $statut_ancien,
379
				'date_ancienne' => $date_ancienne,
380
			),
381
			'data' => $champs
382
		)
383
	);
384
385
	// Notifications
386 View Code Duplication
	if ($notifications = charger_fonction('notifications', 'inc')) {
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...
387
		$notifications('instituerarticle', $id_article,
388
			array('statut' => $statut, 'statut_ancien' => $statut_ancien, 'date' => $date, 'date_ancienne' => $date_ancienne)
389
		);
390
	}
391
392
	return ''; // pas d'erreur
393
}
394
395
/**
396
 * Fabrique la requête de modification de l'article, avec champs hérités
397
 *
398
 * @global array $GLOBALS ['meta']
399
 *
400
 * @param int $id_article
401
 *     Identifiant de l'article
402
 * @param int $id_rubrique
403
 *     Identifiant de la rubrique parente
404
 * @param string $statut
405
 *     Statut de l'article (prop, publie, ...)
406
 * @param array $champs
407
 *     Couples (colonne => valeur) des champs qui ont été modifiés
408
 * @param bool $cond
409
 *     True pour actualiser le statut et date de publication de la rubrique
410
 *     parente si nécessaire
411
 * @return void|null
412
 *     null si aucune action à faire
413
 *     void sinon
414
 */
415
function editer_article_heritage($id_article, $id_rubrique, $statut, $champs, $cond = true) {
416
417
	// Si on deplace l'article
418
	//  changer aussi son secteur et sa langue (si heritee)
419
	if (isset($champs['id_rubrique'])) {
420
421
		$row_rub = sql_fetsel("id_secteur, lang", "spip_rubriques", "id_rubrique=" . sql_quote($champs['id_rubrique']));
422
423
		$langue = $row_rub['lang'];
424
		$champs['id_secteur'] = $row_rub['id_secteur'];
425 View Code Duplication
		if (sql_fetsel('1', 'spip_articles',
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...
426
			"id_article=" . intval($id_article) . " AND langue_choisie<>'oui' AND lang<>" . sql_quote($langue))) {
427
			$champs['lang'] = $langue;
428
		}
429
	}
430
431
	if (!$champs) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $champs 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...
432
		return;
433
	}
434
435
	sql_updateq('spip_articles', $champs, "id_article=" . intval($id_article));
436
437
	// Changer le statut des rubriques concernees 
438
439
	if ($cond) {
440
		include_spip('inc/rubriques');
441
		$postdate = ($GLOBALS['meta']["post_dates"] == "non" and isset($champs['date']) and (strtotime($champs['date']) < time())) ? $champs['date'] : false;
442
		calculer_rubriques_if($id_rubrique, $champs, $statut, $postdate);
443
	}
444
}
445
446
/**
447
 * Réunit les textes decoupés parce que trop longs
448
 *
449
 * @return void
450
 */
451
function trop_longs_articles() {
452
	if (is_array($plus = _request('texte_plus'))) {
453
		foreach ($plus as $n => $t) {
454
			$plus[$n] = preg_replace(",<!--SPIP-->[\n\r]*,", "", $t);
455
		}
456
		set_request('texte', join('', $plus) . _request('texte'));
457
	}
458
}
459