Subversion Repositories eFlore/Applications.cel

Rev

Rev 2458 | Rev 2461 | Go to most recent revision | Only display areas with differences | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 2458 Rev 2459
1
<?php
1
<?php
2
// declare(encoding='UTF-8');
2
// declare(encoding='UTF-8');
3
/**
3
/**
4
 * Service d'import de données d'observation du CEL au format XLS
4
 * Service d'import de données d'observation du CEL au format XLS
5
 *
5
 *
6
 * Sont define()'d commme n° de colonne tous les abbrevs retournés par
6
 * Sont define()'d commme n° de colonne tous les abbrevs retournés par
7
 * FormateurGroupeColonne::nomEnsembleVersListeColonnes() préfixés par C_  cf: detectionEntete()
7
 * FormateurGroupeColonne::nomEnsembleVersListeColonnes() préfixés par C_  cf: detectionEntete()
8
 *
8
 *
9
 * Exemple d'un test:
9
 * Exemple d'un test:
10
 * $ GET "/jrest/ExportXLS/22506?format=csv&range=*&limite=13" \
10
 * $ GET "/jrest/ExportXLS/22506?format=csv&range=*&limite=13" \
11
 *   | curl -F "upload=@-" -F utilisateur=22506 "/jrest/ImportXLS"
11
 *   | curl -F "upload=@-" -F utilisateur=22506 "/jrest/ImportXLS"
12
 * # 13 observations importées
12
 * # 13 observations importées
13
 * + cf MySQL general_log = 1
13
 * + cf MySQL general_log = 1
14
 *
14
 *
15
 * @internal   Mininum PHP version : 5.2
15
 * @internal   Mininum PHP version : 5.2
16
 * @category   CEL
16
 * @category   CEL
17
 * @package    Services
17
 * @package    Services
18
 * @subpackage Observations
18
 * @subpackage Observations
19
 * @version    0.1
19
 * @version    0.1
20
 * @author     Mathias CHOUET <mathias@tela-botanica.org>
20
 * @author     Mathias CHOUET <mathias@tela-botanica.org>
21
 * @author     Raphaël DROZ <raphael@tela-botanica.org>
21
 * @author     Raphaël DROZ <raphael@tela-botanica.org>
22
 * @author     Jean-Pascal MILCENT <jpm@tela-botanica.org>
22
 * @author     Jean-Pascal MILCENT <jpm@tela-botanica.org>
23
 * @author     Aurelien PERONNET <aurelien@tela-botanica.org>
23
 * @author     Aurelien PERONNET <aurelien@tela-botanica.org>
24
 * @license    GPL v3 <http://www.gnu.org/licenses/gpl.txt>
24
 * @license    GPL v3 <http://www.gnu.org/licenses/gpl.txt>
25
 * @license    CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
25
 * @license    CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
26
 * @copyright  1999-2014 Tela Botanica <accueil@tela-botanica.org>
26
 * @copyright  1999-2014 Tela Botanica <accueil@tela-botanica.org>
27
 */
27
 */
28
set_include_path(get_include_path() . PATH_SEPARATOR . dirname(dirname(realpath(__FILE__))) . '/lib');
28
set_include_path(get_include_path() . PATH_SEPARATOR . dirname(dirname(realpath(__FILE__))) . '/lib');
29
// TERM
29
// TERM
30
error_reporting(-1);
30
error_reporting(-1);
31
ini_set('html_errors', 0);
31
ini_set('html_errors', 0);
32
ini_set('xdebug.cli_color', 2);
32
ini_set('xdebug.cli_color', 2);
33
require_once('lib/PHPExcel/Classes/PHPExcel.php');
33
require_once 'lib/PHPExcel/Classes/PHPExcel.php';
34
require_once('FormateurGroupeColonne.php');
34
require_once 'FormateurGroupeColonne.php';
35
 
35
 
36
date_default_timezone_set('Europe/Paris');
36
date_default_timezone_set('Europe/Paris');
37
 
37
 
38
// nombre d'INSERT à cumuler par requête SQL
38
// nombre d'INSERT à cumuler par requête SQL
39
// (= nombre de lignes XLS à bufferiser)
39
// (= nombre de lignes XLS à bufferiser)
40
//define('NB_LIRE_LIGNE_SIMUL', 30);
40
//define('NB_LIRE_LIGNE_SIMUL', 30);
41
define('NB_LIRE_LIGNE_SIMUL', 5);
41
define('NB_LIRE_LIGNE_SIMUL', 5);
42
 
42
 
43
// en cas d'import d'un fichier CSV, utilise fgetcsv() plutôt
43
// en cas d'import d'un fichier CSV, utilise fgetcsv() plutôt
44
// que PHPExcel ce qui se traduit par un gain de performances très substanciel
44
// que PHPExcel ce qui se traduit par un gain de performances très substanciel
45
define('QUICK_CSV_IMPORT', TRUE);
45
define('QUICK_CSV_IMPORT', TRUE);
46
 
46
 
47
// Numbers of days between January 1, 1900 and 1970 (including 19 leap years)
47
// Numbers of days between January 1, 1900 and 1970 (including 19 leap years)
48
// see traiterDateObs()
48
// see traiterDateObs()
49
// define("MIN_DATES_DIFF", 25569);
49
// define("MIN_DATES_DIFF", 25569);
50
class MyReadFilter implements PHPExcel_Reader_IReadFilter {
50
class MyReadFilter implements PHPExcel_Reader_IReadFilter {
51
	// exclusion de colonnes
51
	// exclusion de colonnes
52
	public $exclues = array();
52
	public $exclues = array();
53
 
53
 
54
	// lecture par morceaux
54
	// lecture par morceaux
55
	public $ligne_debut = 0;
55
	public $ligne_debut = 0;
56
	public $ligne_fin = 0;
56
	public $ligne_fin = 0;
57
 
57
 
58
	public static $gestion_mots_cles = null;
58
	public static $gestion_mots_cles = null;
59
 
59
 
60
	public function __construct() {}
60
	public function __construct() {}
61
 
61
 
62
	public function def_interval($debut, $nb) {
62
	public function def_interval($debut, $nb) {
63
		$this->ligne_debut = $debut;
63
		$this->ligne_debut = $debut;
64
		$this->ligne_fin = $debut + $nb;
64
		$this->ligne_fin = $debut + $nb;
65
	}
65
	}
66
 
66
 
67
	public function readCell($colonne, $ligne, $worksheetName = '') {
67
	public function readCell($colonne, $ligne, $worksheetName = '') {
68
		if(@$this->exclues[$colonne]) return false;
68
		if(@$this->exclues[$colonne]) return false;
69
		// si des n° de morceaux ont été initialisés, on filtre...
69
		// si des n° de morceaux ont été initialisés, on filtre...
70
		if($this->ligne_debut && ($ligne < $this->ligne_debut || $ligne >= $this->ligne_fin)) return false;
70
		if($this->ligne_debut && ($ligne < $this->ligne_debut || $ligne >= $this->ligne_fin)) return false;
71
		return true;
71
		return true;
72
	}
72
	}
73
}
73
}
74
 
74
 
75
function __anonyme_1($v) {	return !$v['importable']; }
75
function __anonyme_1($v) {	return !$v['importable']; }
76
function __anonyme_2(&$v) {	$v = $v['nom']; }
76
function __anonyme_2(&$v) {	$v = $v['nom']; }
77
function __anonyme_3($cell) { return !is_null($cell); };
77
function __anonyme_3($cell) { return !is_null($cell); };
78
function __anonyme_5($item) { return is_null($item) ? '?' : $item; }
78
function __anonyme_5($item) { return is_null($item) ? '?' : $item; }
79
function __anonyme_6() { return NULL; }
79
function __anonyme_6() { return NULL; }
80
 
80
 
