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

modifier.php ➔ modifier_contenu()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 5
dl 0
loc 5
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
 * Fonctions d'aides pour les fonctions d'objets de modification de contenus
15
 *
16
 * @package SPIP\Core\Objets\Modifications
17
 **/
18
19
if (!defined('_ECRIRE_INC_VERSION')) {
20
	return;
21
}
22
23
/**
24
 * Collecte des champs postés
25
 *
26
 * Fonction générique pour la collecte des posts
27
 * dans action/editer_xxx
28
 *
29
 * @param array $white_list
30
 *     Les champs à récupérer
31
 * @param array $black_list
32
 *     Les champs à ignorer
33
 * @param array|null $set
34
 *     array : Tableau des champs postés
35
 *     null  : Les champs sont obtenus par des _request() sur les noms de la white liste
36
 * @param bool $tous
37
 *     true : Recupère tous les champs de white_list meme ceux n'ayant pas ete postés
38
 * @return array
39
 *     Tableau des champs et valeurs collectées
40
 */
41
function collecter_requests($white_list, $black_list = array(), $set = null, $tous = false) {
42
	$c = $set;
43
	if (!$c) {
44
		$c = array();
45
		foreach ($white_list as $champ) {
46
			// on ne collecte que les champs reellement envoyes par defaut.
47
			// le cas d'un envoi de valeur NULL peut du coup poser probleme.
48
			$val = _request($champ);
49
			if ($tous or $val !== null) {
50
				$c[$champ] = $val;
51
			}
52
		}
53
		// on ajoute toujours la lang en saisie possible
54
		// meme si pas prevu au depart pour l'objet concerne
55
		if ($l = _request('changer_lang')) {
56
			$c['lang'] = $l;
57
		}
58
	}
59
	foreach ($black_list as $champ) {
60
		unset($c[$champ]);
61
	}
62
63
	return $c;
64
}
65
66
/**
67
 * Modifie le contenu d'un objet
68
 *
69
 * Fonction generique pour l'API de modification de contenu, qui se
70
 * charge entre autres choses d'appeler les pipelines pre_edition
71
 * et post_edition
72
 *
73
 * Attention, pour éviter des hacks on interdit des champs
74
 * (statut, id_secteur, id_rubrique, id_parent),
75
 * mais la securite doit étre assurée en amont
76
 *
77
 * @api
78
 * @param string $objet
79
 *     Type d'objet
80
 * @param int $id_objet
81
 *     Identifiant de l'objet
82
 * @param array $options
83
 *     array data : tableau des donnees sources utilisees pour la detection de conflit ($_POST sinon fourni ou si nul)
84
 *     array nonvide : valeur par defaut des champs que l'on ne veut pas vide
85
 *     string date_modif : champ a mettre a date('Y-m-d H:i:s') s'il y a modif
86
 *     string invalideur : id de l'invalideur eventuel
87
 *     array champs : non documente (utilise seulement par inc/rechercher ?)
88
 *     string action : action realisee, passee aux pipelines pre/post edition (par defaut 'modifier')
89
 *     bool indexation : deprecie
90
 * @param array|null $c
91
 *     Couples champ/valeur à modifier
92
 * @param string $serveur
93
 *     Nom du connecteur à la base de données
94
 * @return bool|string
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use string|false.

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...
95
 *     - false  : Aucune modification, aucun champ n'est à modifier
96
 *     - chaîne vide : Vide si tout s'est bien passé
97
 *     - chaîne : Texte d'un message d'erreur
98
 */
