Subversion Repositories eFlore/Applications.cel

Rev

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

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