81
class ImportXLS extends Cel  {
81
class ImportXLS extends Cel  {
82
	static function __anonyme_4(&$item, $key) { $item = self::quoteNonNull(trim($item)); }
82
	static function __anonyme_4(&$item, $key) { $item = self::quoteNonNull(trim($item)); }
83
 
83
 
84
	static $ordre_BDD = Array(
84
	static $ordre_BDD = Array(
85
		"ce_utilisateur",
85
		"ce_utilisateur",
86
		"prenom_utilisateur",
86
		"prenom_utilisateur",
87
		"nom_utilisateur",
87
		"nom_utilisateur",
88
		"courriel_utilisateur",
88
		"courriel_utilisateur",
89
		"ordre",
89
		"ordre",
90
		"nom_sel",
90
		"nom_sel",
91
		"nom_sel_nn",
91
		"nom_sel_nn",
92
		"nom_ret",
92
		"nom_ret",
93
		"nom_ret_nn",
93
		"nom_ret_nn",
94
		"nt",
94
		"nt",
95
		"famille",
95
		"famille",
96
		"nom_referentiel",
96
		"nom_referentiel",
97
		"zone_geo",
97
		"zone_geo",
98
		"ce_zone_geo",
98
		"ce_zone_geo",
99
		"date_observation",
99
		"date_observation",
100
		"lieudit",
100
		"lieudit",
101
		"station",
101
		"station",
102
		"milieu",
102
		"milieu",
103
		"mots_cles_texte",
103
		"mots_cles_texte",
104
		"commentaire",
104
		"commentaire",
105
		"transmission",
105
		"transmission",
106
		"date_creation",
106
		"date_creation",
107
		"date_modification",
107
		"date_modification",
108
		"date_transmission",
108
		"date_transmission",
109
		"latitude",
109
		"latitude",
110
		"longitude",
110
		"longitude",
111
		"altitude",
111
		"altitude",
112
		"abondance",
112
		"abondance",
113
		"certitude",
113
		"certitude",
114
		"phenologie",
114
		"phenologie",
115
		"code_insee_calcule"
115
		"code_insee_calcule"
116
	);
116
	);
117
 
117
 
118
	// cf: initialiser_pdo_ordered_statements()
118
	// cf: initialiser_pdo_ordered_statements()
119
	// eg: "INSERT INTO cel_obs (ce_utilisateur, ..., phenologie, code_insee_calcule) VALUES"
119
	// eg: "INSERT INTO cel_obs (ce_utilisateur, ..., phenologie, code_insee_calcule) VALUES"
120
	// colonnes statiques d'abord, les autres ensuite, dans l'ordre de $ordre_BDD
120
	// colonnes statiques d'abord, les autres ensuite, dans l'ordre de $ordre_BDD
121
	static $insert_prefix_ordre;
121
	static $insert_prefix_ordre;
122
	// eg: "(<id>, <prenom>, <nom>, <email>, now(), now(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
122
	// eg: "(<id>, <prenom>, <nom>, <email>, now(), now(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
123
	// dont le nombre de placeholder dépend du nombre de colonnes non-statiques
123
	// dont le nombre de placeholder dépend du nombre de colonnes non-statiques
124
	// colonnes statiques d'abord, les autres ensuite, dans l'ordre de $ordre_BDD
124
	// colonnes statiques d'abord, les autres ensuite, dans l'ordre de $ordre_BDD
125
	static $insert_ligne_pattern_ordre;
125
	static $insert_ligne_pattern_ordre;
126
 
126
 
127
	// seconde (meilleure) possibilité
127
	// seconde (meilleure) possibilité
128
	// cf: initialiser_pdo_statements()
128
	// cf: initialiser_pdo_statements()
129
	// eg: "INSERT INTO cel_obs (ce_utilisateur, ..., date_creation, ...phenologie, code_insee_calcule) VALUES"
129
	// eg: "INSERT INTO cel_obs (ce_utilisateur, ..., date_creation, ...phenologie, code_insee_calcule) VALUES"
130
	static $insert_prefix;
130
	static $insert_prefix;
131
	// eg: "(<id>, <prenom>, <nom>, <email>, ?, ?, ?, now(), now(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
131
	// eg: "(<id>, <prenom>, <nom>, <email>, ?, ?, ?, now(), now(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"
132
	// dont le nombre de placeholder dépend du nombre de colonnes non-statiques
132
	// dont le nombre de placeholder dépend du nombre de colonnes non-statiques
133
	static $insert_ligne_pattern;
133
	static $insert_ligne_pattern;
134
 
134
 
135
	/*
135
	/*
136
	 Ces colonnes:
136
	 Ces colonnes:
137
	 - sont propres à l'ensemble des enregistrements uploadés
137
	 - sont propres à l'ensemble des enregistrements uploadés
138
	 - sont indépendantes du numéro de lignes
138
	 - sont indépendantes du numéro de lignes
139
	 - n'ont pas de valeur par défaut dans la structure de la table
139
	 - n'ont pas de valeur par défaut dans la structure de la table
140
	 - nécessitent une initialisation dans le cadre de l'upload
140
	 - nécessitent une initialisation dans le cadre de l'upload
141
	 initialiser_colonnes_statiques() y merge les données d'identification utilisateur
141
	 initialiser_colonnes_statiques() y merge les données d'identification utilisateur
142
	*/
142
	*/
143
	public $colonnes_statiques = array(
143
	public $colonnes_statiques = array(
144
		"ce_utilisateur" => NULL,
144
		"ce_utilisateur" => NULL,
145
		"prenom_utilisateur" => NULL,
145
		"prenom_utilisateur" => NULL,
146
		"nom_utilisateur" => NULL,
146
		"nom_utilisateur" => NULL,
147
		"courriel_utilisateur" => NULL,
147
		"courriel_utilisateur" => NULL,
148
 
148
 
149
		// fixes (fonction SQL)
149
		// fixes (fonction SQL)
150
		// XXX future: mais pourraient varier dans le futur si la mise-à-jour
150
		// XXX future: mais pourraient varier dans le futur si la mise-à-jour
151
		// d'observation est implémentée
151
		// d'observation est implémentée
152
		"date_creation" => "now()",
152
		"date_creation" => "now()",
153
		"date_modification" => "now()",
153
		"date_modification" => "now()",
154
	);
154
	);
155
 
155
 
156
	public static $prefixe_colonnes_etendues = 'ext:';
156
	public static $prefixe_colonnes_etendues = 'ext:';
157
	public static $indexes_colonnes_etendues = Array();
157
	public static $indexes_colonnes_etendues = Array();
158
	public static $gestion_champs_etendus = null;
158
	public static $gestion_champs_etendus = null;
159
 
159
 
160
	public $id_utilisateur = NULL;
160
	public $id_utilisateur = NULL;
161
 
161
 
162
	// erreurs d'import
162
	// erreurs d'import
163
	public $bilan = Array();
163
	public $bilan = Array();
164
 
164
 
165
	// cache (pour traiterLocalisation() pour l'instant)
165
	// cache (pour traiterLocalisation() pour l'instant)
166
	static $cache = Array('geo' => array());
166
	static $cache = Array('geo' => array());
167
 
167
 
168
	function ImportXLS($config) {
168
	function ImportXLS($config) {
169
		parent::__construct($config);
169
		parent::__construct($config);
170
	}
170
	}
171
 
171
 
172
	function createElement($pairs) {
172
	function createElement($pairs) {
173
		if (!isset($pairs['utilisateur']) || trim($pairs['utilisateur']) == '') {
173
		if (!isset($pairs['utilisateur']) || trim($pairs['utilisateur']) == '') {
174
			exit('0');
174
			exit('0');
175
		}
175
		}
176
 
176
 
177
		$id_utilisateur = intval($pairs['utilisateur']);
177
		$id_utilisateur = intval($pairs['utilisateur']);
178
		$this->id_utilisateur = $id_utilisateur; // pour traiterImage();
178
		$this->id_utilisateur = $id_utilisateur; // pour traiterImage();
179
 
179
 
180
		if (!isset($_SESSION)) {
180
		if (!isset($_SESSION)) {
181
			session_start();
181
			session_start();
182
		}
182
		}
183
		$this->controleUtilisateur($id_utilisateur);
183
		$this->controleUtilisateur($id_utilisateur);
184
 
184
 
185
		$this->utilisateur = $this->getInfosComplementairesUtilisateur($id_utilisateur);
185
		$this->utilisateur = $this->getInfosComplementairesUtilisateur($id_utilisateur);
186
		$this->initialiser_colonnes_statiques($id_utilisateur);
186
		$this->initialiser_colonnes_statiques($id_utilisateur);
187
		list(self::$insert_prefix, self::$insert_ligne_pattern) = $this->initialiser_pdo_statements($this->colonnes_statiques);
187
		list(self::$insert_prefix, self::$insert_ligne_pattern) = $this->initialiser_pdo_statements($this->colonnes_statiques);
188
 
188
 
189
		$infos_fichier = array_pop($_FILES);
189
		$infos_fichier = array_pop($_FILES);
190
 
190
 
191
		// renomme le fichier pour lui ajouter son extension initiale, ce qui
191
		// renomme le fichier pour lui ajouter son extension initiale, ce qui
192
		// permet (une sorte) d'autodétection du format.
192
		// permet (une sorte) d'autodétection du format.
193
		$fichier = $infos_fichier['tmp_name'];
193
		$fichier = $infos_fichier['tmp_name'];
194
		$extension = pathinfo($infos_fichier['name'], PATHINFO_EXTENSION);
194
		$extension = pathinfo($infos_fichier['name'], PATHINFO_EXTENSION);
195
		if ( (strlen($extension) == 3 || strlen($extension) == 4) && (@rename($fichier, "$fichier.$extension"))) {
195
		if ( (strlen($extension) == 3 || strlen($extension) == 4) && (@rename($fichier, "$fichier.$extension"))) {
196
			$fichier = "$fichier.$extension";
196
			$fichier = "$fichier.$extension";
197
		}
197
		}
198
 
198
 
199
		$objReader = PHPExcel_IOFactory::createReaderForFile($fichier);
199
		$objReader = PHPExcel_IOFactory::createReaderForFile($fichier);
200
		// TODO: check if compatible with toArray(<1>,<2>,TRUE,<4>)
200
		// TODO: check if compatible with toArray(<1>,<2>,TRUE,<4>)
201
		$objReader->setReadDataOnly(true);
201
		$objReader->setReadDataOnly(true);
202
 
202
 
203
		// TODO: is_a obsolete entre 5.0 et 5.3, retirer le @ à terme
203
		// TODO: is_a obsolete entre 5.0 et 5.3, retirer le @ à terme
204
		$IS_CSV = @is_a($objReader, 'PHPExcel_Reader_CSV') && QUICK_CSV_IMPORT;
204
		$IS_CSV = @is_a($objReader, 'PHPExcel_Reader_CSV') && QUICK_CSV_IMPORT;
205
		// en cas d'usage de fgetcsv, testons que nous pouvons compter les lignes
205
		// en cas d'usage de fgetcsv, testons que nous pouvons compter les lignes
206
		if ($IS_CSV) {
206
		if ($IS_CSV) {
207
			$nb_lignes = intval(exec("wc -l $fichier"));
207
			$nb_lignes = intval(exec("wc -l $fichier"));
208
		}
208
		}
209
		// et, le cas échéant, fallback sur PHPExcel à nouveau. La raison de ce test ici est
209
		// et, le cas échéant, fallback sur PHPExcel à nouveau. La raison de ce test ici est
210
		// l'instabilité du serveur (safe_mode, safe_mode_exec_dir, symlink vers binaires pour exec(), ... multiples points-of-failure)
210
		// l'instabilité du serveur (safe_mode, safe_mode_exec_dir, symlink vers binaires pour exec(), ... multiples points-of-failure)
211
		if ($IS_CSV && !$nb_lignes) {
211
		if ($IS_CSV && !$nb_lignes) {
212
			$IS_CSV = FALSE;
212
			$IS_CSV = FALSE;
213
		}
213
		}
214
 
214
 
215
		if ($IS_CSV) {
215
		if ($IS_CSV) {
216
			$objReader->setDelimiter(',')
216
			$objReader->setDelimiter(',')
217
				->setEnclosure('"')
217
				->setEnclosure('"')
218
				->setLineEnding("\n")
218
				->setLineEnding("\n")
219
				->setSheetIndex(0);
219
				->setSheetIndex(0);
220
		}
220
		}
221
 
221
 
222
		// on ne conserve que l'en-tête
222
		// on ne conserve que l'en-tête
223
		$filtre = new MyReadFilter();
223
		$filtre = new MyReadFilter();
224
		$filtre->def_interval(1, 2);
224
		$filtre->def_interval(1, 2);
225
		$objReader->setReadFilter($filtre);
225
		$objReader->setReadFilter($filtre);
226
 
226
 
227
		$objPHPExcel = $objReader->load($fichier);
227
		$objPHPExcel = $objReader->load($fichier);
228
		$obj_infos = $objReader->listWorksheetInfo($fichier);
228
		$obj_infos = $objReader->listWorksheetInfo($fichier);
229
 
229
 
230
		if ($IS_CSV) {
230
		if ($IS_CSV) {
231
			// $nb_lignes est déjà défini ci-dessus
231
			// $nb_lignes est déjà défini ci-dessus
232
			$csvFileHandler = fopen($fichier, 'r');
232
			$csvFileHandler = fopen($fichier, 'r');
233
			// nous utilisons la valeur de retour dans un but informatif de l'utilisateur à la
233
			// nous utilisons la valeur de retour dans un but informatif de l'utilisateur à la
234
			// fin de l'import, *mais aussi* dans un array_diff_key() ci-dessous car bien que dans le
234
			// fin de l'import, *mais aussi* dans un array_diff_key() ci-dessous car bien que dans le
235
			// fond le "parser" fgetcsv() n'ait pas d'intérêt à connaître les colonnes à ignorer,
235
			// fond le "parser" fgetcsv() n'ait pas d'intérêt à connaître les colonnes à ignorer,
236
			// il se trouve que celles-ci peuvent interférer sur des fonctions comme traiterEspece()
236
			// il se trouve que celles-ci peuvent interférer sur des fonctions comme traiterEspece()
237
			// cf test "ref-nom-num.test.php" pour lequel l'élément C_NOM_SEL vaudrait 3 et $ligne serait array(3 => -42)
237
			// cf test "ref-nom-num.test.php" pour lequel l'élément C_NOM_SEL vaudrait 3 et $ligne serait array(3 => -42)
238
			$filtre->exclues = self::detectionEntete(fgetcsv($csvFileHandler), TRUE);
238
			$filtre->exclues = self::detectionEntete(fgetcsv($csvFileHandler), TRUE);
239
		} else {
239
		} else {
240
			// XXX: indépendant du readFilter ?
240
			// XXX: indépendant du readFilter ?
241
			$nb_lignes = $obj_infos[0]['totalRows'];
241
			$nb_lignes = $obj_infos[0]['totalRows'];
242
			$donnees = $objPHPExcel->getActiveSheet()->toArray(NULL, FALSE, TRUE, TRUE);
242
			$donnees = $objPHPExcel->getActiveSheet()->toArray(NULL, FALSE, TRUE, TRUE);
243
			$filtre->exclues = self::detectionEntete($donnees[1]);
243
			$filtre->exclues = self::detectionEntete($donnees[1]);
244
		}
244
		}
245
 
245
 
246
		$obs_ajouts = 0;
246
		$obs_ajouts = 0;
247
		$obs_maj = 0;
247
		$obs_maj = 0;
248
		$nb_images_ajoutees = 0;
248
		$nb_images_ajoutees = 0;
249
		$nb_mots_cle_ajoutes = 0;
249
		$nb_mots_cle_ajoutes = 0;
250
		$nb_champs_etendus_inseres = 0;
250
		$nb_champs_etendus_inseres = 0;
251
 
251
 
252
		$dernier_ordre = Cel::db()->requeter("SELECT MAX(ordre) AS ordre FROM cel_obs WHERE ce_utilisateur = $id_utilisateur");
252
		$dernier_ordre = Cel::db()->requeter("SELECT MAX(ordre) AS ordre FROM cel_obs WHERE ce_utilisateur = $id_utilisateur");
253
		$dernier_ordre = intval($dernier_ordre[0]['ordre']) + 1;
253
		$dernier_ordre = intval($dernier_ordre[0]['ordre']) + 1;
254
		if (! $dernier_ordre) {
254
		if (! $dernier_ordre) {
255
			$dernier_ordre = 0;
255
			$dernier_ordre = 0;
256
		}
256
		}
257
 
257
 
258
		// on catch to les trigger_error(E_USER_NOTICE);
258
		// on catch to les trigger_error(E_USER_NOTICE);
259
		set_error_handler(array($this, 'erreurs_stock'), E_USER_NOTICE);
259
		set_error_handler(array($this, 'erreurs_stock'), E_USER_NOTICE);
260
		$this->taxon_info_webservice = new RechercheInfosTaxonBeta($this->config, NULL);
260
		$this->taxon_info_webservice = new RechercheInfosTaxonBeta($this->config, NULL);
261
 
261
 
262
		// lecture par morceaux (chunks), NB_LIRE_LIGNE_SIMUL lignes à fois
262
		// lecture par morceaux (chunks), NB_LIRE_LIGNE_SIMUL lignes à fois
263
		// pour aboutir des requêtes SQL d'insert groupés.
263
		// pour aboutir des requêtes SQL d'insert groupés.
264
		for ($ligne = 2; $ligne < $nb_lignes + NB_LIRE_LIGNE_SIMUL; $ligne += NB_LIRE_LIGNE_SIMUL) {
264
		for ($ligne = 2; $ligne < $nb_lignes + NB_LIRE_LIGNE_SIMUL; $ligne += NB_LIRE_LIGNE_SIMUL) {
265
			if (!$IS_CSV) {
265
			if (!$IS_CSV) {
266
				$filtre->def_interval($ligne, NB_LIRE_LIGNE_SIMUL);
266
				$filtre->def_interval($ligne, NB_LIRE_LIGNE_SIMUL);
267
				$objReader->setReadFilter($filtre);
267
				$objReader->setReadFilter($filtre);
268
 
268
 
269
				$objPHPExcel = $objReader->load($fichier)->getActiveSheet();
269
				$objPHPExcel = $objReader->load($fichier)->getActiveSheet();
270
 
270
 
271
				// set col typing
271
				// set col typing
272
				if (C_CE_ZONE_GEO != 'C_CE_ZONE_GEO') {
272
				if (C_CE_ZONE_GEO != 'C_CE_ZONE_GEO') {
273
					$objPHPExcel->getStyle(C_CE_ZONE_GEO . '2:' . C_CE_ZONE_GEO . $objPHPExcel->getHighestRow())->getNumberFormat()->setFormatCode('00000');
273
					$objPHPExcel->getStyle(C_CE_ZONE_GEO . '2:' . C_CE_ZONE_GEO . $objPHPExcel->getHighestRow())->getNumberFormat()->setFormatCode('00000');
274
				}
274
				}
275
				// TODO: set to string type
275
				// TODO: set to string type
276
				if (C_ZONE_GEO != 'C_ZONE_GEO') {
276
				if (C_ZONE_GEO != 'C_ZONE_GEO') {
277
					$objPHPExcel->getStyle(C_ZONE_GEO . '2:' . C_ZONE_GEO . $objPHPExcel->getHighestRow())->getNumberFormat()->setFormatCode('00000');
277
					$objPHPExcel->getStyle(C_ZONE_GEO . '2:' . C_ZONE_GEO . $objPHPExcel->getHighestRow())->getNumberFormat()->setFormatCode('00000');
278
				}
278
				}
279
				$donnees = $objPHPExcel->toArray(NULL, FALSE, TRUE, TRUE);
279
				$donnees = $objPHPExcel->toArray(NULL, FALSE, TRUE, TRUE);
280
			} else {
280
			} else {
281
				$i = NB_LIRE_LIGNE_SIMUL;
281
				$i = NB_LIRE_LIGNE_SIMUL;
282
				$donnees = array();
282
				$donnees = array();
283
				while ($i--) {
283
				while ($i--) {
284
					$tab = fgetcsv($csvFileHandler);
284
					$tab = fgetcsv($csvFileHandler);
285
					if (!$tab) {
285
					if (!$tab) {
286
						continue;
286
						continue;
287
					}
287
					}
288
					$donnees[] = array_diff_key($tab, $filtre->exclues);
288
					$donnees[] = array_diff_key($tab, $filtre->exclues);
289
				}
289
				}
290
			}
290
			}
291
 
291
 
292
			list($enregistrements, $images, $mots_cle, $champs_etendus) = self::chargerLignes($this, $donnees, $this->colonnes_statiques, $dernier_ordre);
292
			list($enregistrements, $images, $mots_cle, $champs_etendus) = self::chargerLignes($this, $donnees, $this->colonnes_statiques, $dernier_ordre);
293
			if (! $enregistrements) {
293
			if (! $enregistrements) {
294
				break;
294
				break;
295
			}
295
			}
296
 
296
 
297
			self::trierColonnes($enregistrements);
297
			self::trierColonnes($enregistrements);
298
			// normalement: NB_LIRE_LIGNE_SIMUL, sauf si une enregistrement ne semble pas valide
298
			// normalement: NB_LIRE_LIGNE_SIMUL, sauf si une enregistrement ne semble pas valide
299
			// ou bien lors du dernier chunk
299
			// ou bien lors du dernier chunk
300
 
300
 
301
			$nb_rec = count($enregistrements);
301
			$nb_rec = count($enregistrements);
302
			$sql_pattern = self::$insert_prefix.
302
			$sql_pattern = self::$insert_prefix.
303
				str_repeat(self::$insert_ligne_pattern.', ', $nb_rec - 1).
303
				str_repeat(self::$insert_ligne_pattern.', ', $nb_rec - 1).
304
				self::$insert_ligne_pattern;
304
				self::$insert_ligne_pattern;
305
 
305
 
306
			Cel::db()->beginTransaction();
306
			Cel::db()->beginTransaction();
307
			$stmt = Cel::db()->prepare($sql_pattern);
307
			$stmt = Cel::db()->prepare($sql_pattern);
308
			$donnees = array();
308
			$donnees = array();
309
			foreach ($enregistrements as $e) {
309
			foreach ($enregistrements as $e) {
310
				$donnees = array_merge($donnees, array_values($e));
310
				$donnees = array_merge($donnees, array_values($e));
311
			}
311
			}
312
 
312
 
313
			$stmt->execute($donnees);
313
			$stmt->execute($donnees);
314
 
314
 
315
			$dernier_autoinc = Cel::db()->lastInsertId();
315
			$dernier_autoinc = Cel::db()->lastInsertId();
316
			Cel::db()->commit();
316
			Cel::db()->commit();
317
 
317
 
318
			if (! $dernier_autoinc) {
318
			if (! $dernier_autoinc) {
319
				trigger_error("l'insertion semble avoir échoué", E_USER_NOTICE);
319
				trigger_error("l'insertion semble avoir échoué", E_USER_NOTICE);
320
			}
320
			}
321
 
321
 
322
			$obs_ajouts += count($enregistrements);
322
			$obs_ajouts += count($enregistrements);
323
 
323
 
324
			$ordre_ids = self::chargerCorrespondancesIdOrdre($this, $enregistrements);
324
			$ordre_ids = self::chargerCorrespondancesIdOrdre($this, $enregistrements);
325
 
325
 
326
			$nb_images_ajoutees += self::stockerImages($enregistrements, $images, $ordre_ids);
326
			$nb_images_ajoutees += self::stockerImages($enregistrements, $images, $ordre_ids);
327
			$nb_mots_cle_ajoutes += self::stockerMotsCle($enregistrements, $mots_cle, $dernier_autoinc);
327
			$nb_mots_cle_ajoutes += self::stockerMotsCle($enregistrements, $mots_cle, $dernier_autoinc);
328
			$nb_champs_etendus_inseres += self::stockerChampsEtendus($champs_etendus, $ordre_ids, $this->config);
328
			$nb_champs_etendus_inseres += self::stockerChampsEtendus($champs_etendus, $ordre_ids, $this->config);
329
		}
329
		}
330
 
330
 
331
		restore_error_handler();
331
		restore_error_handler();
332
 
332
 
333
		if ($this->bilan) {
333
		if ($this->bilan) {
334
			echo implode("\n", $this->bilan) . "\n";
334
			echo implode("\n", $this->bilan) . "\n";
335
		}
335
		}
336
		printf('%1$d observation%2$s ajoutée%2$s' . "\n" .
336
		printf('%1$d observation%2$s ajoutée%2$s' . "\n" .
337
			'%3$d image%4$s attachée%4$s' . "\n" .
337
			'%3$d image%4$s attachée%4$s' . "\n" .
338
			(count($filtre->exclues) > 0 ? 'colonne%7$s non-traitée%7$s: %8$s' . "\n" : ''),
338
			(count($filtre->exclues) > 0 ? 'colonne%7$s non-traitée%7$s: %8$s' . "\n" : ''),
339
 
339
 
340
			$obs_ajouts,
340
			$obs_ajouts,
341
			$obs_ajouts > 1 ? 's' : '',
341
			$obs_ajouts > 1 ? 's' : '',
342
			$nb_images_ajoutees,
342
			$nb_images_ajoutees,
343
			$nb_images_ajoutees > 1 ? 's' : '',
343
			$nb_images_ajoutees > 1 ? 's' : '',
344
			$nb_mots_cle_ajoutes,
344
			$nb_mots_cle_ajoutes,
345
			$nb_mots_cle_ajoutes > 1 ? 's' : '',
345
			$nb_mots_cle_ajoutes > 1 ? 's' : '',
346
			count($filtre->exclues) > 1 ? 's' : '',
346
			count($filtre->exclues) > 1 ? 's' : '',
347
			implode(', ', $filtre->exclues));
347
			implode(', ', $filtre->exclues));
348
		die();
348
		die();
349
	}
349
	}
350
 
350
 
351
	/* detectionEntete() sert deux rôles:
351
	/* detectionEntete() sert deux rôles:
352
	 1) détecter le type de colonne attendu à partir des textes de la ligne d'en-tête afin de define()
352
	 1) détecter le type de colonne attendu à partir des textes de la ligne d'en-tête afin de define()
353
	 2) permet d'identifier les colonnes non-supportées/inutiles afin d'alléger le processus de parsing de PHPExcel
353
	 2) permet d'identifier les colonnes non-supportées/inutiles afin d'alléger le processus de parsing de PHPExcel
354
	 grace au ReadFilter (C'est le rôle de la valeur de retour)
354
	 grace au ReadFilter (C'est le rôle de la valeur de retour)
355
 
355
 
356
	 La raison de la présence du paramètre $numeric_keys est que pour réussir à identifier les colonnes à exclure nous
356
	 La raison de la présence du paramètre $numeric_keys est que pour réussir à identifier les colonnes à exclure nous
357
	 devons traiter un tableau représentant la ligne d'en-tête aussi bien:
357
	 devons traiter un tableau représentant la ligne d'en-tête aussi bien:
358
	  - sous forme associative pour PHPExcel (les clefs sont les lettres de l'alphabet)
358
	  - sous forme associative pour PHPExcel (les clefs sont les lettres de l'alphabet)
359
	  - sous forme de clefs numériques (fgetcsv())
359
	  - sous forme de clefs numériques (fgetcsv())
360
	  Le détecter après coup est difficile et pourtant cette distinction est importante car le comportement
360
	  Le détecter après coup est difficile et pourtant cette distinction est importante car le comportement
361
	  d'array_merge() (réordonnancement des clefs numérique) n'est pas souhaitable dans le second cas. */
361
	  d'array_merge() (réordonnancement des clefs numérique) n'est pas souhaitable dans le second cas. */
362
	static function detectionEntete($entete, $numeric_keys = FALSE) {
362
	static function detectionEntete($entete, $numeric_keys = FALSE) {
363
		$colonnes_reconnues = Array();
363
		$colonnes_reconnues = Array();
364
		$cols = FormateurGroupeColonne::nomEnsembleVersListeColonnes('standard,avance');
364
		$cols = FormateurGroupeColonne::nomEnsembleVersListeColonnes('standard,avance');
365
 
365
 
366
		foreach ($entete as $k => $v) {
366
		foreach ($entete as $k => $v) {
367
			// traite les colonnes en faisant fi de la casse et des accents
367
			// traite les colonnes en faisant fi de la casse et des accents
368
			$entete_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($v)));
368
			$entete_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($v)));
369
			foreach ($cols as $col) {
369
			foreach ($cols as $col) {
370
				$entete_officiel_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($col['nom'])));
370
				$entete_officiel_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($col['nom'])));
