Subversion Repositories eFlore/Applications.cel

Rev

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

Rev 2660 Rev 2663
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
		if ($this->bilan) {
328
		if ($this->bilan) {
329
			$retour = array('import_erreurs' => implode("\n", $this->bilan) . "\n");
329
			$retour = array('import_erreurs' => '"'.implode("\n", $this->bilan).'"');
330
		} else {
330
		} else {
331
			$retour = array(
331
			$retour = array(
332
						'import_obs_ajoutees' => $obs_ajouts,
332
						'import_obs_ajoutees' => '"'.$obs_ajouts.'"',
333
						'import_images_ajoutees' => $nb_images_ajoutees,
333
						'import_images_ajoutees' => '"'.$nb_images_ajoutees.'"',
334
						'import_mots_cles_ajoutes' => $nb_mots_cle_ajoutes,
334
						'import_mots_cles_ajoutes' => '"'.$nb_mots_cle_ajoutes.'"',
335
						'import_colonnes_non_traitees' => implode(', ', $filtre->exclues)
335
						'import_colonnes_non_traitees' => '"'.implode(', ', $filtre->exclues).'"'
336
					);
336
					);
337
		}
337
		}
338
		// le JSON ci-dessus est envoyé à la main car provenant d'une requête synchrone 
338
		// le JSON ci-dessus est envoyé à la main car provenant d'une requête synchrone 
339
		// certains navigateurs/plugins le modifient à la volée pour l'afficher (genre jsonView) 
339
		// certains navigateurs/plugins le modifient à la volée pour l'afficher (genre jsonView) 
340
		// ce qui fait rater le parsing de la requete
340
		// ce qui fait rater le parsing de la requete
341
		//echo json_encode($retour);
341
		echo json_encode($retour);
342
		$this->envoyerJson($retour);
-
 
343
		die();
342
		die();
344
	}
343
	}
345
 
344
 
346
	/* detectionEntete() sert deux rôles:
345
	/* detectionEntete() sert deux rôles:
347
	 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()
348
	 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
349
	 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)
350
 
349
 
351
	 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
352
	 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:
353
	  - 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)
354
	  - sous forme de clefs numériques (fgetcsv())
353
	  - sous forme de clefs numériques (fgetcsv())
355
	  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
356
	  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. */
357
	static function detectionEntete($entete, $numeric_keys = FALSE) {
356
	static function detectionEntete($entete, $numeric_keys = FALSE) {
358
		$colonnes_reconnues = Array();
357
		$colonnes_reconnues = Array();
359
		$cols = FormateurGroupeColonne::nomEnsembleVersListeColonnes('standard,avance');
358
		$cols = FormateurGroupeColonne::nomEnsembleVersListeColonnes('standard,avance');
360
 
359
 
361
		foreach ($entete as $k => $v) {
360
		foreach ($entete as $k => $v) {
362
			// 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
363
			$entete_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($v)));
362
			$entete_simple = iconv('UTF-8', 'ASCII//TRANSLIT', strtolower(trim($v)));
364
			foreach ($cols as $col) {
363
			foreach ($cols as $col) {
365
				$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'])));
366
				$entete_officiel_abbrev = $col['abbrev'];
365
				$entete_officiel_abbrev = $col['abbrev'];
367
				if ($entete_simple == $entete_officiel_simple || $entete_simple == $entete_officiel_abbrev) {
366
				if ($entete_simple == $entete_officiel_simple || $entete_simple == $entete_officiel_abbrev) {
368
					// debug echo "define C_" . strtoupper($entete_officiel_abbrev) . ", $k ($v)\n";
367
					// debug echo "define C_" . strtoupper($entete_officiel_abbrev) . ", $k ($v)\n";
369
					define("C_" . strtoupper($entete_officiel_abbrev), $k);
368
					define("C_" . strtoupper($entete_officiel_abbrev), $k);
370
					$colonnes_reconnues[$k] = 1;
369
					$colonnes_reconnues[$k] = 1;
371
					break;
370
					break;
372
				}
371
				}
373
 
372
 
374
				if (strpos($v, self::$prefixe_colonnes_etendues) === 0) {
373
				if (strpos($v, self::$prefixe_colonnes_etendues) === 0) {
375
					$colonnes_reconnues[$k] = 1;
374
					$colonnes_reconnues[$k] = 1;
376
					self::$indexes_colonnes_etendues[$k] = $v;
375
					self::$indexes_colonnes_etendues[$k] = $v;
377
					break;
376
					break;
378
				}
377
				}
379
			}
378
			}
380
		}
379
		}
381
 
380
 
382
		// 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
383
		// le tableau renvoyé par PHPExcel
382
		// le tableau renvoyé par PHPExcel
384
		// 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()
385
		// les utilisent
384
		// les utilisent
386
		foreach ($cols as $col) {
385
		foreach ($cols as $col) {
387
			if (!defined('C_'.strtoupper($col['abbrev']))) {
386
			if (!defined('C_'.strtoupper($col['abbrev']))) {
388
				define('C_'.strtoupper($col['abbrev']), 'C_'.strtoupper($col['abbrev']));
387
				define('C_'.strtoupper($col['abbrev']), 'C_'.strtoupper($col['abbrev']));
389
			}
388
			}
390
		}
389
		}
391
 
390
 
392
		// 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
393
		$colonnesID_non_reconnues = array_diff_key($entete, $colonnes_reconnues);
392
		$colonnesID_non_reconnues = array_diff_key($entete, $colonnes_reconnues);
394
 
393
 
395
		// des colonnes de FormateurGroupeColonne::nomEnsembleVersListeColonnes()
394
		// des colonnes de FormateurGroupeColonne::nomEnsembleVersListeColonnes()
396
		// ne retient que celles marquées "importables"
395
		// ne retient que celles marquées "importables"
397
		$colonnes_automatiques = array_filter($cols, '__anonyme_1');
396
		$colonnes_automatiques = array_filter($cols, '__anonyme_1');
398
 
397
 
399
		// 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
400
		array_walk($colonnes_automatiques, '__anonyme_2');
399
		array_walk($colonnes_automatiques, '__anonyme_2');
401
 
400
 
402
		$colonnesID_a_exclure = array_intersect($entete, $colonnes_automatiques);
401
		$colonnesID_a_exclure = array_intersect($entete, $colonnes_automatiques);
403
 
402
 
404
		if ($numeric_keys) {
403
		if ($numeric_keys) {
405
			return $colonnesID_non_reconnues + $colonnesID_a_exclure;
404
			return $colonnesID_non_reconnues + $colonnesID_a_exclure;
406
		}
405
		}
407
		return array_merge($colonnesID_non_reconnues, $colonnesID_a_exclure);
406
		return array_merge($colonnesID_non_reconnues, $colonnesID_a_exclure);
408
	}
407
	}
409
 
408
 
410
	static function chargerCorrespondancesIdOrdre($cel, $lignes) {
409
	static function chargerCorrespondancesIdOrdre($cel, $lignes) {
411
		$ordresObs = array();
410
		$ordresObs = array();
412
		foreach ($lignes as &$ligne) {
411
		foreach ($lignes as &$ligne) {
413
			$ordresObs[] = $ligne['ordre'];
412
			$ordresObs[] = $ligne['ordre'];
414
		}
413
		}
415
		$ordresObsConcat = implode(',', $ordresObs);
414
		$ordresObsConcat = implode(',', $ordresObs);
416
		$idUtilisateurP = Cel::db()->proteger($cel->id_utilisateur);
415
		$idUtilisateurP = Cel::db()->proteger($cel->id_utilisateur);
417
		$requete = 'SELECT id_observation, ordre '.
416
		$requete = 'SELECT id_observation, ordre '.
418
			'FROM cel_obs '.
417
			'FROM cel_obs '.
419
			"WHERE ordre IN ($ordresObsConcat) ".
418
			"WHERE ordre IN ($ordresObsConcat) ".
420
			"AND ce_utilisateur = $idUtilisateurP ".
419
			"AND ce_utilisateur = $idUtilisateurP ".
421
			' -- '.__FILE__.':'.__LINE__;
420
			' -- '.__FILE__.':'.__LINE__;
422
		$resultats = Cel::db()->requeter($requete);
421
		$resultats = Cel::db()->requeter($requete);
423
		$ordresIds = array();
422
		$ordresIds = array();
424
		foreach ($resultats as &$infos) {
423
		foreach ($resultats as &$infos) {
425
			$ordresIds[$infos['ordre']] = $infos['id_observation'];
424
			$ordresIds[$infos['ordre']] = $infos['id_observation'];
426
		}
425
		}
427
		return $ordresIds;
426
		return $ordresIds;
428
	}
427
	}
429
 
428
 
430
	/*
429
	/*
431
	 * charge un groupe de lignes
430
	 * charge un groupe de lignes
432
	 */
431
	 */
