Subversion Repositories eFlore/Applications.cel

Rev

Rev 2676 | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

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