371
				$entete_officiel_abbrev = $col['abbrev'];
371
				$entete_officiel_abbrev = $col['abbrev'];
372
				if ($entete_simple == $entete_officiel_simple || $entete_simple == $entete_officiel_abbrev) {
372
				if ($entete_simple == $entete_officiel_simple || $entete_simple == $entete_officiel_abbrev) {
373
					// debug echo "define C_" . strtoupper($entete_officiel_abbrev) . ", $k ($v)\n";
373
					// debug echo "define C_" . strtoupper($entete_officiel_abbrev) . ", $k ($v)\n";
374
					define("C_" . strtoupper($entete_officiel_abbrev), $k);
374
					define("C_" . strtoupper($entete_officiel_abbrev), $k);
375
					$colonnes_reconnues[$k] = 1;
375
					$colonnes_reconnues[$k] = 1;
376
					break;
376
					break;
377
				}
377
				}
378
 
378
 
379
				if (strpos($v, self::$prefixe_colonnes_etendues) === 0) {
379
				if (strpos($v, self::$prefixe_colonnes_etendues) === 0) {
380
					$colonnes_reconnues[$k] = 1;
380
					$colonnes_reconnues[$k] = 1;
381
					self::$indexes_colonnes_etendues[$k] = $v;
381
					self::$indexes_colonnes_etendues[$k] = $v;
382
					break;
382
					break;
383
				}
383
				}
384
			}
384
			}
385
		}
385
		}
386
 
386
 
387
		// défini tous les index que nous utilisons à une valeur d'index de colonne Excel qui n'existe pas dans
387
		// défini tous les index que nous utilisons à une valeur d'index de colonne Excel qui n'existe pas dans
388
		// le tableau renvoyé par PHPExcel
388
		// le tableau renvoyé par PHPExcel
389
		// Attention cependant d'utiliser des indexes différenciés car traiterLonLat() et traiterEspece()
389
		// Attention cependant d'utiliser des indexes différenciés car traiterLonLat() et traiterEspece()
390
		// les utilisent
390
		// les utilisent
391
		foreach ($cols as $col) {
391
		foreach ($cols as $col) {
392
			if (!defined('C_'.strtoupper($col['abbrev']))) {
392
			if (!defined('C_'.strtoupper($col['abbrev']))) {
393
				define('C_'.strtoupper($col['abbrev']), 'C_'.strtoupper($col['abbrev']));
393
				define('C_'.strtoupper($col['abbrev']), 'C_'.strtoupper($col['abbrev']));
394
			}
394
			}
395
		}
395
		}
396
 
396
 
397
		// prépare le filtre de PHPExcel qui évitera le traitement de toutes les colonnes superflues
397
		// prépare le filtre de PHPExcel qui évitera le traitement de toutes les colonnes superflues
398
		$colonnesID_non_reconnues = array_diff_key($entete, $colonnes_reconnues);
398
		$colonnesID_non_reconnues = array_diff_key($entete, $colonnes_reconnues);
399
 
399
 
400
		// des colonnes de FormateurGroupeColonne::nomEnsembleVersListeColonnes()
400
		// des colonnes de FormateurGroupeColonne::nomEnsembleVersListeColonnes()
401
		// ne retient que celles marquées "importables"
401
		// ne retient que celles marquées "importables"
402
		$colonnes_automatiques = array_filter($cols, '__anonyme_1');
402
		$colonnes_automatiques = array_filter($cols, '__anonyme_1');
403
 
403
 
404
		// ne conserve que le nom long pour matcher avec la ligne XLS d'entête
404
		// ne conserve que le nom long pour matcher avec la ligne XLS d'entête
405
		array_walk($colonnes_automatiques, '__anonyme_2');
405
		array_walk($colonnes_automatiques, '__anonyme_2');
406
 
406
 
407
		$colonnesID_a_exclure = array_intersect($entete, $colonnes_automatiques);
407
		$colonnesID_a_exclure = array_intersect($entete, $colonnes_automatiques);
408
 
408
 
409
		if ($numeric_keys) {
409
		if ($numeric_keys) {
410
			return $colonnesID_non_reconnues + $colonnesID_a_exclure;
410
			return $colonnesID_non_reconnues + $colonnesID_a_exclure;
411
		}
411
		}
412
		return array_merge($colonnesID_non_reconnues, $colonnesID_a_exclure);
412
		return array_merge($colonnesID_non_reconnues, $colonnesID_a_exclure);
413
	}
413
	}
414
 
414
 