433
	static function chargerLignes($cel, $lignes, $colonnes_statiques, &$dernier_ordre) {
432
	static function chargerLignes($cel, $lignes, $colonnes_statiques, &$dernier_ordre) {
434
		$enregistrement = NULL;
433
		$enregistrement = NULL;
435
		$enregistrements = array();
434
		$enregistrements = array();
436
		$toutes_images = array();
435
		$toutes_images = array();
437
		$tous_mots_cle = array();
436
		$tous_mots_cle = array();
438
		$tous_champs_etendus = array();
437
		$tous_champs_etendus = array();
439
 
438
 
440
		foreach ($lignes as $ligne) {
439
		foreach ($lignes as $ligne) {
441
			// 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)
442
			if ($ligne === false) {
441
			if ($ligne === false) {
443
				continue;
442
				continue;
444
			}
443
			}
445
 
444
 
446
			// 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
447
			if (! array_filter($ligne, '__anonyme_3')) {
446
			if (! array_filter($ligne, '__anonyme_3')) {
448
				continue;
447
				continue;
449
			}
448
			}
450
 
449
 
451
			if ($enregistrement = self::chargerLigne($ligne, $dernier_ordre, $cel)) {
450
			if ($enregistrement = self::chargerLigne($ligne, $dernier_ordre, $cel)) {
452
				// $enregistrements[] = array_merge($colonnes_statiques, $enregistrement);
451
				// $enregistrements[] = array_merge($colonnes_statiques, $enregistrement);
453
				$enregistrements[] = $enregistrement;
452
				$enregistrements[] = $enregistrement;
454
				$pos = count($enregistrements) - 1;
453
				$pos = count($enregistrements) - 1;
455
				$last = &$enregistrements[$pos];
454
				$last = &$enregistrements[$pos];
456
 
455
 
457
				if (isset($enregistrement['_images'])) {
456
				if (isset($enregistrement['_images'])) {
458
					// 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
459
					// 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
460
					$toutes_images[] = array(
459
					$toutes_images[] = array(
461
						'images' => $last['_images'],
460
						'images' => $last['_images'],
462
						'obs_pos' => $pos);
461
						'obs_pos' => $pos);
463
					// 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,
464
					// mais est utile pour la liaison avec les images
463
					// mais est utile pour la liaison avec les images
465
					unset($last['_images']);
464
					unset($last['_images']);
466
				}
465
				}
467
 
466
 
468
				if (isset($enregistrement['_mots_cle'])) {
467
				if (isset($enregistrement['_mots_cle'])) {
469
					// 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
470
					// 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
471
					$tous_mots_cle[] = array(
470
					$tous_mots_cle[] = array(
472
						'mots_cle' => $last['_mots_cle'],
471
						'mots_cle' => $last['_mots_cle'],
473
						'obs_pos' => $pos);
472
						'obs_pos' => $pos);
474
					unset($last['_mots_cle']);
473
					unset($last['_mots_cle']);
475
				}
474
				}
476
 
475
 
477
				if (isset($enregistrement['_champs_etendus'])) {
476
				if (isset($enregistrement['_champs_etendus'])) {
478
					$tous_champs_etendus[] = array(
477
					$tous_champs_etendus[] = array(
479
						'champs_etendus' => $last['_champs_etendus'],
478
						'champs_etendus' => $last['_champs_etendus'],
480
						'ordre' => $dernier_ordre);
479
						'ordre' => $dernier_ordre);
481
					unset($last['_champs_etendus']);
480
					unset($last['_champs_etendus']);
482
				}
481
				}
483
				$dernier_ordre++;
482
				$dernier_ordre++;
484
			}
483
			}
485
		}
484
		}
486
		return array($enregistrements, $toutes_images, $tous_mots_cle, $tous_champs_etendus);
485
		return array($enregistrements, $toutes_images, $tous_mots_cle, $tous_champs_etendus);
487
	}
486
	}
488
 
487
 
489
	static function trierColonnes(&$enregistrements) {
488
	static function trierColonnes(&$enregistrements) {
490
		foreach ($enregistrements as &$enregistrement) {
489
		foreach ($enregistrements as &$enregistrement) {
491
			$enregistrement = self::sortArrayByArray($enregistrement, self::$ordre_BDD);
490
			$enregistrement = self::sortArrayByArray($enregistrement, self::$ordre_BDD);
492
		}
491
		}
493
	}
492
	}
494
 
493
 
495
	static function stockerMotsCle($enregistrements, $tous_mots_cle, $lastid) {
494
	static function stockerMotsCle($enregistrements, $tous_mots_cle, $lastid) {
496
		$c = 0;
495
		$c = 0;
497
		// debug: var_dump($tous_mots_cle);die;
496
		// debug: var_dump($tous_mots_cle);die;
498
		foreach ($tous_mots_cle as $v) {
497
		foreach ($tous_mots_cle as $v) {
499
			$c += count($v['mots_cle']['to_insert']);
498
			$c += count($v['mots_cle']['to_insert']);
500
		}
499
		}
501
		return $c;
500
		return $c;
502
	}
501
	}
503
 
502
 
504
	static function stockerImages($enregistrements, $toutes_images, $ordre_ids) {
503
	static function stockerImages($enregistrements, $toutes_images, $ordre_ids) {
505
		$valuesSql = array();
504
		$valuesSql = array();
506
		foreach ($toutes_images as $images_pour_obs) {
505
		foreach ($toutes_images as $images_pour_obs) {
507
			$obs = $enregistrements[$images_pour_obs['obs_pos']];
506
			$obs = $enregistrements[$images_pour_obs['obs_pos']];
508
			$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
509
			$transmission = $obs['transmission'];
508
			$transmission = $obs['transmission'];
510
			$date_transmission = $obs['date_transmission'];
509
			$date_transmission = $obs['date_transmission'];
511
			foreach ($images_pour_obs['images'] as $image) {
510
			foreach ($images_pour_obs['images'] as $image) {
512
				$id_img = $image['id_image'];
511
				$id_img = $image['id_image'];
513
				$valuesSql[] = "($id_img, $id_obs, NOW(), $transmission, $date_transmission)";
512
				$valuesSql[] = "($id_img, $id_obs, NOW(), $transmission, $date_transmission)";
514
			}
513
			}
515
		}
514
		}
516
 
515
 
517
		if ($valuesSql) {
516
		if ($valuesSql) {
518
			$clauseValues = implode(', ', $valuesSql);
517
			$clauseValues = implode(', ', $valuesSql);
519
			// 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
520
			$requete = 'INSERT INTO cel_images '.
519
			$requete = 'INSERT INTO cel_images '.
521
				'(id_image, ce_observation, date_liaison, transmission, date_transmission) '.
520
				'(id_image, ce_observation, date_liaison, transmission, date_transmission) '.
522
				"VALUES $clauseValues ".
521
				"VALUES $clauseValues ".
523
				'ON DUPLICATE KEY UPDATE '.
522
				'ON DUPLICATE KEY UPDATE '.
524
				'ce_observation = VALUES(ce_observation), '.
523
				'ce_observation = VALUES(ce_observation), '.
525
				'date_liaison = NOW(), '.
524
				'date_liaison = NOW(), '.
526
				'transmission = VALUES(transmission), '.
525
				'transmission = VALUES(transmission), '.
527
				'date_transmission = VALUES(date_transmission) '.
526
				'date_transmission = VALUES(date_transmission) '.
528
				' -- '.__FILE__.':'.__LINE__;
527
				' -- '.__FILE__.':'.__LINE__;
529
			Cel::db()->executer($requete);
528
			Cel::db()->executer($requete);
530
		}
529
		}
531
		return count($valuesSql);
530
		return count($valuesSql);
532
	}
531
	}
533
 
532
 
534
	/*
533
	/*
535
	 Aucune des valeurs présentes dans $enregistrement n'est quotée
534
	 Aucune des valeurs présentes dans $enregistrement n'est quotée
536
	 cad aucune des valeurs retournée par traiter{Espece|Localisation}()
535
	 cad aucune des valeurs retournée par traiter{Espece|Localisation}()
537
	 car ce tableau est passé à un PDO::preparedStatement() qui applique
536
	 car ce tableau est passé à un PDO::preparedStatement() qui applique
538
	  proprement les règle d'échappement.
537
	  proprement les règle d'échappement.
539
	*/
538
	*/
540
	static function chargerLigne($ligne, $dernier_ordre, $cel) {
539
	static function chargerLigne($ligne, $dernier_ordre, $cel) {
541
		// évite des notices d'index lors des trigger_error()
540
		// évite des notices d'index lors des trigger_error()
542
		$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]) : '';
543
 
542
 
544
		// en premier car le résultat est utile pour
543
		// en premier car le résultat est utile pour
545
		// * traiter espèce (traiterEspece())
544
		// * traiter espèce (traiterEspece())
546
		// * traiter longitude et latitude (traiterLonLat())
545
		// * traiter longitude et latitude (traiterLonLat())
547
		$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);
548
 
547
 
549
		// $espece est rempli de plusieurs informations
548
		// $espece est rempli de plusieurs informations
550
		$espece = array(
549
		$espece = array(
551
			C_NOM_SEL => NULL,
550
			C_NOM_SEL => NULL,
552
			C_NOM_SEL_NN => NULL,
551
			C_NOM_SEL_NN => NULL,
553
			C_NOM_RET => NULL,
552
			C_NOM_RET => NULL,
554
			C_NOM_RET_NN => NULL,
553
			C_NOM_RET_NN => NULL,
555
			C_NT => NULL,
554
			C_NT => NULL,
556
			C_FAMILLE => NULL);
555
			C_FAMILLE => NULL);
557
		self::traiterEspece($ligne, $espece, $referentiel, $cel->taxon_info_webservice);
556
		self::traiterEspece($ligne, $espece, $referentiel, $cel->taxon_info_webservice);
558
 
557
 
559
		if (!$espece[C_NOM_SEL]) {
558
		if (!$espece[C_NOM_SEL]) {
560
			$referentiel = Cel::$fallback_referentiel;
559
			$referentiel = Cel::$fallback_referentiel;
561
		}
560
		}
