Subversion Repositories eFlore/Applications.cel

Rev

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

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