415
	static function chargerCorrespondancesIdOrdre($cel, $lignes) {
415
	static function chargerCorrespondancesIdOrdre($cel, $lignes) {
416
		$ordresObs = array();
416
		$ordresObs = array();
417
		foreach ($lignes as &$ligne) {
417
		foreach ($lignes as &$ligne) {
418
			$ordresObs[] = $ligne['ordre'];
418
			$ordresObs[] = $ligne['ordre'];
419
		}
419
		}
420
		$ordresObsConcat = implode(',', $ordresObs);
420
		$ordresObsConcat = implode(',', $ordresObs);
421
		$idUtilisateurP = Cel::db()->proteger($cel->id_utilisateur);
421
		$idUtilisateurP = Cel::db()->proteger($cel->id_utilisateur);
422
		$requete = 'SELECT id_observation, ordre '.
422
		$requete = 'SELECT id_observation, ordre '.
423
			'FROM cel_obs '.
423
			'FROM cel_obs '.
424
			"WHERE ordre IN ($ordresObsConcat) ".
424
			"WHERE ordre IN ($ordresObsConcat) ".
425
			"AND ce_utilisateur = $idUtilisateurP ".
425
			"AND ce_utilisateur = $idUtilisateurP ".
426
			' -- '.__FILE__.':'.__LINE__;
426
			' -- '.__FILE__.':'.__LINE__;
427
		$resultats = Cel::db()->requeter($requete);
427
		$resultats = Cel::db()->requeter($requete);
428
		$ordresIds = array();
428
		$ordresIds = array();
429
		foreach ($resultats as &$infos) {
429
		foreach ($resultats as &$infos) {
430
			$ordresIds[$infos['ordre']] = $infos['id_observation'];
430
			$ordresIds[$infos['ordre']] = $infos['id_observation'];
431
		}
431
		}
432
		return $ordresIds;
432
		return $ordresIds;
433
	}
433
	}
434
 
434
 
435
	/*
435
	/*
436
	 * charge un groupe de lignes
436
	 * charge un groupe de lignes
437
	 */
437
	 */
438
	static function chargerLignes($cel, $lignes, $colonnes_statiques, &$dernier_ordre) {
438
	static function chargerLignes($cel, $lignes, $colonnes_statiques, &$dernier_ordre) {
439
		$enregistrement = NULL;
439
		$enregistrement = NULL;
440
		$enregistrements = array();
440
		$enregistrements = array();
441
		$toutes_images = array();
441
		$toutes_images = array();
442
		$tous_mots_cle = array();
442
		$tous_mots_cle = array();
443
		$tous_champs_etendus = array();
443
		$tous_champs_etendus = array();
444
 
444
 
445
		foreach ($lignes as $ligne) {
445
		foreach ($lignes as $ligne) {
446
			// dans le cas de fgetcsv, on peut avoir des false additionnel (cf do/while l. 279)
446
			// dans le cas de fgetcsv, on peut avoir des false additionnel (cf do/while l. 279)
447
			if ($ligne === false) {
447
			if ($ligne === false) {
448
				continue;
448
				continue;
449
			}
449
			}
450
 
450
 
451
			// on a besoin des NULL pour éviter des notice d'index indéfini
451
			// on a besoin des NULL pour éviter des notice d'index indéfini
452
			if (! array_filter($ligne, '__anonyme_3')) {
452
			if (! array_filter($ligne, '__anonyme_3')) {
453
				continue;
453
				continue;
454
			}
454
			}
455
 
455
 
456
			if ($enregistrement = self::chargerLigne($ligne, $dernier_ordre, $cel)) {
456
			if ($enregistrement = self::chargerLigne($ligne, $dernier_ordre, $cel)) {
457
				// $enregistrements[] = array_merge($colonnes_statiques, $enregistrement);
457
				// $enregistrements[] = array_merge($colonnes_statiques, $enregistrement);
458
				$enregistrements[] = $enregistrement;
458
				$enregistrements[] = $enregistrement;
459
				$pos = count($enregistrements) - 1;
459
				$pos = count($enregistrements) - 1;
460
				$last = &$enregistrements[$pos];
460
				$last = &$enregistrements[$pos];
461
 
461
 
462
				if (isset($enregistrement['_images'])) {
462
				if (isset($enregistrement['_images'])) {
463
					// ne dépend pas de cel_obs, et seront insérées *après* les enregistrements
463
					// ne dépend pas de cel_obs, et seront insérées *après* les enregistrements
464
					// mais nous ne voulons pas nous priver de faire des INSERT multiples pour autant
464
					// mais nous ne voulons pas nous priver de faire des INSERT multiples pour autant
465
					$toutes_images[] = array(
465
					$toutes_images[] = array(
466
						'images' => $last['_images'],
466
						'images' => $last['_images'],
467
						'obs_pos' => $pos);
467
						'obs_pos' => $pos);
468
					// ce champ n'a pas à faire partie de l'insertion dans cel_obs,
468
					// ce champ n'a pas à faire partie de l'insertion dans cel_obs,
469
					// mais est utile pour la liaison avec les images
469
					// mais est utile pour la liaison avec les images
470
					unset($last['_images']);
470
					unset($last['_images']);
471
				}
471
				}
472
 
472
 
473
				if (isset($enregistrement['_mots_cle'])) {
473
				if (isset($enregistrement['_mots_cle'])) {
474
					// ne dépend pas de cel_obs, et seront insérés *après* les enregistrements
474
					// ne dépend pas de cel_obs, et seront insérés *après* les enregistrements
475
					// mais nous ne voulons pas nous priver de faire des INSERT multiples pour autant
475
					// mais nous ne voulons pas nous priver de faire des INSERT multiples pour autant
476
					$tous_mots_cle[] = array(
476
					$tous_mots_cle[] = array(
477
						'mots_cle' => $last['_mots_cle'],
477
						'mots_cle' => $last['_mots_cle'],
478
						'obs_pos' => $pos);
478
						'obs_pos' => $pos);
479
					unset($last['_mots_cle']);
479
					unset($last['_mots_cle']);
480
				}
480
				}
481
 
481
 
482
				if (isset($enregistrement['_champs_etendus'])) {
482
				if (isset($enregistrement['_champs_etendus'])) {
483
					$tous_champs_etendus[] = array(
483
					$tous_champs_etendus[] = array(
484
						'champs_etendus' => $last['_champs_etendus'],
484
						'champs_etendus' => $last['_champs_etendus'],
485
						'ordre' => $dernier_ordre);
485
						'ordre' => $dernier_ordre);
486
					unset($last['_champs_etendus']);
486
					unset($last['_champs_etendus']);
487
				}
487
				}
488
				$dernier_ordre++;
488
				$dernier_ordre++;
489
			}
489
			}
490
		}
490
		}
491
		return array($enregistrements, $toutes_images, $tous_mots_cle, $tous_champs_etendus);
491
		return array($enregistrements, $toutes_images, $tous_mots_cle, $tous_champs_etendus);
492
	}
492
	}
493
 
493
 
494
	static function trierColonnes(&$enregistrements) {
494
	static function trierColonnes(&$enregistrements) {
495
		foreach ($enregistrements as &$enregistrement) {
495
		foreach ($enregistrements as &$enregistrement) {
496
			$enregistrement = self::sortArrayByArray($enregistrement, self::$ordre_BDD);
496
			$enregistrement = self::sortArrayByArray($enregistrement, self::$ordre_BDD);
497
		}
497
		}
498
	}
498
	}
499
 
499
 
500
	static function stockerMotsCle($enregistrements, $tous_mots_cle, $lastid) {
500
	static function stockerMotsCle($enregistrements, $tous_mots_cle, $lastid) {
501
		$c = 0;
501
		$c = 0;
502
		// debug: var_dump($tous_mots_cle);die;
502
		// debug: var_dump($tous_mots_cle);die;
503
		foreach ($tous_mots_cle as $v) {
503
		foreach ($tous_mots_cle as $v) {
504
			$c += count($v['mots_cle']['to_insert']);
504
			$c += count($v['mots_cle']['to_insert']);
505
		}
505
		}
506
		return $c;
506
		return $c;
507
	}
507
	}
508
 
508
 
509
	static function stockerImages($enregistrements, $toutes_images, $ordre_ids) {
509
	static function stockerImages($enregistrements, $toutes_images, $ordre_ids) {
510
		$idsImagesObs = array();
510
		$idsImagesObs = array();
511
		foreach ($toutes_images as $images_pour_obs) {
511
		foreach ($toutes_images as $images_pour_obs) {
512
			$obs = $enregistrements[$images_pour_obs['obs_pos']];
512
			$obs = $enregistrements[$images_pour_obs['obs_pos']];
513
			$id_obs = $ordre_ids[$obs['ordre']]; // id réel de l'observation correspondant à l'ordre
513
			$id_obs = $ordre_ids[$obs['ordre']]; // id réel de l'observation correspondant à l'ordre
514
			foreach ($images_pour_obs['images'] as $image) {
514
			foreach ($images_pour_obs['images'] as $image) {
515
				$idsImagesObs[] = sprintf('(%d,%d, NOW())', $image['id_image'], $id_obs);
515
				$idsImagesObs[] = sprintf('(%d,%d, NOW())', $image['id_image'], $id_obs);
516
			}
516
			}
517
		}
517
		}
518
 
518
 
519
		if ($idsImagesObs) {
519
		if ($idsImagesObs) {
520
			$clauseValues = implode(', ', $idsImagesObs);
520
			$clauseValues = implode(', ', $idsImagesObs);
521
			// Utilisation de INSET pour faire des UPDATE multiples en une seule requête
521
			// Utilisation de INSET pour faire des UPDATE multiples en une seule requête
522
			$requete = 'INSERT INTO cel_images (id_image, ce_observation, date_liaison) '.
522
			$requete = 'INSERT INTO cel_images (id_image, ce_observation, date_liaison) '.
523
				"VALUES $clauseValues ".
523
				"VALUES $clauseValues ".
524
				'ON DUPLICATE KEY UPDATE ce_observation = VALUES(ce_observation), date_liaison = NOW() '.
524
				'ON DUPLICATE KEY UPDATE ce_observation = VALUES(ce_observation), date_liaison = NOW() '.
525
				' -- '.__FILE__.':'.__LINE__;
525
				' -- '.__FILE__.':'.__LINE__;
526
			Cel::db()->requeter($requete);
526
			Cel::db()->requeter($requete);
527
		}
527
		}
528
		return count($idsImagesObs);
528
		return count($idsImagesObs);
529
	}
529
	}
530
 
530
 
531
	/*
531
	/*
532
	 Aucune des valeurs présentes dans $enregistrement n'est quotée
532
	 Aucune des valeurs présentes dans $enregistrement n'est quotée
533
	 cad aucune des valeurs retournée par traiter{Espece|Localisation}()
533
	 cad aucune des valeurs retournée par traiter{Espece|Localisation}()
534
	 car ce tableau est passé à un PDO::preparedStatement() qui applique
534
	 car ce tableau est passé à un PDO::preparedStatement() qui applique
535
	  proprement les règle d'échappement.
535
	  proprement les règle d'échappement.
536
	*/
536
	*/