562
		if ($espece[C_NOM_SEL] && !$espece[C_NOM_SEL_NN]) {
561
		if ($espece[C_NOM_SEL] && !$espece[C_NOM_SEL_NN]) {
563
			$referentiel = Cel::$fallback_referentiel;
562
			$referentiel = Cel::$fallback_referentiel;
564
		}
563
		}
565
 
564
 
566
		// $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
567
		$localisation = Array(C_ZONE_GEO => NULL, C_CE_ZONE_GEO => NULL);
566
		$localisation = Array(C_ZONE_GEO => NULL, C_CE_ZONE_GEO => NULL);
568
		self::traiterLocalisation($ligne, $localisation);
567
		self::traiterLocalisation($ligne, $localisation);
569
		//TODO: le jour où c'est efficace, traiter le pays à l'import
568
		//TODO: le jour où c'est efficace, traiter le pays à l'import
570
 
569
 
571
		// $transmission est utilisé pour date_transmission
570
		// $transmission est utilisé pour date_transmission
572
		// XXX: @ contre "Undefined index"
571
		// XXX: @ contre "Undefined index"
573
		@$transmission = in_array(strtolower(trim($ligne[C_TRANSMISSION])), array(1, 'oui')) ? 1 : 0;
572
		@$transmission = in_array(strtolower(trim($ligne[C_TRANSMISSION])), array(1, 'oui')) ? 1 : 0;
574
 
573
 
575
 
574
 
576
		// Dans ce tableau, seules devraient apparaître les données variable pour chaque ligne.
575
		// Dans ce tableau, seules devraient apparaître les données variable pour chaque ligne.
577
		// Dans ce tableau, l'ordre des clefs n'importe pas (cf: self::sortArrayByArray())
576
		// Dans ce tableau, l'ordre des clefs n'importe pas (cf: self::sortArrayByArray())
578
		$enregistrement = array(
577
		$enregistrement = array(
579
			"ordre" => $dernier_ordre,
578
			"ordre" => $dernier_ordre,
580
 
579
 
581
			"nom_sel" => $espece[C_NOM_SEL],
580
			"nom_sel" => $espece[C_NOM_SEL],
582
			"nom_sel_nn" => $espece[C_NOM_SEL_NN],
581
			"nom_sel_nn" => $espece[C_NOM_SEL_NN],
583
			"nom_ret" => $espece[C_NOM_RET],
582
			"nom_ret" => $espece[C_NOM_RET],
584
			"nom_ret_nn" => $espece[C_NOM_RET_NN],
583
			"nom_ret_nn" => $espece[C_NOM_RET_NN],
585
			"nt" => $espece[C_NT],
584
			"nt" => $espece[C_NT],
586
			"famille" => $espece[C_FAMILLE],
585
			"famille" => $espece[C_FAMILLE],
587
 
586
 
588
			"nom_referentiel" => $referentiel,
587
			"nom_referentiel" => $referentiel,
589
 
588
 
590
			"pays" => $ligne[C_PAYS],
589
			"pays" => $ligne[C_PAYS],
591
			"zone_geo" => $localisation[C_ZONE_GEO],
590
			"zone_geo" => $localisation[C_ZONE_GEO],
592
			"ce_zone_geo" => $localisation[C_CE_ZONE_GEO],
591
			"ce_zone_geo" => $localisation[C_CE_ZONE_GEO],
593
 
592
 
594
			// $ligne: uniquement pour les infos en cas de gestion d'erreurs (date incompréhensible)
593
			// $ligne: uniquement pour les infos en cas de gestion d'erreurs (date incompréhensible)
595
			"date_observation" => isset($ligne[C_DATE_OBSERVATION]) ? self::traiterDateObs($ligne[C_DATE_OBSERVATION], $ref_ligne) : null,
594
			"date_observation" => isset($ligne[C_DATE_OBSERVATION]) ? self::traiterDateObs($ligne[C_DATE_OBSERVATION], $ref_ligne) : null,
596
 
595
 
597
			"lieudit" => isset($ligne[C_LIEUDIT]) ? trim($ligne[C_LIEUDIT]) : null,
596
			"lieudit" => isset($ligne[C_LIEUDIT]) ? trim($ligne[C_LIEUDIT]) : null,
598
			"station" => isset($ligne[C_STATION]) ? trim($ligne[C_STATION]) : null,
597
			"station" => isset($ligne[C_STATION]) ? trim($ligne[C_STATION]) : null,
599
			"milieu" => isset($ligne[C_MILIEU]) ? trim($ligne[C_MILIEU]) : null,
598
			"milieu" => isset($ligne[C_MILIEU]) ? trim($ligne[C_MILIEU]) : null,
600
 
599
 
601
			"mots_cles_texte" => NULL, // TODO: foreign-key
600
			"mots_cles_texte" => NULL, // TODO: foreign-key
602
			// XXX: @ contre "Undefined index"
601
			// XXX: @ contre "Undefined index"
603
			"commentaire" => isset($ligne[C_COMMENTAIRE]) ? trim($ligne[C_COMMENTAIRE]) : null,
602
			"commentaire" => isset($ligne[C_COMMENTAIRE]) ? trim($ligne[C_COMMENTAIRE]) : null,
604
 
603
 
605
			"transmission" => $transmission,
604
			"transmission" => $transmission,
606
			"date_transmission" => $transmission ? date('Y-m-d H:i:s') : null, // pas de fonction SQL dans un PDO statement, <=> now()
605
			"date_transmission" => $transmission ? date('Y-m-d H:i:s') : null, // pas de fonction SQL dans un PDO statement, <=> now()
607
 
606
 
608
			// $ligne: uniquement pour les infos en cas de gestion d'erreurs (lon/lat incompréhensible)
607
			// $ligne: uniquement pour les infos en cas de gestion d'erreurs (lon/lat incompréhensible)
609
			"latitude" => isset($ligne[C_LATITUDE]) ? self::traiterLonLat(null, $ligne[C_LATITUDE], $referentiel, $ref_ligne) : null,
608
			"latitude" => isset($ligne[C_LATITUDE]) ? self::traiterLonLat(null, $ligne[C_LATITUDE], $referentiel, $ref_ligne) : null,
610
			"longitude" => isset($ligne[C_LONGITUDE]) ? self::traiterLonLat($ligne[C_LONGITUDE], null, $referentiel, $ref_ligne) : null,
609
			"longitude" => isset($ligne[C_LONGITUDE]) ? self::traiterLonLat($ligne[C_LONGITUDE], null, $referentiel, $ref_ligne) : null,
611
			"altitude" => isset($ligne[C_ALTITUDE]) ? intval($ligne[C_ALTITUDE]) : null, // TODO: guess alt from lon/lat
610
			"altitude" => isset($ligne[C_ALTITUDE]) ? intval($ligne[C_ALTITUDE]) : null, // TODO: guess alt from lon/lat
612
 
611
 
613
			// @ car potentiellement optionnelles ou toutes vides => pas d'index dans PHPExcel (tableau optimisé)
612
			// @ car potentiellement optionnelles ou toutes vides => pas d'index dans PHPExcel (tableau optimisé)
614
			"abondance" => @$ligne[C_ABONDANCE],
613
			"abondance" => @$ligne[C_ABONDANCE],
615
			"certitude" => @$ligne[C_CERTITUDE],
614
			"certitude" => @$ligne[C_CERTITUDE],
616
			"phenologie" => @$ligne[C_PHENOLOGIE],
615
			"phenologie" => @$ligne[C_PHENOLOGIE],
617
 
616
 
618
			"code_insee_calcule" => substr($localisation[C_CE_ZONE_GEO], -5) // varchar(5)
617
			"code_insee_calcule" => substr($localisation[C_CE_ZONE_GEO], -5) // varchar(5)
619
		);
618
		);
620
 
619
 
621
		// passage de $enregistrement par référence, ainsi ['_images'] n'est défini
620
		// passage de $enregistrement par référence, ainsi ['_images'] n'est défini
622
		// que si des résultats sont trouvés
621
		// que si des résultats sont trouvés
623
		// "@" car PHPExcel supprime les colonnes null sur toute la feuille (ou tout le chunk)
622
		// "@" car PHPExcel supprime les colonnes null sur toute la feuille (ou tout le chunk)
624
		if (@$ligne[C_IMAGES]) {
623
		if (@$ligne[C_IMAGES]) {
625
			self::traiterImage($ligne[C_IMAGES], $cel->id_utilisateur, $enregistrement);
624
			self::traiterImage($ligne[C_IMAGES], $cel->id_utilisateur, $enregistrement);
626
		}
625
		}
627
 
626
 
628
		if (@$ligne[C_MOTS_CLES_TEXTE]) {
627
		if (@$ligne[C_MOTS_CLES_TEXTE]) {
629
			self::traiterMotsCle($ligne[C_MOTS_CLES_TEXTE], $cel->id_utilisateur, $enregistrement);
628
			self::traiterMotsCle($ligne[C_MOTS_CLES_TEXTE], $cel->id_utilisateur, $enregistrement);
630
		}
629
		}
631
 
630
 
632
		$champs_etendus = self::traiterChampsEtendus($ligne, self::$indexes_colonnes_etendues);
631
		$champs_etendus = self::traiterChampsEtendus($ligne, self::$indexes_colonnes_etendues);