99
function objet_modifier_champs($objet, $id_objet, $options, $c = null, $serveur = '') {
100
	if (!$id_objet = intval($id_objet)) {
101
		spip_log('Erreur $id_objet non defini', 'warn');
102
103
		return _T('erreur_technique_enregistrement_impossible');
104
	}
105
106
	include_spip('inc/filtres');
107
108
	$table_objet = table_objet($objet, $serveur);
109
	$spip_table_objet = table_objet_sql($objet, $serveur);
110
	$id_table_objet = id_table_objet($objet, $serveur);
111
	$trouver_table = charger_fonction('trouver_table', 'base');
112
	$desc = $trouver_table($spip_table_objet, $serveur);
113
114
	// Appels incomplets (sans $c)
115
	if (!is_array($c)) {
116
		spip_log('erreur appel objet_modifier_champs(' . $objet . '), manque $c');
117
118
		return _T('erreur_technique_enregistrement_impossible');
119
	}
120
121
	// Securite : certaines variables ne sont jamais acceptees ici
122
	// car elles ne relevent pas de autoriser(xxx, modifier) ;
123
	// il faut passer par instituer_XX()
124
	// TODO: faut-il passer ces variables interdites
125
	// dans un fichier de description separe ?
126
	unset($c['statut']);
127
	unset($c['id_parent']);
128
	unset($c['id_rubrique']);
129
	unset($c['id_secteur']);
130
131
	// Gerer les champs non vides
132 View Code Duplication
	if (isset($options['nonvide']) and is_array($options['nonvide'])) {
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...
133
		foreach ($options['nonvide'] as $champ => $sinon) {
134
			if (isset($c[$champ]) and $c[$champ] === '') {
135
				$c[$champ] = $sinon;
136
			}
137
		}
138
	}
139
140
141
	// N'accepter que les champs qui existent
142
	// TODO: ici aussi on peut valider les contenus
143
	// en fonction du type
144
	$champs = array();
145 View Code Duplication
	foreach ($desc['field'] as $champ => $ignore) {
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...
146
		if (isset($c[$champ])) {
147
			$champs[$champ] = $c[$champ];
148
		}
149
	}
150
151
	// Nettoyer les valeurs
152
	$champs = array_map('corriger_caracteres', $champs);
153
154
	// Envoyer aux plugins
155
	$champs = pipeline('pre_edition',
156
		array(
157
			'args' => array(
158
				'table' => $spip_table_objet, // compatibilite
159
				'table_objet' => $table_objet,
160
				'spip_table_objet' => $spip_table_objet,
161
				'desc' => $desc,
162
				'type' => $objet,
163
				'id_objet' => $id_objet,
164
				'data' => isset($options['data']) ? $options['data'] : null,
165
				'champs' => isset($options['champs']) ? $options['champs'] : array(), // [doc] c'est quoi ?
166
				'serveur' => $serveur,
167
				'action' => isset($options['action']) ? $options['action'] : 'modifier'
168
			),
169
			'data' => $champs
170
		)
171
	);
172
173
	if (!$champs) {
174
		return false;
175
	}
176
177
178
	// marquer le fait que l'objet est travaille par toto a telle date
179
	if ($GLOBALS['meta']['articles_modif'] != 'non') {
180
		include_spip('inc/drapeau_edition');
181
		signale_edition($id_objet, $GLOBALS['visiteur_session'], $objet);
182
	}
183
184
	// Verifier si les mises a jour sont pertinentes, datees, en conflit etc
185
	include_spip('inc/editer');
186
	if (!isset($options['data']) or is_null($options['data'])){
187
		$options['data'] = &$_POST;
188
	}
189
	$conflits = controler_md5($champs, $options['data'], $objet, $id_objet, $serveur);
190
	// cas hypothetique : normalement inc/editer verifie en amont le conflit edition
191
	// et gere l'interface
192
	// ici on ne renvoie donc qu'un messsage d'erreur, au cas ou on y arrive quand meme
193
	if ($conflits) {
194
		return _T('titre_conflit_edition');
195
	}
196
197
	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...
198
		// cas particulier de la langue : passer par instituer_langue_objet
199
		if (isset($champs['lang'])) {
200
			if ($changer_lang = $champs['lang']) {
201
				$id_rubrique = 0;
202
				if (isset($desc['field']['id_rubrique'])) {
203
					$parent = ($objet == 'rubrique') ? 'id_parent' : 'id_rubrique';
204
					$id_rubrique = sql_getfetsel($parent, $spip_table_objet, "$id_table_objet=" . intval($id_objet));
205
				}
206
				$instituer_langue_objet = charger_fonction('instituer_langue_objet', 'action');
207
				$champs['lang'] = $instituer_langue_objet($objet, $id_objet, $id_rubrique, $changer_lang, $serveur);
208
			}
209
			// on laisse 'lang' dans $champs,
210
			// ca permet de passer dans le pipeline post_edition et de journaliser
211
			// et ca ne gene pas qu'on refasse un sql_updateq dessus apres l'avoir
212
			// deja pris en compte
213
		}
214
215
		// la modif peut avoir lieu
216
217
		// faut-il ajouter date_modif ?
218
		if (isset($options['date_modif']) and $options['date_modif']
219
			and !isset($champs[$options['date_modif']])
220
		) {
221
			$champs[$options['date_modif']] = date('Y-m-d H:i:s');
222
		}
223
224
		// allez on commit la modif
225
		sql_updateq($spip_table_objet, $champs, "$id_table_objet=" . intval($id_objet), $serveur);
226
227
		// on verifie si elle est bien passee
228
		$moof = sql_fetsel(array_keys($champs), $spip_table_objet, "$id_table_objet=" . intval($id_objet), array(), array(),
229
			'', array(), $serveur);
230
		// si difference entre les champs, reperer les champs mal enregistres
231
		if ($moof != $champs) {
232
			$liste = array();
233
			foreach ($moof as $k => $v) {
234
				if ($v !== $champs[$k]
235
					// ne pas alerter si le champ est numerique est que les valeurs sont equivalentes
236
					and (!is_numeric($v) or intval($v) !== intval($champs[$k]))
237
					// ne pas alerter si le champ est date, qu'on a envoye une valeur vide et qu'on recupere une date nulle
238
					and (strlen($champs[$k]) or !in_array($v, ['0000-00-00 00:00:00', '0000-00-00']))
239
				) {
240
					$liste[] = $k;
241
					$conflits[$k]['post'] = $champs[$k];
242
					$conflits[$k]['save'] = $v;
243
244
					// cas specifique MySQL+emoji : si l'un est la
245
					// conversion utf8_noplanes de l'autre alors c'est OK
246
					if (defined('_MYSQL_NOPLANES') && _MYSQL_NOPLANES) {
247
						include_spip('inc/charsets');
248
						if ($v == utf8_noplanes($champs[$k])) {
249
							array_pop($liste);
250
						}
251
					}
252
				}
253
			}
254
			// si un champ n'a pas ete correctement enregistre, loger et retourner une erreur
255
			// c'est un cas exceptionnel
256
			if (count($liste)) {
257
				spip_log("Erreur enregistrement en base $objet/$id_objet champs :" . var_export($conflits, true),
258
					'modifier.' . _LOG_CRITIQUE);
259
260
				return _T('erreur_technique_enregistrement_champs',
261
					array('champs' => "<i>'" . implode("'</i>,<i>'", $liste) . "'</i>"));
262
			}
263
		}
264
265
		// Invalider les caches
266 View Code Duplication
		if (isset($options['invalideur']) and $options['invalideur']) {
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...
267
			include_spip('inc/invalideur');
268
			if (is_array($options['invalideur'])) {
269
				array_map('suivre_invalideur', $options['invalideur']);
270
			} else {
271
				suivre_invalideur($options['invalideur']);
272
			}
273
		}
274
275
		// Notifications, gestion des revisions...
276
		// en standard, appelle |nouvelle_revision ci-dessous
277
		pipeline('post_edition',
278
			array(
279
				'args' => array(
280
					'table' => $spip_table_objet,
281
					'table_objet' => $table_objet,
282
					'spip_table_objet' => $spip_table_objet,
283
					'desc' => $desc,
284
					'type' => $objet,
285
					'id_objet' => $id_objet,
286
					'champs' => isset($options['champs']) ? $options['champs'] : array(), // [doc] kesako ?
287
					'serveur' => $serveur,
288
					'action' => isset($options['action']) ? $options['action'] : 'modifier'
289
				),
290
				'data' => $champs
291
			)
292
		);
293
	}
294
295
	// journaliser l'affaire
296
	// message a affiner :-)
297
	include_spip('inc/filtres_mini');
298
	$qui = isset($GLOBALS['visiteur_session']['nom']) and $GLOBALS['visiteur_session']['nom'] ? $GLOBALS['visiteur_session']['nom'] : $GLOBALS['ip'];
299
	journal(_L($qui . ' a &#233;dit&#233; l&#8217;' . $objet . ' ' . $id_objet . ' (' . join('+',
300
			array_diff(array_keys($champs), array('date_modif'))) . ')'), array(
301
		'faire' => 'modifier',
302
		'quoi' => $objet,
303
		'id' => $id_objet
304
	));
305
306
	return '';
307
}
308