537
	static function chargerLigne($ligne, $dernier_ordre, $cel) {
537
	static function chargerLigne($ligne, $dernier_ordre, $cel) {
538
		// évite des notices d'index lors des trigger_error()
538
		// évite des notices d'index lors des trigger_error()
539
		$ref_ligne = !empty($ligne[C_NOM_SEL]) ? trim($ligne[C_NOM_SEL]) : '';
539
		$ref_ligne = !empty($ligne[C_NOM_SEL]) ? trim($ligne[C_NOM_SEL]) : '';
540
 
540
 
541
		// en premier car le résultat est utile pour
541
		// en premier car le résultat est utile pour
542
		// * traiter espèce (traiterEspece())
542
		// * traiter espèce (traiterEspece())
543
		// * traiter longitude et latitude (traiterLonLat())
543
		// * traiter longitude et latitude (traiterLonLat())
544
		$referentiel = self::identReferentiel(trim(strtolower(@$ligne[C_NOM_REFERENTIEL])), $ligne, $ref_ligne);
544
		$referentiel = self::identReferentiel(trim(strtolower(@$ligne[C_NOM_REFERENTIEL])), $ligne, $ref_ligne);
545
 
545
 
546
		// $espece est rempli de plusieurs informations
546
		// $espece est rempli de plusieurs informations
547
		$espece = array(
547
		$espece = array(
548
			C_NOM_SEL => NULL,
548
			C_NOM_SEL => NULL,
549
			C_NOM_SEL_NN => NULL,
549
			C_NOM_SEL_NN => NULL,
550
			C_NOM_RET => NULL,
550
			C_NOM_RET => NULL,
551
			C_NOM_RET_NN => NULL,
551
			C_NOM_RET_NN => NULL,
552
			C_NT => NULL,
552
			C_NT => NULL,
553
			C_FAMILLE => NULL);
553
			C_FAMILLE => NULL);
554
		self::traiterEspece($ligne, $espece, $referentiel, $cel->taxon_info_webservice);
554
		self::traiterEspece($ligne, $espece, $referentiel, $cel->taxon_info_webservice);
555
 
555
 
556
		if (!$espece[C_NOM_SEL]) {
556
		if (!$espece[C_NOM_SEL]) {
557
			$referentiel = Cel::$fallback_referentiel;
557
			$referentiel = Cel::$fallback_referentiel;
558
		}
558
		}
559
		if ($espece[C_NOM_SEL] && !$espece[C_NOM_SEL_NN]) {
559
		if ($espece[C_NOM_SEL] && !$espece[C_NOM_SEL_NN]) {
560
			$referentiel = Cel::$fallback_referentiel;
560
			$referentiel = Cel::$fallback_referentiel;
561
		}
561
		}
562
 
562
 
563
		// $localisation est rempli à partir de plusieurs champs: C_ZONE_GEO et C_CE_ZONE_GEO
563
		// $localisation est rempli à partir de plusieurs champs: C_ZONE_GEO et C_CE_ZONE_GEO
564
		$localisation = Array(C_ZONE_GEO => NULL, C_CE_ZONE_GEO => NULL);
564
		$localisation = Array(C_ZONE_GEO => NULL, C_CE_ZONE_GEO => NULL);
565
		self::traiterLocalisation($ligne, $localisation);
565
		self::traiterLocalisation($ligne, $localisation);
566
 
566
 
567
		// $transmission est utilisé pour date_transmission
567
		// $transmission est utilisé pour date_transmission
568
		// XXX: @ contre "Undefined index"
568
		// XXX: @ contre "Undefined index"
569
		@$transmission = in_array(strtolower(trim($ligne[C_TRANSMISSION])), array(1, 'oui')) ? 1 : 0;
569
		@$transmission = in_array(strtolower(trim($ligne[C_TRANSMISSION])), array(1, 'oui')) ? 1 : 0;
570
 
570
 
571
 
571
 
572
		// Dans ce tableau, seules devraient apparaître les données variable pour chaque ligne.
572
		// Dans ce tableau, seules devraient apparaître les données variable pour chaque ligne.
573
		// Dans ce tableau, l'ordre des clefs n'importe pas (cf: self::sortArrayByArray())
573
		// Dans ce tableau, l'ordre des clefs n'importe pas (cf: self::sortArrayByArray())
574
		$enregistrement = Array(
574
		$enregistrement = Array(
575
			"ordre" => $dernier_ordre,
575
			"ordre" => $dernier_ordre,
576
 
576
 
577
			"nom_sel" => $espece[C_NOM_SEL],
577
			"nom_sel" => $espece[C_NOM_SEL],
578
			"nom_sel_nn" => $espece[C_NOM_SEL_NN],
578
			"nom_sel_nn" => $espece[C_NOM_SEL_NN],
579
			"nom_ret" => $espece[C_NOM_RET],
579
			"nom_ret" => $espece[C_NOM_RET],
580
			"nom_ret_nn" => $espece[C_NOM_RET_NN],
580
			"nom_ret_nn" => $espece[C_NOM_RET_NN],
581
			"nt" => $espece[C_NT],
581
			"nt" => $espece[C_NT],
582
			"famille" => $espece[C_FAMILLE],
582
			"famille" => $espece[C_FAMILLE],
583
 
583
 
584
			"nom_referentiel" => $referentiel,
584
			"nom_referentiel" => $referentiel,
585
 
585
 
586
			"zone_geo" => $localisation[C_ZONE_GEO],
586
			"zone_geo" => $localisation[C_ZONE_GEO],
587
			"ce_zone_geo" => $localisation[C_CE_ZONE_GEO],
587
			"ce_zone_geo" => $localisation[C_CE_ZONE_GEO],
588
 
588
 
589
			// $ligne: uniquement pour les infos en cas de gestion d'erreurs (date incompréhensible)
589
			// $ligne: uniquement pour les infos en cas de gestion d'erreurs (date incompréhensible)
590
			"date_observation" => isset($ligne[C_DATE_OBSERVATION]) ? self::traiterDateObs($ligne[C_DATE_OBSERVATION], $ref_ligne) : NULL,
590
			"date_observation" => isset($ligne[C_DATE_OBSERVATION]) ? self::traiterDateObs($ligne[C_DATE_OBSERVATION], $ref_ligne) : NULL,
591
 
591
 
592
			"lieudit" => isset($ligne[C_LIEUDIT]) ? trim($ligne[C_LIEUDIT]) : NULL,
592
			"lieudit" => isset($ligne[C_LIEUDIT]) ? trim($ligne[C_LIEUDIT]) : NULL,
593
			"station" => isset($ligne[C_STATION]) ? trim($ligne[C_STATION]) : NULL,
593
			"station" => isset($ligne[C_STATION]) ? trim($ligne[C_STATION]) : NULL,
594
			"milieu" => isset($ligne[C_MILIEU]) ? trim($ligne[C_MILIEU]) : NULL,
594
			"milieu" => isset($ligne[C_MILIEU]) ? trim($ligne[C_MILIEU]) : NULL,
595
 
595
 
596
			"mots_cles_texte" => NULL, // TODO: foreign-key
596
			"mots_cles_texte" => NULL, // TODO: foreign-key
597
			// XXX: @ contre "Undefined index"
597
			// XXX: @ contre "Undefined index"
598
			"commentaire" => isset($ligne[C_COMMENTAIRE]) ? trim($ligne[C_COMMENTAIRE]) : NULL,
598
			"commentaire" => isset($ligne[C_COMMENTAIRE]) ? trim($ligne[C_COMMENTAIRE]) : NULL,
599
 
599
 
600
			"transmission" => $transmission,
600
			"transmission" => $transmission,
601
			"date_transmission" => $transmission ? date("Y-m-d H:i:s") : NULL, // pas de fonction SQL dans un PDO statement, <=> now()
601
			"date_transmission" => $transmission ? date("Y-m-d H:i:s") : NULL, // pas de fonction SQL dans un PDO statement, <=> now()
602
 
602
 
603
			// $ligne: uniquement pour les infos en cas de gestion d'erreurs (lon/lat incompréhensible)
603
			// $ligne: uniquement pour les infos en cas de gestion d'erreurs (lon/lat incompréhensible)
604
			"latitude" => isset($ligne[C_LATITUDE]) ? self::traiterLonLat(NULL, $ligne[C_LATITUDE], $referentiel, $ref_ligne) : NULL,
604
			"latitude" => isset($ligne[C_LATITUDE]) ? self::traiterLonLat(NULL, $ligne[C_LATITUDE], $referentiel, $ref_ligne) : NULL,
605
			"longitude" => isset($ligne[C_LONGITUDE]) ? self::traiterLonLat($ligne[C_LONGITUDE], NULL, $referentiel, $ref_ligne) : NULL,
605
			"longitude" => isset($ligne[C_LONGITUDE]) ? self::traiterLonLat($ligne[C_LONGITUDE], NULL, $referentiel, $ref_ligne) : NULL,
606
			"altitude" => isset($ligne[C_ALTITUDE]) ? intval($ligne[C_ALTITUDE]) : NULL, // TODO: guess alt from lon/lat
606
			"altitude" => isset($ligne[C_ALTITUDE]) ? intval($ligne[C_ALTITUDE]) : NULL, // TODO: guess alt from lon/lat
607
 
607
 
608
			// @ car potentiellement optionnelles ou toutes vides => pas d'index dans PHPExcel (tableau optimisé)
608
			// @ car potentiellement optionnelles ou toutes vides => pas d'index dans PHPExcel (tableau optimisé)
609
			"abondance" => @$ligne[C_ABONDANCE],
609
			"abondance" => @$ligne[C_ABONDANCE],
610
			"certitude" => @$ligne[C_CERTITUDE],
610
			"certitude" => @$ligne[C_CERTITUDE],
611
			"phenologie" => @$ligne[C_PHENOLOGIE],
611
			"phenologie" => @$ligne[C_PHENOLOGIE],
612
 
612
 
613
			"code_insee_calcule" => substr($localisation[C_CE_ZONE_GEO], -5) // varchar(5)
613
			"code_insee_calcule" => substr($localisation[C_CE_ZONE_GEO], -5) // varchar(5)
614
		);
614
		);
615
 
615
 
616
		// passage de $enregistrement par référence, ainsi ['_images'] n'est défini
616
		// passage de $enregistrement par référence, ainsi ['_images'] n'est défini
617
		// que si des résultats sont trouvés
617
		// que si des résultats sont trouvés
618
		// "@" car PHPExcel supprime les colonnes null sur toute la feuille (ou tout le chunk)
618
		// "@" car PHPExcel supprime les colonnes null sur toute la feuille (ou tout le chunk)
619
		if (@$ligne[C_IMAGES]) {
619
		if (@$ligne[C_IMAGES]) {
620
			self::traiterImage($ligne[C_IMAGES], $cel->id_utilisateur, $enregistrement);
620
			self::traiterImage($ligne[C_IMAGES], $cel->id_utilisateur, $enregistrement);
621
		}
621
		}
622
 
622
 
623
		if (@$ligne[C_MOTS_CLES_TEXTE]) {
623
		if (@$ligne[C_MOTS_CLES_TEXTE]) {
624
			self::traiterMotsCle($ligne[C_MOTS_CLES_TEXTE], $cel->id_utilisateur, $enregistrement);
624
			self::traiterMotsCle($ligne[C_MOTS_CLES_TEXTE], $cel->id_utilisateur, $enregistrement);
625
		}
625
		}
626
 
626
 
627
		$champs_etendus = self::traiterChampsEtendus($ligne, self::$indexes_colonnes_etendues);
627
		$champs_etendus = self::traiterChampsEtendus($ligne, self::$indexes_colonnes_etendues);
628
		if (!empty($champs_etendus)) {
628
		if (!empty($champs_etendus)) {
629
			$enregistrement['_champs_etendus'] = $champs_etendus;
629
			$enregistrement['_champs_etendus'] = $champs_etendus;
630
		}
630
		}
631
 
631
 
632
		return $enregistrement;
632
		return $enregistrement;
633
	}
633
	}
634
 
634
 
635
	static function traiterChampsEtendus(&$ligne, &$indexes_colonnes_etendues) {
635
	static function traiterChampsEtendus(&$ligne, &$indexes_colonnes_etendues) {
636
		$champs_etendus_indexes = array();
636
		$champs_etendus_indexes = array();
637
		foreach($indexes_colonnes_etendues as $index_num => $label) {
637
		foreach($indexes_colonnes_etendues as $index_num => $label) {
638
			if (isset($ligne[$index_num])) {
638
			if (isset($ligne[$index_num])) {
639
				$champs_etendus_indexes[str_replace(self::$prefixe_colonnes_etendues, '', $label)] = $ligne[$index_num];
639
				$champs_etendus_indexes[str_replace(self::$prefixe_colonnes_etendues, '', $label)] = $ligne[$index_num];
640
			}
640
			}
641
		}
641
		}
642
		return $champs_etendus_indexes;
642
		return $champs_etendus_indexes;
643
	}
643
	}
644
 
644
 
645
	static function traiterImage($str, $id_utilisateur, &$enregistrement) {
645
	static function traiterImage($str, $id_utilisateur, &$enregistrement) {
646
		$liste_images = array_filter(explode('/', $str));
646
		$liste_images = array_filter(explode('/', $str));
647
		array_walk($liste_images, array(__CLASS__, '__anonyme_4'));
647
		array_walk($liste_images, array(__CLASS__, '__anonyme_4'));
648
 
648
 
649
		$nomsOrignalConcat = implode(',', $liste_images);
649
		$nomsOrignalConcat = implode(',', $liste_images);
650
		$requete = 'SELECT id_image, nom_original '.
650
		$requete = 'SELECT id_image, nom_original '.
651
			'FROM cel_images '.
651
			'FROM cel_images '.
652
			"WHERE ce_utilisateur = $id_utilisateur AND nom_original IN ($nomsOrignalConcat) ".
652
			"WHERE ce_utilisateur = $id_utilisateur AND nom_original IN ($nomsOrignalConcat) ".
653
			' -- '.__FILE__.':'.__LINE__;
653
			' -- '.__FILE__.':'.__LINE__;
654
		$resultat = Cel::db()->requeter($requete);
654
		$resultat = Cel::db()->requeter($requete);
655
 
655
 
656
		if ($resultat) {
656
		if ($resultat) {
657
			$enregistrement['_images'] = $resultat;
657
			$enregistrement['_images'] = $resultat;
658
		}
658
		}
659
	}
659
	}
660
 
660
 
661
	static function traiterMotsCle($str, $id_utilisateur, &$enregistrement) {
661
	static function traiterMotsCle($str, $id_utilisateur, &$enregistrement) {
662
		$liste_mots_cle = $liste_mots_cle_recherche = array_map('trim', array_unique(array_filter(explode(',', $str))));
662
		$liste_mots_cle = $liste_mots_cle_recherche = array_map('trim', array_unique(array_filter(explode(',', $str))));
663
		array_walk($liste_mots_cle_recherche, array(__CLASS__, '__anonyme_4'));
663
		array_walk($liste_mots_cle_recherche, array(__CLASS__, '__anonyme_4'));
664
 
664
 
665
		if (self::$gestion_mots_cles == null) {
665
		if (self::$gestion_mots_cles == null) {
666
			$gestion_mots_cles = new GestionMotsCles($this->config, 'obs');
666
			$gestion_mots_cles = new GestionMotsCles($this->config, 'obs');
667
		}
667
		}
668
		$mots_cles_ids = $gestion_mots_cles->obtenirIdsMotClesPourMotsCles($liste_mots_cle, $id_utilisateur);
668
		$mots_cles_ids = $gestion_mots_cles->obtenirIdsMotClesPourMotsCles($liste_mots_cle, $id_utilisateur);
669
		foreach ($mots_cles_ids as $mot_cle) {
669
		foreach ($mots_cles_ids as $mot_cle) {
670
			$resultat[$mot_cle['id_mot_cle']] = $mot_cle['mot_cle'];
670
			$resultat[$mot_cle['id_mot_cle']] = $mot_cle['mot_cle'];
671
		}
671
		}
672
 
672
 
673
		$enregistrement['mots_cles_texte'] = implode(',', $liste_mots_cle);
673
		$enregistrement['mots_cles_texte'] = implode(',', $liste_mots_cle);
674
		$enregistrement['_mots_cle'] = array(
674
		$enregistrement['_mots_cle'] = array(
675
			'existing' => $resultat,
675
			'existing' => $resultat,
676
			'to_insert' => array_diff($liste_mots_cle, $resultat));
676
			'to_insert' => array_diff($liste_mots_cle, $resultat));
677
	}
677
	}
678
 
678
 
679
 
679
 
680
	/* FONCTIONS de TRANSFORMATION de VALEUR DE CELLULE */