633
		if (!empty($champs_etendus)) {
632
		if (!empty($champs_etendus)) {
634
			$enregistrement['_champs_etendus'] = $champs_etendus;
633
			$enregistrement['_champs_etendus'] = $champs_etendus;
635
		}
634
		}
636
 
635
 
637
		return $enregistrement;
636
		return $enregistrement;
638
	}
637
	}
639
 
638
 
640
	static function traiterChampsEtendus(&$ligne, &$indexes_colonnes_etendues) {
639
	static function traiterChampsEtendus(&$ligne, &$indexes_colonnes_etendues) {
641
		$champs_etendus_indexes = array();
640
		$champs_etendus_indexes = array();
642
		foreach($indexes_colonnes_etendues as $index_num => $label) {
641
		foreach($indexes_colonnes_etendues as $index_num => $label) {
643
			if (isset($ligne[$index_num])) {
642
			if (isset($ligne[$index_num])) {
644
				$champs_etendus_indexes[str_replace(self::$prefixe_colonnes_etendues, '', $label)] = $ligne[$index_num];
643
				$champs_etendus_indexes[str_replace(self::$prefixe_colonnes_etendues, '', $label)] = $ligne[$index_num];
645
			}
644
			}
646
		}
645
		}
647
		return $champs_etendus_indexes;
646
		return $champs_etendus_indexes;
648
	}
647
	}
649
 
648
 
650
	static function traiterImage($str, $id_utilisateur, &$enregistrement) {
649
	static function traiterImage($str, $id_utilisateur, &$enregistrement) {
651
		$liste_images = array_filter(explode('/', $str));
650
		$liste_images = array_filter(explode('/', $str));
652
		array_walk($liste_images, array(__CLASS__, '__anonyme_4'));
651
		array_walk($liste_images, array(__CLASS__, '__anonyme_4'));
653
 
652
 
654
		$nomsOrignalConcat = implode(',', $liste_images);
653
		$nomsOrignalConcat = implode(',', $liste_images);
655
		$requete = 'SELECT id_image, nom_original '.
654
		$requete = 'SELECT id_image, nom_original '.
656
			'FROM cel_images '.
655
			'FROM cel_images '.
657
			"WHERE ce_utilisateur = $id_utilisateur AND nom_original IN ($nomsOrignalConcat) ".
656
			"WHERE ce_utilisateur = $id_utilisateur AND nom_original IN ($nomsOrignalConcat) ".
658
			' -- '.__FILE__.':'.__LINE__;
657
			' -- '.__FILE__.':'.__LINE__;
659
		$resultat = Cel::db()->requeter($requete);
658
		$resultat = Cel::db()->requeter($requete);
660
 
659
 
661
		if ($resultat) {
660
		if ($resultat) {
662
			$enregistrement['_images'] = $resultat;
661
			$enregistrement['_images'] = $resultat;
663
		}
662
		}
664
	}
663
	}
665
 
664
 
666
	static function traiterMotsCle($str, $id_utilisateur, &$enregistrement) {
665
	static function traiterMotsCle($str, $id_utilisateur, &$enregistrement) {
667
		$liste_mots_cle = $liste_mots_cle_recherche = array_map('trim', array_unique(array_filter(explode(',', $str))));
666
		$liste_mots_cle = $liste_mots_cle_recherche = array_map('trim', array_unique(array_filter(explode(',', $str))));
668
		array_walk($liste_mots_cle_recherche, array(__CLASS__, '__anonyme_4'));
667
		array_walk($liste_mots_cle_recherche, array(__CLASS__, '__anonyme_4'));
669
 
668
 
670
		if (self::$gestion_mots_cles == null) {
669
		if (self::$gestion_mots_cles == null) {
671
			$gestion_mots_cles = new GestionMotsCles($this->config, 'obs');
670
			$gestion_mots_cles = new GestionMotsCles($this->config, 'obs');
672
		}
671
		}
673
		$mots_cles_ids = $gestion_mots_cles->obtenirIdsMotClesPourMotsCles($liste_mots_cle, $id_utilisateur);
672
		$mots_cles_ids = $gestion_mots_cles->obtenirIdsMotClesPourMotsCles($liste_mots_cle, $id_utilisateur);
674
		foreach ($mots_cles_ids as $mot_cle) {
673
		foreach ($mots_cles_ids as $mot_cle) {
675
			$resultat[$mot_cle['id_mot_cle']] = $mot_cle['mot_cle'];
674
			$resultat[$mot_cle['id_mot_cle']] = $mot_cle['mot_cle'];
676
		}
675
		}
677
 
676
 
678
		$enregistrement['mots_cles_texte'] = implode(',', $liste_mots_cle);
677
		$enregistrement['mots_cles_texte'] = implode(',', $liste_mots_cle);
679
		$enregistrement['_mots_cle'] = array(
678
		$enregistrement['_mots_cle'] = array(
680
			'existing' => $resultat,
679
			'existing' => $resultat,
681
			'to_insert' => array_diff($liste_mots_cle, $resultat));
680
			'to_insert' => array_diff($liste_mots_cle, $resultat));
682
	}
681
	}
683
 
682
 
684
 
683
 
685
	/* FONCTIONS de TRANSFORMATION de VALEUR DE CELLULE */
684
	/* FONCTIONS de TRANSFORMATION de VALEUR DE CELLULE */
686
	// TODO: PHP 5.3, utiliser date_parse_from_format()
685
	// TODO: PHP 5.3, utiliser date_parse_from_format()
687
	// TODO: parser les heures (cf product-owner)
686
	// TODO: parser les heures (cf product-owner)
688
	// TODO: passer par le timestamp pour s'assurer de la validité
687
	// TODO: passer par le timestamp pour s'assurer de la validité
689
	static function traiterDateObs($date, $ref_ligne) {
688
	static function traiterDateObs($date, $ref_ligne) {
690
		// TODO: see https://github.com/PHPOffice/PHPExcel/issues/208
689
		// TODO: see https://github.com/PHPOffice/PHPExcel/issues/208
691
		// TODO: PHPExcel_Shared_Date::ExcelToPHP()
690
		// TODO: PHPExcel_Shared_Date::ExcelToPHP()
692
		if (is_double($date)) {
691
		if (is_double($date)) {
693
			if ($date > 0) {
692
			if ($date > 0) {
694
				return PHPExcel_Style_NumberFormat::toFormattedString($date, PHPExcel_Style_NumberFormat::FORMAT_DATE_YYYYMMDD2) . " 00:00:00";
693
				return PHPExcel_Style_NumberFormat::toFormattedString($date, PHPExcel_Style_NumberFormat::FORMAT_DATE_YYYYMMDD2) . " 00:00:00";
695
			}
694
			}
696
 
695
 
697
			$msg = "ligne «{$ref_ligne}»: Attention: date antérieure à 1970 et format de cellule «DATE» utilisés ensemble";
696
			$msg = "ligne «{$ref_ligne}»: Attention: date antérieure à 1970 et format de cellule «DATE» utilisés ensemble";
698
			trigger_error($msg, E_USER_NOTICE);
697
			trigger_error($msg, E_USER_NOTICE);
699
		} else {
698
		} else {
700
			// attend l'un des formats de
699
			// attend l'un des formats de
701
			// http://www.php.net/manual/fr/datetime.formats.date.php
700
			// http://www.php.net/manual/fr/datetime.formats.date.php
702
			// le plus simple: YYYY/MM/DD (utilisé à l'export), mais DD-MM-YYYY est aussi supporté
701
			// le plus simple: YYYY/MM/DD (utilisé à l'export), mais DD-MM-YYYY est aussi supporté
703
			$matches = NULL;
702
			$matches = NULL;
704
			// et on essaie d'être sympa et supporter aussi DD/MM/YYYY
703
			// et on essaie d'être sympa et supporter aussi DD/MM/YYYY
705
			if (preg_match(';^([0-3]?\d)/([01]\d)/([12]\d\d\d)$;', $date, $matches)) {
704
			if (preg_match(';^([0-3]?\d)/([01]\d)/([12]\d\d\d)$;', $date, $matches)) {
706
				$date = $matches[3] . '/' . $matches[2] . '/' . $matches[1];
705
				$date = $matches[3] . '/' . $matches[2] . '/' . $matches[1];
707
			}
706
			}
708
			$timestamp = strtotime($date);
707
			$timestamp = strtotime($date);
709
			if (! $timestamp || $timestamp > time() + 3600 * 24 * 1) { // une journée d'avance maxi autorisée (décallage horaire ?)
708
			if (! $timestamp || $timestamp > time() + 3600 * 24 * 1) { // une journée d'avance maxi autorisée (décallage horaire ?)
710
				if ($date) {
709
				if ($date) {
711
					$msg = "ligne «{$ref_ligne}»: Attention: date erronée ($date)";
710
					$msg = "ligne «{$ref_ligne}»: Attention: date erronée ($date)";
712
					trigger_error($msg, E_USER_NOTICE);
711
					trigger_error($msg, E_USER_NOTICE);
713
				}
712
				}
714
				return NULL;
713
				return NULL;
715
			}
714
			}
716
			return strftime('%Y-%m-%d 00:00:00', $timestamp);
715
			return strftime('%Y-%m-%d 00:00:00', $timestamp);
717
		}
716
		}
718
	}
717
	}
719
 
718
 
720
	static function identReferentiel($referentiel, $ligne, $ref_ligne) {
719
	static function identReferentiel($referentiel, $ligne, $ref_ligne) {
721
		// SELECT DISTINCT nom_referentiel, COUNT(id_observation) AS count FROM cel_obs GROUP BY nom_referentiel ORDER BY count DESC;
720
		// SELECT DISTINCT nom_referentiel, COUNT(id_observation) AS count FROM cel_obs GROUP BY nom_referentiel ORDER BY count DESC;
722
		if (strpos($referentiel, 'bdtfx') !== FALSE) {
721
		if (strpos($referentiel, 'bdtfx') !== FALSE) {
723
			return 'bdtfx'; //:v1.01';
722
			return 'bdtfx'; //:v1.01';
724
		}
723
		}
725
		if (strpos($referentiel, 'bdtxa') !== FALSE) {
724
		if (strpos($referentiel, 'bdtxa') !== FALSE) {
726
			return 'bdtxa'; //:v1.00';
725
			return 'bdtxa'; //:v1.00';
727
		}
726
		}
728
		if (strpos($referentiel, 'bdnff') !== FALSE) {
727
		if (strpos($referentiel, 'bdnff') !== FALSE) {
729
			return 'bdtfx';
728
			return 'bdtfx';
730
		}
729
		}
731
		if (strpos($referentiel, 'isfan') !== FALSE) {
730
		if (strpos($referentiel, 'isfan') !== FALSE) {
732
			return 'isfan'; //:v1.00';
731
			return 'isfan'; //:v1.00';
733
		}
732
		}
734
		if (strpos($referentiel, 'apd') !== FALSE) {
733
		if (strpos($referentiel, 'apd') !== FALSE) {
735
			return 'apd'; //:v1.00';
734
			return 'apd'; //:v1.00';
736
		}
735
		}
737
		if (strpos($referentiel, 'autre') !== FALSE) {
736
		if (strpos($referentiel, 'autre') !== FALSE) {
738
			return 'autre';
737
			return 'autre';
739
		}
738
		}
740
 
739
 
741
		if ($referentiel && isset($ligne[C_NOM_SEL]) && $ligne[C_NOM_SEL]) {
740
		if ($referentiel && isset($ligne[C_NOM_SEL]) && $ligne[C_NOM_SEL]) {
742
			$msg = "ligne «{$ref_ligne}»: Attention: référentiel «{$referentiel}» inconnu";
741
			$msg = "ligne «{$ref_ligne}»: Attention: référentiel «{$referentiel}» inconnu";
743
			trigger_error($msg, E_USER_NOTICE);
742
			trigger_error($msg, E_USER_NOTICE);
744
			return 'autre';
743
			return 'autre';
745
		}
744
		}
746
		return NULL;
745
		return NULL;
747
	}
746
	}
748
 
747
 
749
	static function traiterLonLat($lon = NULL, $lat = NULL, $referentiel = 'bdtfx', $ref_ligne) {
748
	static function traiterLonLat($lon = NULL, $lat = NULL, $referentiel = 'bdtfx', $ref_ligne) {
750
		// en CSV ces valeurs sont des string, avec séparateur en français (","; cf défauts dans ExportXLS)
749
		// en CSV ces valeurs sont des string, avec séparateur en français (","; cf défauts dans ExportXLS)
751
		if ($lon && is_string($lon)) {
750
		if ($lon && is_string($lon)) {
752
			$lon = str_replace(',', '.', $lon);
751
			$lon = str_replace(',', '.', $lon);
753
		}
752
		}
754
		if ($lat && is_string($lat)) {
753
		if ($lat && is_string($lat)) {
755
			$lat = str_replace(',', '.', $lat);
754
			$lat = str_replace(',', '.', $lat);
756
		}
755
		}
757
 
756
 
758
		// sprintf applique une précision à 5 décimale (comme le ferait MySQL)
757
		// sprintf applique une précision à 5 décimale (comme le ferait MySQL)
759
		// tout en uniformisant le format de séparateur des décimales (le ".")
758
		// tout en uniformisant le format de séparateur des décimales (le ".")
760
		if ($lon && is_numeric($lon) && $lon >= -180 && $lon <= 180) {
759
		if ($lon && is_numeric($lon) && $lon >= -180 && $lon <= 180) {
761
			return sprintf('%.5F', $lon);
760
			return sprintf('%.5F', $lon);
762
		}
761
		}
763
		if ($lat && is_numeric($lat) && $lat >= -90 && $lat <= 90) {
762
		if ($lat && is_numeric($lat) && $lat >= -90 && $lat <= 90) {
764
			return sprintf('%.5F', $lat);
763
			return sprintf('%.5F', $lat);
765
		}
764
		}
766
 
765
 
767
		if ($lon || $lat) {
766
		if ($lon || $lat) {
768
			trigger_error("ligne \"{$ref_ligne}\": " .
767
			trigger_error("ligne \"{$ref_ligne}\": " .
769
				  "Attention: longitude ou latitude erronée",
768
				  "Attention: longitude ou latitude erronée",
770
				  E_USER_NOTICE);
769
				  E_USER_NOTICE);
771
		}
770
		}
772
		return NULL;
771
		return NULL;
773
	}
772
	}
774
 
773
 
775
	/*
774
	/*
776
	  TODO: s'affranchir du webservice pour la détermination du nom scientifique en s'appuyant sur cel_references,
775
	  TODO: s'affranchir du webservice pour la détermination du nom scientifique en s'appuyant sur cel_references,
777
	  pour des questions de performances
776
	  pour des questions de performances
778
	*/
777
	*/
779
	static function traiterEspece($ligne, Array &$espece, &$referentiel, $taxon_info_webservice) {
778
	static function traiterEspece($ligne, Array &$espece, &$referentiel, $taxon_info_webservice) {
780
		if (empty($ligne[C_NOM_SEL])) {
779
		if (empty($ligne[C_NOM_SEL])) {
781
			return;
780
			return;
782
		}
781
		}
783
 
782
 
784
		// nom_sel reste toujours celui de l'utilisateur
783
		// nom_sel reste toujours celui de l'utilisateur
785
		$espece[C_NOM_SEL] = trim($ligne[C_NOM_SEL]);
784
		$espece[C_NOM_SEL] = trim($ligne[C_NOM_SEL]);
786
 
785
 
787
		// XXX/attention, nous ne devrions pas accepter un référentiel absent !
786
		// XXX/attention, nous ne devrions pas accepter un référentiel absent !
788
		if (!$referentiel) {
787
		if (!$referentiel) {
789
			$referentiel = 'bdtfx';
788
			$referentiel = 'bdtfx';
790
		}
789
		}
791
		$taxon_info_webservice->setReferentiel($referentiel);
790
		$taxon_info_webservice->setReferentiel($referentiel);
792
		$ascii = iconv('UTF-8', 'ASCII//TRANSLIT', $ligne[C_NOM_SEL]);
791
		$ascii = iconv('UTF-8', 'ASCII//TRANSLIT', $ligne[C_NOM_SEL]);
793
 
792
 
794
		$determ = $taxon_info_webservice->rechercherInfosSurTexteCodeOuNumTax(trim($ligne[C_NOM_SEL]));
793
		$determ = $taxon_info_webservice->rechercherInfosSurTexteCodeOuNumTax(trim($ligne[C_NOM_SEL]));
795
 
794
 
796
		// note: rechercherInfosSurTexteCodeOuNumTax peut ne retourner qu'une seule clef "nom_sel"
795
		// note: rechercherInfosSurTexteCodeOuNumTax peut ne retourner qu'une seule clef "nom_sel"
797
		if (! $determ) {
796
		if (! $determ) {
798
			// on supprime les noms retenus et renvoi tel quel
797
			// on supprime les noms retenus et renvoi tel quel
799
			// on réutilise les define pour les noms d'indexes, tant qu'à faire
798
			// on réutilise les define pour les noms d'indexes, tant qu'à faire
800
			// XXX; tout à NULL sauf C_NOM_SEL ci-dessus ?
799
			// XXX; tout à NULL sauf C_NOM_SEL ci-dessus ?
801
			$espece[C_NOM_SEL_NN] = @$ligne[C_NOM_SEL_NN];
800
			$espece[C_NOM_SEL_NN] = @$ligne[C_NOM_SEL_NN];
802
			$espece[C_NOM_RET] = @$ligne[C_NOM_RET];
801
			$espece[C_NOM_RET] = @$ligne[C_NOM_RET];
803
			$espece[C_NOM_RET_NN] = @$ligne[C_NOM_RET_NN];
802
			$espece[C_NOM_RET_NN] = @$ligne[C_NOM_RET_NN];
804
			$espece[C_NT] = @$ligne[C_NT];
803
			$espece[C_NT] = @$ligne[C_NT];
805
			$espece[C_FAMILLE] = @$ligne[C_FAMILLE];
804
			$espece[C_FAMILLE] = @$ligne[C_FAMILLE];
806
 
805
 
807
			return;
806
			return;
808
		}
807
		}
809
 
808
 
810
		// succès de la détection, mais résultat partiel
809
		// succès de la détection, mais résultat partiel
811
		if (!isset($determ->id)) {
810
		if (!isset($determ->id)) {
812
			$determ = $taxon_info_webservice->effectuerRequeteInfosComplementairesSurNumNom($determ->{"nom_retenu.id"});
811
			$determ = $taxon_info_webservice->effectuerRequeteInfosComplementairesSurNumNom($determ->{"nom_retenu.id"});
813
		}
812
		}
814
 
813
 
815
		// ne devrait jamais arriver !
814
		// ne devrait jamais arriver !
816
		if (!$determ) {
815
		if (!$determ) {
817
			die("erreur critique: " . __FILE__ . ':' . __LINE__);
816
			die("erreur critique: " . __FILE__ . ':' . __LINE__);
818
		}
817
		}
819
 
818
 
820
		// un schéma <ref>:(nt|nn):<num> (ie: bdtfx:nt:8503) a été passé
819
		// un schéma <ref>:(nt|nn):<num> (ie: bdtfx:nt:8503) a été passé
821
		// dans ce cas on met à jour le référentiel avec celui passé dans le champ espèce
820
		// dans ce cas on met à jour le référentiel avec celui passé dans le champ espèce
822
		if (isset($determ->ref)) {
821
		if (isset($determ->ref)) {
823
			$referentiel = $determ->ref;
822
			$referentiel = $determ->ref;
824
		}
823
		}
825
 
824
 
826
		// succès de la détection
825
		// succès de la détection
827
		// nom_sel est remplacé, mais seulement si un motif spécial à été utilisé (bdtfx:nn:4567)
826
		// nom_sel est remplacé, mais seulement si un motif spécial à été utilisé (bdtfx:nn:4567)
828
		if ($taxon_info_webservice->is_notation_spe) {
827
		if ($taxon_info_webservice->is_notation_spe) {
829
			$espece[C_NOM_SEL] = $determ->nom_sci;
828
			$espece[C_NOM_SEL] = $determ->nom_sci;
830
		}
829
		}
831
 
830
 
832
		// écrasement des numéros (nomenclatural, taxonomique) saisis...
831
		// écrasement des numéros (nomenclatural, taxonomique) saisis...
833
		$espece[C_NOM_SEL_NN] = $determ->id;
832
		$espece[C_NOM_SEL_NN] = $determ->id;
834
		$espece[C_NOM_RET] = RechercheInfosTaxonBeta::supprimerBiblio($determ->nom_retenu_complet);
833
		$espece[C_NOM_RET] = RechercheInfosTaxonBeta::supprimerBiblio($determ->nom_retenu_complet);
835
		$espece[C_NOM_RET_NN] = $determ->{"nom_retenu.id"};
834
		$espece[C_NOM_RET_NN] = $determ->{"nom_retenu.id"};
836
		$espece[C_NT] = $determ->num_taxonomique;
835
		$espece[C_NT] = $determ->num_taxonomique;
837
		$espece[C_FAMILLE] = $determ->famille;
836
		$espece[C_FAMILLE] = $determ->famille;
838
		return;
837
		return;
839
	}
838
	}