680
	/* FONCTIONS de TRANSFORMATION de VALEUR DE CELLULE */
681
	// TODO: PHP 5.3, utiliser date_parse_from_format()
681
	// TODO: PHP 5.3, utiliser date_parse_from_format()
682
	// TODO: parser les heures (cf product-owner)
682
	// TODO: parser les heures (cf product-owner)
683
	// TODO: passer par le timestamp pour s'assurer de la validité
683
	// TODO: passer par le timestamp pour s'assurer de la validité
684
	static function traiterDateObs($date, $ref_ligne) {
684
	static function traiterDateObs($date, $ref_ligne) {
685
		// TODO: see https://github.com/PHPOffice/PHPExcel/issues/208
685
		// TODO: see https://github.com/PHPOffice/PHPExcel/issues/208
686
		// TODO: PHPExcel_Shared_Date::ExcelToPHP()
686
		// TODO: PHPExcel_Shared_Date::ExcelToPHP()
687
		if (is_double($date)) {
687
		if (is_double($date)) {
688
			if ($date > 0) {
688
			if ($date > 0) {
689
				return PHPExcel_Style_NumberFormat::toFormattedString($date, PHPExcel_Style_NumberFormat::FORMAT_DATE_YYYYMMDD2) . " 00:00:00";
689
				return PHPExcel_Style_NumberFormat::toFormattedString($date, PHPExcel_Style_NumberFormat::FORMAT_DATE_YYYYMMDD2) . " 00:00:00";
690
			}
690
			}
691
 
691
 
692
			$msg = "ligne «{$ref_ligne}»: Attention: date antérieure à 1970 et format de cellule «DATE» utilisés ensemble";
692
			$msg = "ligne «{$ref_ligne}»: Attention: date antérieure à 1970 et format de cellule «DATE» utilisés ensemble";
693
			trigger_error($msg, E_USER_NOTICE);
693
			trigger_error($msg, E_USER_NOTICE);
694
		} else {
694
		} else {
695
			// attend l'un des formats de
695
			// attend l'un des formats de
696
			// http://www.php.net/manual/fr/datetime.formats.date.php
696
			// http://www.php.net/manual/fr/datetime.formats.date.php
697
			// le plus simple: YYYY/MM/DD (utilisé à l'export), mais DD-MM-YYYY est aussi supporté
697
			// le plus simple: YYYY/MM/DD (utilisé à l'export), mais DD-MM-YYYY est aussi supporté
698
			$matches = NULL;
698
			$matches = NULL;
699
			// et on essaie d'être sympa et supporter aussi DD/MM/YYYY
699
			// et on essaie d'être sympa et supporter aussi DD/MM/YYYY
700
			if (preg_match(';^([0-3]?\d)/([01]\d)/([12]\d\d\d)$;', $date, $matches)) {
700
			if (preg_match(';^([0-3]?\d)/([01]\d)/([12]\d\d\d)$;', $date, $matches)) {
701
				$date = $matches[3] . '/' . $matches[2] . '/' . $matches[1];
701
				$date = $matches[3] . '/' . $matches[2] . '/' . $matches[1];
702
			}
702
			}
703
			$timestamp = strtotime($date);
703
			$timestamp = strtotime($date);
704
			if (! $timestamp || $timestamp > time() + 3600 * 24 * 1) { // une journée d'avance maxi autorisée (décallage horaire ?)
704
			if (! $timestamp || $timestamp > time() + 3600 * 24 * 1) { // une journée d'avance maxi autorisée (décallage horaire ?)
705
				if ($date) {
705
				if ($date) {
706
					$msg = "ligne «{$ref_ligne}»: Attention: date erronée ($date)";
706
					$msg = "ligne «{$ref_ligne}»: Attention: date erronée ($date)";
707
					trigger_error($msg, E_USER_NOTICE);
707
					trigger_error($msg, E_USER_NOTICE);
708
				}
708
				}
709
				return NULL;
709
				return NULL;
710
			}
710
			}
711
			return strftime('%Y-%m-%d 00:00:00', $timestamp);
711
			return strftime('%Y-%m-%d 00:00:00', $timestamp);
712
		}
712
		}
713
	}
713
	}
714
 
714
 
715
	static function identReferentiel($referentiel, $ligne, $ref_ligne) {
715
	static function identReferentiel($referentiel, $ligne, $ref_ligne) {
716
		// SELECT DISTINCT nom_referentiel, COUNT(id_observation) AS count FROM cel_obs GROUP BY nom_referentiel ORDER BY count DESC;
716
		// SELECT DISTINCT nom_referentiel, COUNT(id_observation) AS count FROM cel_obs GROUP BY nom_referentiel ORDER BY count DESC;
717
		if (strpos($referentiel, 'bdtfx') !== FALSE) {
717
		if (strpos($referentiel, 'bdtfx') !== FALSE) {
718
			return 'bdtfx'; //:v1.01';
718
			return 'bdtfx'; //:v1.01';
719
		}
719
		}
720
		if (strpos($referentiel, 'bdtxa') !== FALSE) {
720
		if (strpos($referentiel, 'bdtxa') !== FALSE) {
721
			return 'bdtxa'; //:v1.00';
721
			return 'bdtxa'; //:v1.00';
722
		}
722
		}
723
		if (strpos($referentiel, 'bdnff') !== FALSE) {
723
		if (strpos($referentiel, 'bdnff') !== FALSE) {
724
			return 'bdtfx';
724
			return 'bdtfx';
725
		}
725
		}
726
		if (strpos($referentiel, 'isfan') !== FALSE) {
726
		if (strpos($referentiel, 'isfan') !== FALSE) {
727
			return 'isfan'; //:v1.00';
727
			return 'isfan'; //:v1.00';
728
		}
728
		}
729
		if (strpos($referentiel, 'apd') !== FALSE) {
729
		if (strpos($referentiel, 'apd') !== FALSE) {
730
			return 'apd'; //:v1.00';
730
			return 'apd'; //:v1.00';
731
		}
731
		}
732
		if (strpos($referentiel, 'autre') !== FALSE) {
732
		if (strpos($referentiel, 'autre') !== FALSE) {
733
			return 'autre';
733
			return 'autre';
734
		}
734
		}
735
 
735
 
736
		if ($referentiel && isset($ligne[C_NOM_SEL]) && $ligne[C_NOM_SEL]) {
736
		if ($referentiel && isset($ligne[C_NOM_SEL]) && $ligne[C_NOM_SEL]) {
737
			$msg = "ligne «{$ref_ligne}»: Attention: référentiel «{$referentiel}» inconnu";
737
			$msg = "ligne «{$ref_ligne}»: Attention: référentiel «{$referentiel}» inconnu";
738
			trigger_error($msg, E_USER_NOTICE);
738
			trigger_error($msg, E_USER_NOTICE);
739
			return 'autre';
739
			return 'autre';
740
		}
740
		}
741
		return NULL;
741
		return NULL;
742
	}
742
	}
743
 
743
 
744
	static function traiterLonLat($lon = NULL, $lat = NULL, $referentiel = 'bdtfx', $ref_ligne) {
744
	static function traiterLonLat($lon = NULL, $lat = NULL, $referentiel = 'bdtfx', $ref_ligne) {
745
		// en CSV ces valeurs sont des string, avec séparateur en français (","; cf défauts dans ExportXLS)
745
		// en CSV ces valeurs sont des string, avec séparateur en français (","; cf défauts dans ExportXLS)
746
		if ($lon && is_string($lon)) {
746
		if ($lon && is_string($lon)) {
747
			$lon = str_replace(',', '.', $lon);
747
			$lon = str_replace(',', '.', $lon);
748
		}
748
		}
749
		if ($lat && is_string($lat)) {
749
		if ($lat && is_string($lat)) {
750
			$lat = str_replace(',', '.', $lat);
750
			$lat = str_replace(',', '.', $lat);
751
		}
751
		}
752
 
752
 
753
		// sprintf applique une précision à 5 décimale (comme le ferait MySQL)
753
		// sprintf applique une précision à 5 décimale (comme le ferait MySQL)
754
		// tout en uniformisant le format de séparateur des décimales (le ".")
754
		// tout en uniformisant le format de séparateur des décimales (le ".")
755
		if ($lon && is_numeric($lon) && $lon >= -180 && $lon <= 180) {
755
		if ($lon && is_numeric($lon) && $lon >= -180 && $lon <= 180) {
756
			return sprintf('%.5F', $lon);
756
			return sprintf('%.5F', $lon);
757
		}
757
		}
758
		if ($lat && is_numeric($lat) && $lat >= -90 && $lat <= 90) {
758
		if ($lat && is_numeric($lat) && $lat >= -90 && $lat <= 90) {
759
			return sprintf('%.5F', $lat);
759
			return sprintf('%.5F', $lat);
760
		}
760
		}
761
 
761
 
762
		if ($lon || $lat) {
762
		if ($lon || $lat) {
763
			trigger_error("ligne \"{$ref_ligne}\": " .
763
			trigger_error("ligne \"{$ref_ligne}\": " .
764
				  "Attention: longitude ou latitude erronée",
764
				  "Attention: longitude ou latitude erronée",
765
				  E_USER_NOTICE);
765
				  E_USER_NOTICE);
766
		}
766
		}
767
		return NULL;
767
		return NULL;
768
	}
768
	}
769
 
769
 
770
	/*
770
	/*
771
	  TODO: s'affranchir du webservice pour la détermination du nom scientifique en s'appuyant sur cel_references,
771
	  TODO: s'affranchir du webservice pour la détermination du nom scientifique en s'appuyant sur cel_references,
772
	  pour des questions de performances
772
	  pour des questions de performances
773
	*/
773
	*/
774
	static function traiterEspece($ligne, Array &$espece, &$referentiel, $taxon_info_webservice) {
774
	static function traiterEspece($ligne, Array &$espece, &$referentiel, $taxon_info_webservice) {
775
		if (empty($ligne[C_NOM_SEL])) {
775
		if (empty($ligne[C_NOM_SEL])) {
776
			return;
776
			return;
777
		}
777
		}
778
 
778
 
779
		// nom_sel reste toujours celui de l'utilisateur
779
		// nom_sel reste toujours celui de l'utilisateur
780
		$espece[C_NOM_SEL] = trim($ligne[C_NOM_SEL]);
780
		$espece[C_NOM_SEL] = trim($ligne[C_NOM_SEL]);
781
 
781
 
782
		// XXX/attention, nous ne devrions pas accepter un référentiel absent !
782
		// XXX/attention, nous ne devrions pas accepter un référentiel absent !
783
		if (!$referentiel) {
783
		if (!$referentiel) {
784
			$referentiel = 'bdtfx';
784
			$referentiel = 'bdtfx';
785
		}
785
		}
786
		$taxon_info_webservice->setReferentiel($referentiel);
786
		$taxon_info_webservice->setReferentiel($referentiel);
787
		$ascii = iconv('UTF-8', 'ASCII//TRANSLIT', $ligne[C_NOM_SEL]);
787
		$ascii = iconv('UTF-8', 'ASCII//TRANSLIT', $ligne[C_NOM_SEL]);
788
 
788
 
789
		$determ = $taxon_info_webservice->rechercherInfosSurTexteCodeOuNumTax(trim($ligne[C_NOM_SEL]));
789
		$determ = $taxon_info_webservice->rechercherInfosSurTexteCodeOuNumTax(trim($ligne[C_NOM_SEL]));
790
 
790
 
791
		// note: rechercherInfosSurTexteCodeOuNumTax peut ne retourner qu'une seule clef "nom_sel"
791
		// note: rechercherInfosSurTexteCodeOuNumTax peut ne retourner qu'une seule clef "nom_sel"
792
		if (! $determ) {
792
		if (! $determ) {
793
			// on supprime les noms retenus et renvoi tel quel
793
			// on supprime les noms retenus et renvoi tel quel
794
			// on réutilise les define pour les noms d'indexes, tant qu'à faire
794
			// on réutilise les define pour les noms d'indexes, tant qu'à faire
795
			// XXX; tout à NULL sauf C_NOM_SEL ci-dessus ?
795
			// XXX; tout à NULL sauf C_NOM_SEL ci-dessus ?
796
			$espece[C_NOM_SEL_NN] = @$ligne[C_NOM_SEL_NN];
796
			$espece[C_NOM_SEL_NN] = @$ligne[C_NOM_SEL_NN];
797
			$espece[C_NOM_RET] = @$ligne[C_NOM_RET];
797
			$espece[C_NOM_RET] = @$ligne[C_NOM_RET];
798
			$espece[C_NOM_RET_NN] = @$ligne[C_NOM_RET_NN];
798
			$espece[C_NOM_RET_NN] = @$ligne[C_NOM_RET_NN];
799
			$espece[C_NT] = @$ligne[C_NT];
799
			$espece[C_NT] = @$ligne[C_NT];
800
			$espece[C_FAMILLE] = @$ligne[C_FAMILLE];
800
			$espece[C_FAMILLE] = @$ligne[C_FAMILLE];
801
 
801
 
802
			return;
802
			return;
803
		}
803
		}
804
 
804
 
805
		// succès de la détection, mais résultat partiel
805
		// succès de la détection, mais résultat partiel
806
		if (!isset($determ->id)) {
806
		if (!isset($determ->id)) {
807
			$determ = $taxon_info_webservice->effectuerRequeteInfosComplementairesSurNumNom($determ->{"nom_retenu.id"});
807
			$determ = $taxon_info_webservice->effectuerRequeteInfosComplementairesSurNumNom($determ->{"nom_retenu.id"});
808
		}
808
		}
809
 
809
 
810
		// ne devrait jamais arriver !
810
		// ne devrait jamais arriver !
811
		if (!$determ) {
811
		if (!$determ) {
812
			die("erreur critique: " . __FILE__ . ':' . __LINE__);
812
			die("erreur critique: " . __FILE__ . ':' . __LINE__);
813
		}
813
		}
814
 
814
 
815
		// un schéma <ref>:(nt|nn):<num> (ie: bdtfx:nt:8503) a été passé
815
		// un schéma <ref>:(nt|nn):<num> (ie: bdtfx:nt:8503) a été passé
816
		// dans ce cas on met à jour le référentiel avec celui passé dans le champ espèce
816
		// dans ce cas on met à jour le référentiel avec celui passé dans le champ espèce
817
		if (isset($determ->ref)) {
817
		if (isset($determ->ref)) {
818
			$referentiel = $determ->ref;
818
			$referentiel = $determ->ref;
819
		}
819
		}
820
 
820
 
821
		// succès de la détection
821
		// succès de la détection
822
		// nom_sel est remplacé, mais seulement si un motif spécial à été utilisé (bdtfx:nn:4567)
822
		// nom_sel est remplacé, mais seulement si un motif spécial à été utilisé (bdtfx:nn:4567)
823
		if ($taxon_info_webservice->is_notation_spe) {
823
		if ($taxon_info_webservice->is_notation_spe) {
824
			$espece[C_NOM_SEL] = $determ->nom_sci;
824
			$espece[C_NOM_SEL] = $determ->nom_sci;
825
		}
825
		}
826
 
826
 
827
		// écrasement des numéros (nomenclatural, taxonomique) saisis...
827
		// écrasement des numéros (nomenclatural, taxonomique) saisis...
828
		$espece[C_NOM_SEL_NN] = $determ->id;
828
		$espece[C_NOM_SEL_NN] = $determ->id;
829
		$espece[C_NOM_RET] = RechercheInfosTaxonBeta::supprimerBiblio($determ->nom_retenu_complet);
829
		$espece[C_NOM_RET] = RechercheInfosTaxonBeta::supprimerBiblio($determ->nom_retenu_complet);
830
		$espece[C_NOM_RET_NN] = $determ->{"nom_retenu.id"};
830
		$espece[C_NOM_RET_NN] = $determ->{"nom_retenu.id"};
831
		$espece[C_NT] = $determ->num_taxonomique;
831
		$espece[C_NT] = $determ->num_taxonomique;
832
		$espece[C_FAMILLE] = $determ->famille;
832
		$espece[C_FAMILLE] = $determ->famille;
833
		return;
833
		return;
834
	}
834
	}
835
 
835
 
836
	static function detectFromNom($nom) {
836
	static function detectFromNom($nom) {
837
		$r = Cel::db()->requeter(sprintf("SELECT num_nom, num_tax_sup FROM bdtfx_v1_01 WHERE (nom_sci LIKE '%s') ".
837
		$r = Cel::db()->requeter(sprintf("SELECT num_nom, num_tax_sup FROM bdtfx_v1_01 WHERE (nom_sci LIKE '%s') ".
838
			"ORDER BY nom_sci ASC LIMIT 0, 1",
838
			"ORDER BY nom_sci ASC LIMIT 0, 1",
839
			Cel::db()->proteger($nom)));
839
			Cel::db()->proteger($nom)));
840
		if ($r) {
840
		if ($r) {
841
			return $r;
841
			return $r;
842
		}
842
		}
843
 
843
 
844
		Cel::db()->requeter(sprintf("SELECT num_nom, num_tax_sup FROM bdtfx_v1_01 WHERE (nom_sci LIKE '%s' OR nom LIKE '%s') ".
844
		Cel::db()->requeter(sprintf("SELECT num_nom, num_tax_sup FROM bdtfx_v1_01 WHERE (nom_sci LIKE '%s' OR nom LIKE '%s') ".
845
			"ORDER BY nom_sci ASC LIMIT 0, 1",
845
			"ORDER BY nom_sci ASC LIMIT 0, 1",
846
			Cel::db()->proteger($nom),
846
			Cel::db()->proteger($nom),
847
			Cel::db()->proteger(str_replace(' ', '% ', $nom))));
847
			Cel::db()->proteger(str_replace(' ', '% ', $nom))));
848
		return $r;
848
		return $r;
849
	}
849
	}
850
 
850
 
851
	static function traiterLocalisation($ligne, Array &$localisation) {
851
	static function traiterLocalisation($ligne, Array &$localisation) {
852
		if (empty($ligne[C_ZONE_GEO])) {
852
		if (empty($ligne[C_ZONE_GEO])) {
853
			$ligne[C_ZONE_GEO] = NULL;
853
			$ligne[C_ZONE_GEO] = NULL;
854
		}
854
		}
855
		if (empty($ligne[C_CE_ZONE_GEO])) {
855
		if (empty($ligne[C_CE_ZONE_GEO])) {
856
			$ligne[C_CE_ZONE_GEO] = NULL;
856
			$ligne[C_CE_ZONE_GEO] = NULL;
857
		}
857
		}
858
 
858
 
859
		$identifiant_commune = trim($ligne[C_ZONE_GEO]);
859
		$identifiant_commune = trim($ligne[C_ZONE_GEO]);
860
		if (!$identifiant_commune) {
860
		if (!$identifiant_commune) {
861
			$departement = trim($ligne[C_CE_ZONE_GEO]);
861
			$departement = trim($ligne[C_CE_ZONE_GEO]);
862
 
862
 
863
			if (strpos($departement, 'INSEE-C:', 0) === 0) {
863
			if (strpos($departement, 'INSEE-C:', 0) === 0) {
864
				$localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
864
				$localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
865
				if (array_key_exists($localisation[C_CE_ZONE_GEO], self::$cache['geo'])) {
865
				if (array_key_exists($localisation[C_CE_ZONE_GEO], self::$cache['geo'])) {
866
					$localisation[C_ZONE_GEO] = self::$cache['geo'][$localisation[C_CE_ZONE_GEO]];
866
					$localisation[C_ZONE_GEO] = self::$cache['geo'][$localisation[C_CE_ZONE_GEO]];
867
				} else {
867
				} else {
868
					$nom = Cel::db()->requeter(sprintf("SELECT nom FROM cel_zones_geo WHERE code = %s LIMIT 1",
868
					$nom = Cel::db()->requeter(sprintf("SELECT nom FROM cel_zones_geo WHERE code = %s LIMIT 1",
869
						self::quoteNonNull(substr($localisation[C_CE_ZONE_GEO], strlen("INSEE-C:")))));
869
						self::quoteNonNull(substr($localisation[C_CE_ZONE_GEO], strlen("INSEE-C:")))));
870
					if ($nom) {
870
					if ($nom) {
871
						$localisation[C_ZONE_GEO] = $nom[0]['nom'];
871
						$localisation[C_ZONE_GEO] = $nom[0]['nom'];
872
					}
872
					}
873
					self::$cache['geo'][$localisation[C_CE_ZONE_GEO]] = @$nom[0]['nom'];
873
					self::$cache['geo'][$localisation[C_CE_ZONE_GEO]] = @$nom[0]['nom'];
874
				}
874
				}
875
				return;
875
				return;
876
			}
876
			}
877
 
877
 
878
			if (!is_numeric($departement)) {
878
			if (!is_numeric($departement)) {
879
				$localisation[C_CE_ZONE_GEO] = $ligne[C_CE_ZONE_GEO];
879
				$localisation[C_CE_ZONE_GEO] = $ligne[C_CE_ZONE_GEO];
880
				return;
880
				return;
881
			}
881
			}
882
 
882
 
883
			$cache_attempted = FALSE;
883
			$cache_attempted = FALSE;
884
			if(array_key_exists($departement, self::$cache['geo'])) {
884
			if(array_key_exists($departement, self::$cache['geo'])) {
885
				$cache_attempted = TRUE;
885
				$cache_attempted = TRUE;
886
				if (self::$cache['geo'][$departement][0] && self::$cache['geo'][$departement][1]) {
886
				if (self::$cache['geo'][$departement][0] && self::$cache['geo'][$departement][1]) {
887
					$localisation[C_ZONE_GEO] = self::$cache['geo'][$departement][0];
887
					$localisation[C_ZONE_GEO] = self::$cache['geo'][$departement][0];
888
					$localisation[C_CE_ZONE_GEO] = self::$cache['geo'][$departement][1];
888
					$localisation[C_CE_ZONE_GEO] = self::$cache['geo'][$departement][1];
889
					return;
889
					return;
890
				}
890
				}
891
			}
891
			}
892
 
892
 
893
			$requete = "SELECT DISTINCT nom, CONCAT('INSEE-C:', code) AS code ".
893
			$requete = "SELECT DISTINCT nom, CONCAT('INSEE-C:', code) AS code ".
894
				'FROM cel_zones_geo '.
894
				'FROM cel_zones_geo '.
895
				'WHERE code = %s '.
895
				'WHERE code = %s '.
896
				'LIMIT 1 '.
896
				'LIMIT 1 '.
897
				' -- '.__FILE__.':'.__LINE__;
897
				' -- '.__FILE__.':'.__LINE__;
898
			$resultat_commune = Cel::db()->requeter(sprintf($requete, self::quoteNonNull($departement)));
898
			$resultat_commune = Cel::db()->requeter(sprintf($requete, self::quoteNonNull($departement)));
899
			if (! $cache_attempted && $resultat_commune) {
899
			if (! $cache_attempted && $resultat_commune) {
900
				$localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
900
				$localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
901
				$localisation[C_CE_ZONE_GEO] = $resultat_commune[0]['code'];
901
				$localisation[C_CE_ZONE_GEO] = $resultat_commune[0]['code'];
902
				self::$cache['geo'][$departement] = array($resultat_commune[0]['nom'], $resultat_commune[0]['code']);
902
				self::$cache['geo'][$departement] = array($resultat_commune[0]['nom'], $resultat_commune[0]['code']);
903
				return;
903
				return;
904
			}
904
			}
905
			$localisation[C_CE_ZONE_GEO] = $ligne[C_CE_ZONE_GEO];
905
			$localisation[C_CE_ZONE_GEO] = $ligne[C_CE_ZONE_GEO];
906
			return;
906
			return;
907
		}
907
		}
908
 
908
 
909
		$select = "SELECT DISTINCT nom, code FROM cel_zones_geo";
909
		$select = "SELECT DISTINCT nom, code FROM cel_zones_geo";
910
 
910
 
911
		if (preg_match('/(.+) \((\d{1,5})\)/', $identifiant_commune, $elements)) {
911
		if (preg_match('/(.+) \((\d{1,5})\)/', $identifiant_commune, $elements)) {
912
			// commune + departement : montpellier (34)
912
			// commune + departement : montpellier (34)
913
			$nom_commune=$elements[1];
913
			$nom_commune=$elements[1];
914
			$code_commune=$elements[2];
914
			$code_commune=$elements[2];
915
			if (strlen($code_commune) <= 2) {
915
			if (strlen($code_commune) <= 2) {
916
				$requete = sprintf("%s WHERE nom = %s AND code LIKE %s",
916
				$requete = sprintf("%s WHERE nom = %s AND code LIKE %s",
917
					$select, self::quoteNonNull($nom_commune),
917
					$select, self::quoteNonNull($nom_commune),
918
					self::quoteNonNull($code_commune.'%'));
918
					self::quoteNonNull($code_commune.'%'));
919
			} else {
919
			} else {
920
				$requete = sprintf("%s WHERE nom = %s AND code = %d",
920
				$requete = sprintf("%s WHERE nom = %s AND code = %d",
921
					$select, self::quoteNonNull($nom_commune),
921
					$select, self::quoteNonNull($nom_commune),
922
					$code_commune);
922
					$code_commune);
923
			}
923
			}
924
		} elseif (preg_match('/^(\d+|(2[ab]\d+))$/i', $identifiant_commune, $elements)) {
924
		} elseif (preg_match('/^(\d+|(2[ab]\d+))$/i', $identifiant_commune, $elements)) {
925
			// Code insee seul
925
			// Code insee seul
926
			$code_insee_commune=$elements[1];
926
			$code_insee_commune=$elements[1];
927
			$requete = sprintf("%s WHERE code = %s", $select, self::quoteNonNull($code_insee_commune));
927
			$requete = sprintf("%s WHERE code = %s", $select, self::quoteNonNull($code_insee_commune));
928
		} else {
928
		} else {
929
			// Commune seule (le departement sera recupere dans la colonne departement si elle est presente)
929
			// Commune seule (le departement sera recupere dans la colonne departement si elle est presente)
930
			// on prend le risque ici de retourner une mauvaise Commune
930
			// on prend le risque ici de retourner une mauvaise Commune
931
			$nom_commune = str_replace(" ", "%", iconv('UTF-8', 'ASCII//TRANSLIT', $identifiant_commune));
931
			$nom_commune = str_replace(" ", "%", iconv('UTF-8', 'ASCII//TRANSLIT', $identifiant_commune));
932
			$requete = sprintf("%s WHERE nom LIKE %s", $select, self::quoteNonNull($nom_commune.'%'));
932
			$requete = sprintf("%s WHERE nom LIKE %s", $select, self::quoteNonNull($nom_commune.'%'));
933
		}
933
		}
934
 
934
 
935
		if (array_key_exists($identifiant_commune, self::$cache['geo'])) {
935
		if (array_key_exists($identifiant_commune, self::$cache['geo'])) {
936
			$resultat_commune = self::$cache['geo'][$identifiant_commune];
936
			$resultat_commune = self::$cache['geo'][$identifiant_commune];
937
		} else {
937
		} else {
938
			$resultat_commune = Cel::db()->requeter($requete);
938
			$resultat_commune = Cel::db()->requeter($requete);
939
			self::$cache['geo'][$identifiant_commune] = $resultat_commune;
939
			self::$cache['geo'][$identifiant_commune] = $resultat_commune;
940
		}
940
		}
941
 
941
 
942
		// cas de la commune introuvable dans le référentiel
942
		// cas de la commune introuvable dans le référentiel
943
		// réinitialisation aux valeurs du fichier XLS
943
		// réinitialisation aux valeurs du fichier XLS
944
		if (! $resultat_commune) {
944
		if (! $resultat_commune) {
945
			$localisation[C_ZONE_GEO] = trim($ligne[C_ZONE_GEO]);
945
			$localisation[C_ZONE_GEO] = trim($ligne[C_ZONE_GEO]);
946
			$localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
946
			$localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
947
		} else {
947
		} else {
948
			$localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
948
			$localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
949
			$localisation[C_CE_ZONE_GEO] = "INSEE-C:" . $resultat_commune[0]['code'];
949
			$localisation[C_CE_ZONE_GEO] = "INSEE-C:" . $resultat_commune[0]['code'];
950
			return;
950
			return;
951
		}
951
		}
952
 
952
 
953
		$departement =& $localisation[C_CE_ZONE_GEO];
953
		$departement =& $localisation[C_CE_ZONE_GEO];
954
 
954
 
955
		if (strpos($departement, "INSEE-C:", 0) === 0) {
955
		if (strpos($departement, "INSEE-C:", 0) === 0) {
956
			$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
956
			$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
957
			$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
957
			$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
958
		}
958
		}
959
 
959
 
960
		if (!is_numeric($departement)) {
960
		if (!is_numeric($departement)) {
961
			$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
961
			$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
962
			$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
962
			$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
963
		}
963
		}
964
 
964
 
965
		if (strlen($departement) == 4) {
965
		if (strlen($departement) == 4) {
966
			$departement = "INSEE-C:0$departement";
966
			$departement = "INSEE-C:0$departement";
967
		}
967
		}
968
		if (strlen($departement) == 5) {
968
		if (strlen($departement) == 5) {
969
			$departement = "INSEE-C:$departement";
969
			$departement = "INSEE-C:$departement";
970
		}
970
		}
971
		$departement = trim($departement);
971
		$departement = trim($departement);
972
 
972
 
973
		$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
973
		$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
974
		$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
974
		$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
975
	}
975
	}