840
 
839
 
841
	static function detectFromNom($nom) {
840
	static function detectFromNom($nom) {
842
		$r = Cel::db()->requeter(sprintf("SELECT num_nom, num_tax_sup FROM bdtfx_v1_01 WHERE (nom_sci LIKE '%s') ".
841
		$r = Cel::db()->requeter(sprintf("SELECT num_nom, num_tax_sup FROM bdtfx_v1_01 WHERE (nom_sci LIKE '%s') ".
843
			"ORDER BY nom_sci ASC LIMIT 0, 1",
842
			"ORDER BY nom_sci ASC LIMIT 0, 1",
844
			Cel::db()->proteger($nom)));
843
			Cel::db()->proteger($nom)));
845
		if ($r) {
844
		if ($r) {
846
			return $r;
845
			return $r;
847
		}
846
		}
848
 
847
 
849
		Cel::db()->requeter(sprintf("SELECT num_nom, num_tax_sup FROM bdtfx_v1_01 WHERE (nom_sci LIKE '%s' OR nom LIKE '%s') ".
848
		Cel::db()->requeter(sprintf("SELECT num_nom, num_tax_sup FROM bdtfx_v1_01 WHERE (nom_sci LIKE '%s' OR nom LIKE '%s') ".
850
			"ORDER BY nom_sci ASC LIMIT 0, 1",
849
			"ORDER BY nom_sci ASC LIMIT 0, 1",
851
			Cel::db()->proteger($nom),
850
			Cel::db()->proteger($nom),
852
			Cel::db()->proteger(str_replace(' ', '% ', $nom))));
851
			Cel::db()->proteger(str_replace(' ', '% ', $nom))));
853
		return $r;
852
		return $r;
854
	}
853
	}
855
 
854
 