976
 
976
 
977
	public static function stockerChampsEtendus($champs_etendus, $ordre_ids, $config) {
977
	public static function stockerChampsEtendus($champs_etendus, $ordre_ids, $config) {
978
		// singleton du pauvre mais l'export est suffisamment inefficace pour s'en priver
978
		// singleton du pauvre mais l'export est suffisamment inefficace pour s'en priver
979
		self::$gestion_champs_etendus = self::$gestion_champs_etendus == null ?
979
		self::$gestion_champs_etendus = self::$gestion_champs_etendus == null ?
980
			new GestionChampsEtendus($config, 'obs') :
980
			new GestionChampsEtendus($config, 'obs') :
981
			self::$gestion_champs_etendus;
981
			self::$gestion_champs_etendus;
982
 
982
 
983
		$champs_etendus_obs = array();
983
		$champs_etendus_obs = array();
984
		foreach ($champs_etendus as $champ_etendu_a_obs) {
984
		foreach ($champs_etendus as $champ_etendu_a_obs) {
985
			$id_obs = $ordre_ids[$champ_etendu_a_obs['ordre']]; // id réel de l'observation correspondant à l'ordre
985
			$id_obs = $ordre_ids[$champ_etendu_a_obs['ordre']]; // id réel de l'observation correspondant à l'ordre
986
			foreach ($champ_etendu_a_obs['champs_etendus'] as $label => $champ) {
986
			foreach ($champ_etendu_a_obs['champs_etendus'] as $label => $champ) {
987
				// XXX: insère t'on des valeurs vides ?
987
				// XXX: insère t'on des valeurs vides ?
988
				$valeur = $champ;
988
				$valeur = $champ;
989
				$cle = $label;
989
				$cle = $label;
990
 
990
 
991
				if (!empty($cle) && !empty($valeur)) {
991
				if (!empty($cle) && !empty($valeur)) {
992
					$champ_etendu_a_inserer = new ChampEtendu();
992
					$champ_etendu_a_inserer = new ChampEtendu();
993
					$champ_etendu_a_inserer->id = $id_obs;
993
					$champ_etendu_a_inserer->id = $id_obs;
994
					$champ_etendu_a_inserer->cle = $cle;
994
					$champ_etendu_a_inserer->cle = $cle;
995
					$champ_etendu_a_inserer->valeur = $valeur;
995
					$champ_etendu_a_inserer->valeur = $valeur;
996
 
996
 
997
					$champs_etendus_obs[] = $champ_etendu_a_inserer;
997
					$champs_etendus_obs[] = $champ_etendu_a_inserer;
998
				}
998
				}
999
			}
999
			}
1000
		}
1000
		}
1001
 
1001
 
1002
		self::$gestion_champs_etendus->ajouterParLots($champs_etendus_obs);
1002
		self::$gestion_champs_etendus->ajouterParLots($champs_etendus_obs);
1003
		//TODO: que faire si l'insertion des champs étendus échoue ?
1003
		//TODO: que faire si l'insertion des champs étendus échoue ?
1004
		return count($champs_etendus_obs);
1004
		return count($champs_etendus_obs);
1005
	}
1005
	}
1006
 
1006
 
1007
	/* HELPERS */
1007
	/* HELPERS */
1008
 
1008
 
1009
	// http://stackoverflow.com/questions/348410/sort-an-array-based-on-another-array
1009
	// http://stackoverflow.com/questions/348410/sort-an-array-based-on-another-array
1010
	// XXX; utilisé aussi (temporairement ?) par FormateurGroupeColonne.
1010
	// XXX; utilisé aussi (temporairement ?) par FormateurGroupeColonne.
1011
	static function sortArrayByArray($array, $orderArray) {
1011
	static function sortArrayByArray($array, $orderArray) {
1012
		$ordered = array();
1012
		$ordered = array();
1013
		foreach($orderArray as $key) {
1013
		foreach($orderArray as $key) {
1014
			if (array_key_exists($key, $array)) {
1014
			if (array_key_exists($key, $array)) {
1015
				$ordered[$key] = $array[$key];
1015
				$ordered[$key] = $array[$key];
1016
				unset($array[$key]);
1016
				unset($array[$key]);
1017
			}
1017
			}
1018
		}
1018
		}
1019
		return $ordered + $array;
1019
		return $ordered + $array;
1020
	}
1020
	}
1021
 
1021
 
1022
	// retourne une BBox [N,S,E,O) pour un référentiel donné
1022
	// retourne une BBox [N,S,E,O) pour un référentiel donné
1023
	static function getReferentielBBox($referentiel) {
1023
	static function getReferentielBBox($referentiel) {
1024
		if ($referentiel == 'bdtfx') {
1024
		if ($referentiel == 'bdtfx') {
1025
			return Array(
1025
			return Array(
1026
				'NORD' => 51.2, // Dunkerque
1026
				'NORD' => 51.2, // Dunkerque
1027
				'SUD' => 41.3, // Bonifacio
1027
				'SUD' => 41.3, // Bonifacio
1028
				'EST' => 9.7, // Corse
1028
				'EST' => 9.7, // Corse
1029
				'OUEST' => -5.2); // Ouessan
1029
				'OUEST' => -5.2); // Ouessan
1030
		}
1030
		}
1031
		return FALSE;
1031
		return FALSE;
1032
	}
1032
	}
1033
 
1033
 
1034
	// ces valeurs ne sont pas inséré via les placeholders du PDO::preparedStatement
1034
	// ces valeurs ne sont pas inséré via les placeholders du PDO::preparedStatement
1035
	// et doivent donc être échappées correctement.
1035
	// et doivent donc être échappées correctement.
1036
	public function initialiser_colonnes_statiques() {
1036
	public function initialiser_colonnes_statiques() {
1037
		$this->colonnes_statiques = array_merge($this->colonnes_statiques,
1037
		$this->colonnes_statiques = array_merge($this->colonnes_statiques,
1038
			array(
1038
			array(
1039
				'ce_utilisateur' => self::quoteNonNull($this->id_utilisateur), // peut-être un hash ou un id
1039
				'ce_utilisateur' => self::quoteNonNull($this->id_utilisateur), // peut-être un hash ou un id
1040
				'prenom_utilisateur' => self::quoteNonNull($this->utilisateur['prenom']),
1040
				'prenom_utilisateur' => self::quoteNonNull($this->utilisateur['prenom']),
1041
				'nom_utilisateur' => self::quoteNonNull($this->utilisateur['nom']),
1041
				'nom_utilisateur' => self::quoteNonNull($this->utilisateur['nom']),
1042
				'courriel_utilisateur' => self::quoteNonNull($this->utilisateur['courriel']),
1042
				'courriel_utilisateur' => self::quoteNonNull($this->utilisateur['courriel']),
1043
			));
1043
			));
1044
	}
1044
	}
1045
 
1045
 
1046
	static function initialiser_pdo_ordered_statements($colonnes_statiques) {
1046
	static function initialiser_pdo_ordered_statements($colonnes_statiques) {
1047
		return Array(
1047
		return Array(
1048
			// insert_ligne_pattern_ordre
1048
			// insert_ligne_pattern_ordre
1049
			sprintf('INSERT INTO cel_obs (%s, %s) VALUES',
1049
			sprintf('INSERT INTO cel_obs (%s, %s) VALUES',
1050
				implode(', ', array_keys($colonnes_statiques)),
1050
				implode(', ', array_keys($colonnes_statiques)),
1051
				implode(', ', array_diff(self::$ordre_BDD, array_keys($colonnes_statiques)))),
1051
				implode(', ', array_diff(self::$ordre_BDD, array_keys($colonnes_statiques)))),
1052
 
1052
 
1053
			// insert_ligne_pattern_ordre
1053
			// insert_ligne_pattern_ordre
1054
			sprintf('(%s, %s ?)',
1054
			sprintf('(%s, %s ?)',
1055
				implode(', ', $colonnes_statiques),
1055
				implode(', ', $colonnes_statiques),
1056
				str_repeat('?, ', count(self::$ordre_BDD) - count($colonnes_statiques) - 1))
1056
				str_repeat('?, ', count(self::$ordre_BDD) - count($colonnes_statiques) - 1))
1057
		);
1057
		);
1058
	}
1058
	}
1059
 
1059
 
1060
	static function initialiser_pdo_statements($colonnes_statiques) {
1060
	static function initialiser_pdo_statements($colonnes_statiques) {
1061
		return Array(
1061
		return Array(
1062
			// insert_prefix
1062
			// insert_prefix
1063
			sprintf('INSERT INTO cel_obs (%s) VALUES ',
1063
			sprintf('INSERT INTO cel_obs (%s) VALUES ',
1064
				implode(', ', self::$ordre_BDD)),
1064
				implode(', ', self::$ordre_BDD)),
1065
 
1065
 
1066
 
1066
 
1067
			// insert_ligne_pattern, cf: self::$insert_ligne_pattern
1067
			// insert_ligne_pattern, cf: self::$insert_ligne_pattern
1068
			'(' .
1068
			'(' .
1069
			// 3) créé une chaîne de liste de champ à inséré en DB
1069
			// 3) créé une chaîne de liste de champ à inséré en DB
1070
			implode(', ', array_values(
1070
			implode(', ', array_values(
1071
			// 2) garde les valeurs fixes (de $colonnes_statiques),
1071
			// 2) garde les valeurs fixes (de $colonnes_statiques),
1072
			// mais remplace les NULL par des "?"
1072
			// mais remplace les NULL par des "?"
1073
			array_map('__anonyme_5',
1073
			array_map('__anonyme_5',
1074
				  // 1) créé un tableau genre (nom_sel_nn => NULL) depuis self::$ordre_BDD
1074
				  // 1) créé un tableau genre (nom_sel_nn => NULL) depuis self::$ordre_BDD
1075
				  // et écrase certaines valeurs avec $colonnes_statiques (initilisé avec les données utilisateur)
1075
				  // et écrase certaines valeurs avec $colonnes_statiques (initilisé avec les données utilisateur)
1076
				  array_merge(array_map('__anonyme_6', array_flip(self::$ordre_BDD)), $colonnes_statiques
1076
				  array_merge(array_map('__anonyme_6', array_flip(self::$ordre_BDD)), $colonnes_statiques
1077
				  )))) .
1077
				  )))) .
1078
			')'
1078
			')'
1079
		);
1079
		);
1080
	}
1080
	}
1081
 
1081
 
1082
	// équivalent à Bdd2->proteger() (qui wrap PDO::quote),
1082
	// équivalent à Bdd2->proteger() (qui wrap PDO::quote),
1083
	// sans transformer NULL en ""
1083
	// sans transformer NULL en ""
1084
	static function quoteNonNull($chaine) {
1084
	static function quoteNonNull($chaine) {
1085
		if (is_null($chaine)) {
1085
		if (is_null($chaine)) {
1086
			return 'NULL';
1086
			return 'NULL';
1087
		}
1087
		}
1088
		if (!is_string($chaine) && !is_integer($chaine)) {
1088
		if (!is_string($chaine) && !is_integer($chaine)) {
1089
			die('erreur: ' . __FILE__ . ':' . __LINE__);
1089
			die('erreur: ' . __FILE__ . ':' . __LINE__);
1090
		}
1090
		}
1091
		return Cel::db()->quote($chaine);
1091
		return Cel::db()->quote($chaine);
1092
	}
1092
	}
1093
 
1093
 
1094
	public function erreurs_stock($errno, $errstr) {
1094
	public function erreurs_stock($errno, $errstr) {
1095
		$this->bilan[] = $errstr;
1095
		$this->bilan[] = $errstr;
1096
	}
1096
	}
1097
}
1097
}