856
	static function traiterLocalisation($ligne, Array &$localisation) {
855
	static function traiterLocalisation($ligne, Array &$localisation) {
857
		if (empty($ligne[C_ZONE_GEO])) {
856
		if (empty($ligne[C_ZONE_GEO])) {
858
			$ligne[C_ZONE_GEO] = NULL;
857
			$ligne[C_ZONE_GEO] = NULL;
859
		}
858
		}
860
		if (empty($ligne[C_CE_ZONE_GEO])) {
859
		if (empty($ligne[C_CE_ZONE_GEO])) {
861
			$ligne[C_CE_ZONE_GEO] = NULL;
860
			$ligne[C_CE_ZONE_GEO] = NULL;
862
		}
861
		}
863
 
862
 
864
		$identifiant_commune = trim($ligne[C_ZONE_GEO]);
863
		$identifiant_commune = trim($ligne[C_ZONE_GEO]);
865
		if (!$identifiant_commune) {
864
		if (!$identifiant_commune) {
866
			$departement = trim($ligne[C_CE_ZONE_GEO]);
865
			$departement = trim($ligne[C_CE_ZONE_GEO]);
867
 
866
 
868
			if (strpos($departement, 'INSEE-C:', 0) === 0) {
867
			if (strpos($departement, 'INSEE-C:', 0) === 0) {
869
				$localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
868
				$localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
870
				if (array_key_exists($localisation[C_CE_ZONE_GEO], self::$cache['geo'])) {
869
				if (array_key_exists($localisation[C_CE_ZONE_GEO], self::$cache['geo'])) {
871
					$localisation[C_ZONE_GEO] = self::$cache['geo'][$localisation[C_CE_ZONE_GEO]];
870
					$localisation[C_ZONE_GEO] = self::$cache['geo'][$localisation[C_CE_ZONE_GEO]];
872
				} else {
871
				} else {
873
					$nom = Cel::db()->requeter(sprintf("SELECT nom FROM cel_zones_geo WHERE code = %s LIMIT 1",
872
					$nom = Cel::db()->requeter(sprintf("SELECT nom FROM cel_zones_geo WHERE code = %s LIMIT 1",
874
						self::quoteNonNull(substr($localisation[C_CE_ZONE_GEO], strlen("INSEE-C:")))));
873
						self::quoteNonNull(substr($localisation[C_CE_ZONE_GEO], strlen("INSEE-C:")))));
875
					if ($nom) {
874
					if ($nom) {
876
						$localisation[C_ZONE_GEO] = $nom[0]['nom'];
875
						$localisation[C_ZONE_GEO] = $nom[0]['nom'];
877
					}
876
					}
878
					self::$cache['geo'][$localisation[C_CE_ZONE_GEO]] = @$nom[0]['nom'];
877
					self::$cache['geo'][$localisation[C_CE_ZONE_GEO]] = @$nom[0]['nom'];
879
				}
878
				}
880
				return;
879
				return;
881
			}
880
			}
882
 
881
 
883
			if (!is_numeric($departement)) {
882
			if (!is_numeric($departement)) {
884
				$localisation[C_CE_ZONE_GEO] = $ligne[C_CE_ZONE_GEO];
883
				$localisation[C_CE_ZONE_GEO] = $ligne[C_CE_ZONE_GEO];
885
				return;
884
				return;
886
			}
885
			}
887
 
886
 
888
			$cache_attempted = FALSE;
887
			$cache_attempted = FALSE;
889
			if(array_key_exists($departement, self::$cache['geo'])) {
888
			if(array_key_exists($departement, self::$cache['geo'])) {
890
				$cache_attempted = TRUE;
889
				$cache_attempted = TRUE;
891
				if (self::$cache['geo'][$departement][0] && self::$cache['geo'][$departement][1]) {
890
				if (self::$cache['geo'][$departement][0] && self::$cache['geo'][$departement][1]) {
892
					$localisation[C_ZONE_GEO] = self::$cache['geo'][$departement][0];
891
					$localisation[C_ZONE_GEO] = self::$cache['geo'][$departement][0];
893
					$localisation[C_CE_ZONE_GEO] = self::$cache['geo'][$departement][1];
892
					$localisation[C_CE_ZONE_GEO] = self::$cache['geo'][$departement][1];
894
					return;
893
					return;
895
				}
894
				}
896
			}
895
			}
897
 
896
 
898
			$requete = "SELECT DISTINCT nom, CONCAT('INSEE-C:', code) AS code ".
897
			$requete = "SELECT DISTINCT nom, CONCAT('INSEE-C:', code) AS code ".
899
				'FROM cel_zones_geo '.
898
				'FROM cel_zones_geo '.
900
				'WHERE code = %s '.
899
				'WHERE code = %s '.
901
				'LIMIT 1 '.
900
				'LIMIT 1 '.
902
				' -- '.__FILE__.':'.__LINE__;
901
				' -- '.__FILE__.':'.__LINE__;
903
			$resultat_commune = Cel::db()->requeter(sprintf($requete, self::quoteNonNull($departement)));
902
			$resultat_commune = Cel::db()->requeter(sprintf($requete, self::quoteNonNull($departement)));
904
			if (! $cache_attempted && $resultat_commune) {
903
			if (! $cache_attempted && $resultat_commune) {
905
				$localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
904
				$localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
906
				$localisation[C_CE_ZONE_GEO] = $resultat_commune[0]['code'];
905
				$localisation[C_CE_ZONE_GEO] = $resultat_commune[0]['code'];
907
				self::$cache['geo'][$departement] = array($resultat_commune[0]['nom'], $resultat_commune[0]['code']);
906
				self::$cache['geo'][$departement] = array($resultat_commune[0]['nom'], $resultat_commune[0]['code']);
908
				return;
907
				return;
909
			}
908
			}
910
			$localisation[C_CE_ZONE_GEO] = $ligne[C_CE_ZONE_GEO];
909
			$localisation[C_CE_ZONE_GEO] = $ligne[C_CE_ZONE_GEO];
911
			return;
910
			return;
912
		}
911
		}
913
 
912
 
914
		$select = "SELECT DISTINCT nom, code FROM cel_zones_geo";
913
		$select = "SELECT DISTINCT nom, code FROM cel_zones_geo";
915
 
914
 
916
		if (preg_match('/(.+) \((\d{1,5})\)/', $identifiant_commune, $elements)) {
915
		if (preg_match('/(.+) \((\d{1,5})\)/', $identifiant_commune, $elements)) {
917
			// commune + departement : montpellier (34)
916
			// commune + departement : montpellier (34)
918
			$nom_commune=$elements[1];
917
			$nom_commune=$elements[1];
919
			$code_commune=$elements[2];
918
			$code_commune=$elements[2];
920
			if (strlen($code_commune) <= 2) {
919
			if (strlen($code_commune) <= 2) {
921
				$requete = sprintf("%s WHERE nom = %s AND code LIKE %s",
920
				$requete = sprintf("%s WHERE nom = %s AND code LIKE %s",
922
					$select, self::quoteNonNull($nom_commune),
921
					$select, self::quoteNonNull($nom_commune),
923
					self::quoteNonNull($code_commune.'%'));
922
					self::quoteNonNull($code_commune.'%'));
924
			} else {
923
			} else {
925
				$requete = sprintf("%s WHERE nom = %s AND code = %d",
924
				$requete = sprintf("%s WHERE nom = %s AND code = %d",
926
					$select, self::quoteNonNull($nom_commune),
925
					$select, self::quoteNonNull($nom_commune),
927
					$code_commune);
926
					$code_commune);
928
			}
927
			}
929
		} elseif (preg_match('/^(\d+|(2[ab]\d+))$/i', $identifiant_commune, $elements)) {
928
		} elseif (preg_match('/^(\d+|(2[ab]\d+))$/i', $identifiant_commune, $elements)) {
930
			// Code insee seul
929
			// Code insee seul
931
			$code_insee_commune=$elements[1];
930
			$code_insee_commune=$elements[1];
932
			$requete = sprintf("%s WHERE code = %s", $select, self::quoteNonNull($code_insee_commune));
931
			$requete = sprintf("%s WHERE code = %s", $select, self::quoteNonNull($code_insee_commune));
933
		} else {
932
		} else {
934
			// Commune seule (le departement sera recupere dans la colonne departement si elle est presente)
933
			// Commune seule (le departement sera recupere dans la colonne departement si elle est presente)
935
			// on prend le risque ici de retourner une mauvaise Commune
934
			// on prend le risque ici de retourner une mauvaise Commune
936
			$nom_commune = str_replace(" ", "%", iconv('UTF-8', 'ASCII//TRANSLIT', $identifiant_commune));
935
			$nom_commune = str_replace(" ", "%", iconv('UTF-8', 'ASCII//TRANSLIT', $identifiant_commune));
937
			$requete = sprintf("%s WHERE nom LIKE %s", $select, self::quoteNonNull($nom_commune.'%'));
936
			$requete = sprintf("%s WHERE nom LIKE %s", $select, self::quoteNonNull($nom_commune.'%'));
938
		}
937
		}
939
 
938
 
940
		if (array_key_exists($identifiant_commune, self::$cache['geo'])) {
939
		if (array_key_exists($identifiant_commune, self::$cache['geo'])) {
941
			$resultat_commune = self::$cache['geo'][$identifiant_commune];
940
			$resultat_commune = self::$cache['geo'][$identifiant_commune];
942
		} else {
941
		} else {
943
			$resultat_commune = Cel::db()->requeter($requete);
942
			$resultat_commune = Cel::db()->requeter($requete);
944
			self::$cache['geo'][$identifiant_commune] = $resultat_commune;
943
			self::$cache['geo'][$identifiant_commune] = $resultat_commune;
945
		}
944
		}
946
 
945
 
947
		// cas de la commune introuvable dans le référentiel
946
		// cas de la commune introuvable dans le référentiel
948
		// réinitialisation aux valeurs du fichier XLS
947
		// réinitialisation aux valeurs du fichier XLS
949
		if (! $resultat_commune) {
948
		if (! $resultat_commune) {
950
			$localisation[C_ZONE_GEO] = trim($ligne[C_ZONE_GEO]);
949
			$localisation[C_ZONE_GEO] = trim($ligne[C_ZONE_GEO]);
951
			$localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
950
			$localisation[C_CE_ZONE_GEO] = trim($ligne[C_CE_ZONE_GEO]);
952
		} else {
951
		} else {
953
			$localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
952
			$localisation[C_ZONE_GEO] = $resultat_commune[0]['nom'];
954
			$localisation[C_CE_ZONE_GEO] = "INSEE-C:" . $resultat_commune[0]['code'];
953
			$localisation[C_CE_ZONE_GEO] = "INSEE-C:" . $resultat_commune[0]['code'];
955
			return;
954
			return;
956
		}
955
		}
957
 
956
 
958
		$departement =& $localisation[C_CE_ZONE_GEO];
957
		$departement =& $localisation[C_CE_ZONE_GEO];
959
 
958
 
960
		if (strpos($departement, "INSEE-C:", 0) === 0) {
959
		if (strpos($departement, "INSEE-C:", 0) === 0) {
961
			$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
960
			$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
962
			$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
961
			$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
963
		}
962
		}
964
 
963
 
965
		if (!is_numeric($departement)) {
964
		if (!is_numeric($departement)) {
966
			$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
965
			$localisation[C_ZONE_GEO] = $localisation[C_ZONE_GEO];
967
			$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
966
			$localisation[C_CE_ZONE_GEO] = $localisation[C_CE_ZONE_GEO];
968
		}
967
		}
969
 
968
 
970
		if (strlen($departement) == 4) {
969
		if (strlen($departement) == 4) {
971
			$departement = "INSEE-C:0$departement";
970
			$departement = "INSEE-C:0$departement";
972
		}
971
		}
973
		if (strlen($departement) == 5) {
972
		if (strlen($departement) == 5) {
974
			$departement = "INSEE-C:$departement";
973
			$departement = "INSEE-C:$departement";
975
		}
974
		}
976
		$departement = trim($departement);
975
		$departement = trim($departement);
977
 
976
 
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
	public static function stockerChampsEtendus($champs_etendus, $ordre_ids, $config) {
981
	public static function stockerChampsEtendus($champs_etendus, $ordre_ids, $config) {
983
		// singleton du pauvre mais l'export est suffisamment inefficace pour s'en priver
982
		// singleton du pauvre mais l'export est suffisamment inefficace pour s'en priver
984
		self::$gestion_champs_etendus = self::$gestion_champs_etendus == null ?
983
		self::$gestion_champs_etendus = self::$gestion_champs_etendus == null ?
985
			new GestionChampsEtendus($config, 'obs') :
984
			new GestionChampsEtendus($config, 'obs') :
986
			self::$gestion_champs_etendus;
985
			self::$gestion_champs_etendus;
987
 
986
 
988
		$champs_etendus_obs = array();
987
		$champs_etendus_obs = array();
989
		foreach ($champs_etendus as $champ_etendu_a_obs) {
988
		foreach ($champs_etendus as $champ_etendu_a_obs) {
990
			$id_obs = $ordre_ids[$champ_etendu_a_obs['ordre']]; // id réel de l'observation correspondant à l'ordre
989
			$id_obs = $ordre_ids[$champ_etendu_a_obs['ordre']]; // id réel de l'observation correspondant à l'ordre
991
			foreach ($champ_etendu_a_obs['champs_etendus'] as $label => $champ) {
990
			foreach ($champ_etendu_a_obs['champs_etendus'] as $label => $champ) {
992
				// XXX: insère t'on des valeurs vides ?
991
				// XXX: insère t'on des valeurs vides ?
993
				$valeur = $champ;
992
				$valeur = $champ;
994
				$cle = $label;
993
				$cle = $label;
995
 
994
 
996
				if (!empty($cle) && !empty($valeur)) {
995
				if (!empty($cle) && !empty($valeur)) {
997
					$champ_etendu_a_inserer = new ChampEtendu();
996
					$champ_etendu_a_inserer = new ChampEtendu();
998
					$champ_etendu_a_inserer->id = $id_obs;
997
					$champ_etendu_a_inserer->id = $id_obs;
999
					$champ_etendu_a_inserer->cle = $cle;
998
					$champ_etendu_a_inserer->cle = $cle;
1000
					$champ_etendu_a_inserer->valeur = $valeur;
999
					$champ_etendu_a_inserer->valeur = $valeur;
1001
 
1000
 
1002
					$champs_etendus_obs[] = $champ_etendu_a_inserer;
1001
					$champs_etendus_obs[] = $champ_etendu_a_inserer;
1003
				}
1002
				}
1004
			}
1003
			}
1005
		}
1004
		}
1006
 
1005
 
1007
		self::$gestion_champs_etendus->ajouterParLots($champs_etendus_obs);
1006
		self::$gestion_champs_etendus->ajouterParLots($champs_etendus_obs);
1008
		//TODO: que faire si l'insertion des champs étendus échoue ?
1007
		//TODO: que faire si l'insertion des champs étendus échoue ?
1009
		return count($champs_etendus_obs);
1008
		return count($champs_etendus_obs);
1010
	}
1009
	}
1011
 
1010
 
1012
	/* HELPERS */
1011
	/* HELPERS */
1013
 
1012
 
1014
	// http://stackoverflow.com/questions/348410/sort-an-array-based-on-another-array
1013
	// http://stackoverflow.com/questions/348410/sort-an-array-based-on-another-array
1015
	// XXX; utilisé aussi (temporairement ?) par FormateurGroupeColonne.
1014
	// XXX; utilisé aussi (temporairement ?) par FormateurGroupeColonne.
1016
	static function sortArrayByArray($array, $orderArray) {
1015
	static function sortArrayByArray($array, $orderArray) {
1017
		$ordered = array();
1016
		$ordered = array();
1018
		foreach($orderArray as $key) {
1017
		foreach($orderArray as $key) {
1019
			if (array_key_exists($key, $array)) {
1018
			if (array_key_exists($key, $array)) {
1020
				$ordered[$key] = $array[$key];
1019
				$ordered[$key] = $array[$key];
1021
				unset($array[$key]);
1020
				unset($array[$key]);
1022
			}
1021
			}
1023
		}
1022
		}
1024
		return $ordered + $array;
1023
		return $ordered + $array;
1025
	}
1024
	}
1026
 
1025
 
1027
	// retourne une BBox [N,S,E,O) pour un référentiel donné
1026
	// retourne une BBox [N,S,E,O) pour un référentiel donné
1028
	static function getReferentielBBox($referentiel) {
1027
	static function getReferentielBBox($referentiel) {
1029
		if ($referentiel == 'bdtfx') {
1028
		if ($referentiel == 'bdtfx') {
1030
			return Array(
1029
			return Array(
1031
				'NORD' => 51.2, // Dunkerque
1030
				'NORD' => 51.2, // Dunkerque
1032
				'SUD' => 41.3, // Bonifacio
1031
				'SUD' => 41.3, // Bonifacio
1033
				'EST' => 9.7, // Corse
1032
				'EST' => 9.7, // Corse
1034
				'OUEST' => -5.2); // Ouessan
1033
				'OUEST' => -5.2); // Ouessan
1035
		}
1034
		}
1036
		return FALSE;
1035
		return FALSE;
1037
	}
1036
	}
1038
 
1037
 
1039
	// ces valeurs ne sont pas inséré via les placeholders du PDO::preparedStatement
1038
	// ces valeurs ne sont pas inséré via les placeholders du PDO::preparedStatement
1040
	// et doivent donc être échappées correctement.
1039
	// et doivent donc être échappées correctement.
1041
	public function initialiser_colonnes_statiques() {
1040
	public function initialiser_colonnes_statiques() {
1042
		$this->colonnes_statiques = array_merge($this->colonnes_statiques,
1041
		$this->colonnes_statiques = array_merge($this->colonnes_statiques,
1043
			array(
1042
			array(
1044
				'ce_utilisateur' => self::quoteNonNull($this->id_utilisateur), // peut-être un hash ou un id
1043
				'ce_utilisateur' => self::quoteNonNull($this->id_utilisateur), // peut-être un hash ou un id
1045
				'prenom_utilisateur' => self::quoteNonNull($this->utilisateur['prenom']),
1044
				'prenom_utilisateur' => self::quoteNonNull($this->utilisateur['prenom']),
1046
				'nom_utilisateur' => self::quoteNonNull($this->utilisateur['nom']),
1045
				'nom_utilisateur' => self::quoteNonNull($this->utilisateur['nom']),
1047
				'courriel_utilisateur' => self::quoteNonNull($this->utilisateur['courriel']),
1046
				'courriel_utilisateur' => self::quoteNonNull($this->utilisateur['courriel']),
1048
			));
1047
			));
1049
	}
1048
	}
1050
 
1049
 
1051
	static function initialiser_pdo_ordered_statements($colonnes_statiques) {
1050
	static function initialiser_pdo_ordered_statements($colonnes_statiques) {
1052
		return Array(
1051
		return Array(
1053
			// insert_ligne_pattern_ordre
1052
			// insert_ligne_pattern_ordre
1054
			sprintf('INSERT INTO cel_obs (%s, %s) VALUES',
1053
			sprintf('INSERT INTO cel_obs (%s, %s) VALUES',
1055
				implode(', ', array_keys($colonnes_statiques)),
1054
				implode(', ', array_keys($colonnes_statiques)),
1056
				implode(', ', array_diff(self::$ordre_BDD, array_keys($colonnes_statiques)))),
1055
				implode(', ', array_diff(self::$ordre_BDD, array_keys($colonnes_statiques)))),
1057
 
1056
 
1058
			// insert_ligne_pattern_ordre
1057
			// insert_ligne_pattern_ordre
1059
			sprintf('(%s, %s ?)',
1058
			sprintf('(%s, %s ?)',
1060
				implode(', ', $colonnes_statiques),
1059
				implode(', ', $colonnes_statiques),
1061
				str_repeat('?, ', count(self::$ordre_BDD) - count($colonnes_statiques) - 1))
1060
				str_repeat('?, ', count(self::$ordre_BDD) - count($colonnes_statiques) - 1))
1062
		);
1061
		);
1063
	}
1062
	}
1064
 
1063
 
1065
	static function initialiser_pdo_statements($colonnes_statiques) {
1064
	static function initialiser_pdo_statements($colonnes_statiques) {
1066
		return Array(
1065
		return Array(
1067
			// insert_prefix
1066
			// insert_prefix
1068
			sprintf('INSERT INTO cel_obs (%s) VALUES ',
1067
			sprintf('INSERT INTO cel_obs (%s) VALUES ',
1069
				implode(', ', self::$ordre_BDD)),
1068
				implode(', ', self::$ordre_BDD)),
1070
 
1069
 
1071
 
1070
 
1072
			// insert_ligne_pattern, cf: self::$insert_ligne_pattern
1071
			// insert_ligne_pattern, cf: self::$insert_ligne_pattern
1073
			'(' .
1072
			'(' .
1074
			// 3) créé une chaîne de liste de champ à inséré en DB
1073
			// 3) créé une chaîne de liste de champ à inséré en DB
1075
			implode(', ', array_values(
1074
			implode(', ', array_values(
1076
			// 2) garde les valeurs fixes (de $colonnes_statiques),
1075
			// 2) garde les valeurs fixes (de $colonnes_statiques),
1077
			// mais remplace les NULL par des "?"
1076
			// mais remplace les NULL par des "?"
1078
			array_map('__anonyme_5',
1077
			array_map('__anonyme_5',
1079
				  // 1) créé un tableau genre (nom_sel_nn => NULL) depuis self::$ordre_BDD
1078
				  // 1) créé un tableau genre (nom_sel_nn => NULL) depuis self::$ordre_BDD
1080
				  // et écrase certaines valeurs avec $colonnes_statiques (initilisé avec les données utilisateur)
1079
				  // et écrase certaines valeurs avec $colonnes_statiques (initilisé avec les données utilisateur)
1081
				  array_merge(array_map('__anonyme_6', array_flip(self::$ordre_BDD)), $colonnes_statiques
1080
				  array_merge(array_map('__anonyme_6', array_flip(self::$ordre_BDD)), $colonnes_statiques
1082
				  )))) .
1081
				  )))) .
1083
			')'
1082
			')'
1084
		);
1083
		);
1085
	}
1084
	}
1086
 
1085
 
1087
	// équivalent à Bdd->proteger() (qui wrap PDO::quote),
1086
	// équivalent à Bdd->proteger() (qui wrap PDO::quote),
1088
	// sans transformer NULL en ""
1087
	// sans transformer NULL en ""
1089
	static function quoteNonNull($chaine) {
1088
	static function quoteNonNull($chaine) {
1090
		if (is_null($chaine)) {
1089
		if (is_null($chaine)) {
1091
			return 'NULL';
1090
			return 'NULL';
1092
		}
1091
		}
1093
		if (!is_string($chaine) && !is_integer($chaine)) {
1092
		if (!is_string($chaine) && !is_integer($chaine)) {
1094
			die('erreur: ' . __FILE__ . ':' . __LINE__);
1093
			die('erreur: ' . __FILE__ . ':' . __LINE__);
1095
		}
1094
		}
1096
		return Cel::db()->quote($chaine);
1095
		return Cel::db()->quote($chaine);
1097
	}
1096
	}
1098
 
1097
 
1099
	public function erreurs_stock($errno, $errstr) {
1098
	public function erreurs_stock($errno, $errstr) {
1100
		$this->bilan[] = $errstr;
1099
		$this->bilan[] = $errstr;
1101
	}
1100
	}
1102
}
1101
}
1103
?>
1102
?>