Allows secretaries to validate entreprises (Closes #1361).
[platal.git] / classes / address.php
CommitLineData
eb54852e
SJ
1<?php
2/***************************************************************************
3 * Copyright (C) 2003-2010 Polytechnique.org *
4 * http://opensource.polytechnique.org/ *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program is distributed in the hope that it will be useful, *
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the Free Software *
18 * Foundation, Inc., *
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
20 ***************************************************************************/
21
22/** Class Address is meant to perform most of the access to the table profile_addresses.
23 *
24 * profile_addresses describes an Address, which can be related to either a
25 * Profile, a Job or a Company:
26 * - for a Profile:
27 * - `type` is set to 'home'
28 * - `pid` is set to the related profile pid (in profiles)
29 * - `id` is the id of the address in the list of those related to that profile
30 * - `jobid` is set to 0
31 *
32 * - for a Company:
33 * - `type` is set to 'hq'
34 * - `pid` is set to 0
35 * - `jobid` is set to the id of the company (in profile_job_enum)
36 * - `id` is set to 0 (only one address per Company)
37 *
38 * - for a Job:
39 * - `type` is set to 'job'
40 * - `pid` is set to the pid of the Profile of the related Job (in both profiles and profile_job)
41 * - `id` is the id of the job to which we refer (in profile_job)
42 * - `jobid` is set to 0
43 *
44 * Thus an Address can be linked to a Company, a Profile, or a Job.
45 */
46class Address
47{
48 const LINK_JOB = 'job';
49 const LINK_COMPANY = 'hq';
50 const LINK_PROFILE = 'home';
51
f5f68e07
SJ
52 // List of all available postal formattings.
53 private static $formattings = array('FRANCE' => 'FR');
54
55 // Abbreviations to be used to format French postal addresses.
56 private static $streetAbbreviations = array(
57 'ALLEE' => 'ALL',
58 'AVENUE' => 'AV',
59 'BOULEVARD' => 'BD',
60 'CENTRE' => 'CTRE',
61 'CENTRE COMMERCIAL' => 'CCAL',
62 'IMMEUBLE' => 'IMM',
63 'IMMEUBLES' => 'IMM',
64 'IMPASSE' => 'IMP',
65 'LIEU-DIT' => 'LD',
66 'LOTISSEMENT' => 'LOT',
67 'PASSAGE' => 'PAS',
68 'PLACE' => 'PL',
69 'RESIDENCE' => 'RES',
70 'ROND-POINT' => 'RPT',
71 'ROUTE' => 'RTE',
72 'SQUARE' => 'SQ',
73 'VILLAGE' => 'VLGE',
74 'ZONE D\'ACTIVITE' => 'ZA',
75 'ZONE D\'AMENAGEMENT CONCERTE' => 'ZAC',
76 'ZONE D\'AMENAGEMENT DIFFERE' => 'ZAD',
77 'ZONE INDUSTRIELLE' => 'ZI'
78 );
79 private static $otherAbbreviations = array(
80 'ADJUDANT' => 'ADJ',
81 'AERODROME' => 'AERD',
82 'AEROGARE' => 'AERG',
83 'AERONAUTIQUE' => 'AERN',
84 'AEROPORT' => 'AERP',
85 'AGENCE' => 'AGCE',
86 'AGRICOLE' => 'AGRIC',
87 'ANCIEN' => 'ANC',
88 'ANCIENNEMENT' => 'ANC',
89 'APPARTEMENT' => 'APP',
90 'APPARTEMENTS' => 'APP',
91 'ARMEMENT' => 'ARMT',
92 'ARRONDISSEMENT' => 'ARR',
93 'ASPIRANT' => 'ASP',
94 'ASSOCIATION' => 'ASSOC',
95 'ASSURANCE' => 'ASSUR',
96 'ATELIER' => 'AT',
97 'BARAQUEMENT' => 'BRQ',
98 'BAS' => 'BAS',
99 'BASSE' => 'BAS',
100 'BASSES' => 'BAS',
101 'BATAILLON' => 'BTN',
102 'BATAILLONS' => 'BTN',
103 'BATIMENT' => 'BAT',
104 'BATIMENTS' => 'BAT',
105 'BIS' => 'B',
106 'BOITE POSTALE' => 'BP',
107 'CABINET' => 'CAB',
108 'CANTON' => 'CANT',
109 'CARDINAL' => 'CDL',
110 'CASE POSTALE' => 'CP',
111 'CHAMBRE' => 'CHBR',
112 'CITADELLE' => 'CTD',
113 'COLLEGE' => 'COLL',
114 'COLONEL' => 'CNL',
115 'COLONIE' => 'COLO',
116 'COMITE' => 'CTE',
117 'COMMANDANT' => 'CDT',
118 'COMMERCIAL' => 'CIAL',
119 'COMMUNE' => 'COM',
120 'COMMUNAL' => 'COM',
121 'COMMUNAUX' => 'COM',
122 'COMPAGNIE' => 'CIE',
123 'COMPAGNON' => 'COMP',
124 'COMPAGNONS' => 'COMP',
125 'COOPERATIVE' => 'COOP',
126 'COURSE SPECIALE' => 'CS',
127 'CROIX' => 'CRX',
128 'DELEGATION' => 'DELEG',
129 'DEPARTEMENTAL' => 'DEP',
130 'DEPARTEMENTAUX' => 'DEP',
131 'DIRECTEUR' => 'DIR',
132 'DIRECTECTION' => 'DIR',
133 'DIVISION' => 'DIV',
134 'DOCTEUR' => 'DR',
135 'ECONOMIE' => 'ECO',
136 'ECONOMIQUE' => 'ECO',
137 'ECRIVAIN' => 'ECRIV',
138 'ECRIVAINS' => 'ECRIV',
139 'ENSEIGNEMENT' => 'ENST',
140 'ENSEMBLE' => 'ENS',
141 'ENTREE' => 'ENT',
142 'ENTREES' => 'ENT',
143 'ENTREPRISE' => 'ENTR',
144 'EPOUX' => 'EP',
145 'EPOUSE' => 'EP',
146 'ETABLISSEMENT' => 'ETS',
147 'ETAGE' => 'ETG',
148 'ETAT MAJOR' => 'EM',
149 'EVEQUE' => 'EVQ',
150 'FACULTE' => 'FAC',
151 'FORET' => 'FOR',
152 'FORESTIER' => 'FOR',
153 'FRANCAIS' => 'FR',
154 'FRANCAISE' => 'FR',
155 'FUSILIER' => 'FUS',
156 'GENDARMERIE' => 'GEND',
157 'GENERAL' => 'GAL',
158 'GOUVERNEMENTAL' => 'GOUV',
159 'GOUVERNEUR' => 'GOU',
160 'GRAND' => 'GD',
161 'GRANDE' => 'GDE',
162 'GRANDES' => 'GDES',
163 'GRANDS' => 'GDS',
164 'HAUT' => 'HT',
165 'HAUTE' => 'HTE',
166 'HAUTES' => 'HTES',
167 'HAUTS' => 'HTS',
168 'HOPITAL' => 'HOP',
169 'HOPITAUX' => 'HOP',
170 'HOSPICE' => 'HOSP',
171 'HOSPITALIER' => 'HOSP',
172 'HOTEL' => 'HOT',
173 'INFANTERIE' => 'INFANT',
174 'INFERIEUR' => 'INF',
175 'INFERIEUR' => 'INF',
176 'INGENIEUR' => 'ING',
177 'INSPECTEUR' => 'INSP',
178 'INSTITUT' => 'INST',
179 'INTERNATIONAL' => 'INTERN',
180 'INTERNATIONALE' => 'INTERN',
181 'LABORATOIRE' => 'LABO',
182 'LIEUTENANT' => 'LT',
183 'LIEUTENANT DE VAISSEAU' => 'LTDV',
184 'MADAME' => 'MME',
185 'MADEMOISELLE' => 'MLLE',
186 'MAGASIN' => 'MAG',
187 'MAISON' => 'MAIS',
188 'MAITRE' => 'ME',
189 'MARECHAL' => 'MAL',
190 'MARITIME' => 'MAR',
191 'MEDECIN' => 'MED',
192 'MEDICAL' => 'MED',
193 'MESDAMES' => 'MMES',
194 'MESDEMOISELLES' => 'MLLES',
195 'MESSIEURS' => 'MM',
196 'MILITAIRE' => 'MIL',
197 'MINISTERE' => 'MIN',
198 'MONSEIGNEUR' => 'MGR',
199 'MONSIEUR' => 'M',
200 'MUNICIPAL' => 'MUN',
201 'MUTUEL' => 'MUT',
202 'NATIONAL' => 'NAL',
203 'NOTRE DAME' => 'ND',
204 'NOUVEAU' => 'NOUV',
205 'NOUVEL' => 'NOUV',
206 'NOUVELLE' => 'NOUV',
207 'OBSERVATOIRE' => 'OBS',
208 'PASTEUR' => 'PAST',
209 'PETIT' => 'PT',
210 'PETITE' => 'PTE',
211 'PETITES' => 'PTES',
212 'PETITS' => 'PTS',
213 'POLICE' => 'POL',
214 'PREFET' => 'PREF',
215 'PREFECTURE' => 'PREF',
216 'PRESIDENT' => 'PDT',
217 'PROFESSEUR' => 'PR',
218 'PROFESSIONNEL' => 'PROF',
219 'PROFESSIONNELE' => 'PROF',
220 'PROLONGE' => 'PROL',
221 'PROLONGEE' => 'PROL',
222 'PROPRIETE' => 'PROP',
223 'QUATER' => 'Q',
224 'QUINQUIES' => 'C',
225 'RECTEUR' => 'RECT',
226 'REGIMENT' => 'RGT',
227 'REGION' => 'REG',
228 'REGIONAL' => 'REG',
229 'REGIONALE' => 'REG',
230 'REPUBLIQUE' => 'REP',
231 'RESTAURANT' => 'REST',
232 'SAINT' => 'ST',
233 'SAINTE' => 'STE',
234 'SAINTES' => 'STES',
235 'SAINTS' => 'STS',
236 'SANATORIUM' => 'SANA',
237 'SERGENT' => 'SGT',
238 'SERVICE' => 'SCE',
239 'SOCIETE' => 'SOC',
240 'SOUS COUVERT' => 'SC',
241 'SOUS-PREFET' => 'SPREF',
242 'SUPERIEUR' => 'SUP',
243 'SUPERIEURE' => 'SUP',
244 'SYNDICAT' => 'SYND',
245 'TECHNICIEN' => 'TECH',
246 'TECHNICIENNE' => 'TECH',
247 'TECHNICIQUE' => 'TECH',
248 'TER' => 'T',
249 'TRI SERVICE ARRIVEE' => 'TSA',
250 'TUNNEL' => 'TUN',
251 'UNIVERSITAIRE' => 'UNVT',
252 'UNIVERSITE' => 'UNIV',
253 'VELODROME' => 'VELOD',
254 'VEUVE' => 'VVE',
255 'VIEILLE' => 'VIEL',
256 'VIEILLES' => 'VIEL',
257 'VIEUX' => 'VX'
258 );
259 private static $entrepriseAbbreviations = array(
260 'COOPERATIVE D\'UTILISATION DE MATERIEL AGRICOLE EN COMMUN' => 'CUMA',
261 'ETABLISSEMENT PUBLIC A CARACTERE INDUSTRIEL ET COMMERCIAL' => 'EPIC',
262 'ETABLISSEMENT PUBLIC ADMINISTRATIF' => 'EPA',
263 'GROUPEMENT AGRICOLE D\'EXPLOITATION EN COMMUN' => 'GAEC',
264 'GROUPEMENT D\'INTERET ECONOMIQUE' => 'GIE',
265 'GROUPEMENT D\'INTERET PUBLIC' => 'GIP',
266 'GROUPEMENT EUROPEEN D\'INTERET ECONOMIQUE' => 'GEIE',
267 'OFFICE PUBLIC D\'HABITATION A LOYER MODERE' => 'OPHLM',
268 'SOCIETE A RESPONSABILITE LIMITEE' => 'SARL',
269 'SOCIETE ANONYME' => 'SA',
270 'SOCIETE CIVILE DE PLACEMENT COLLECTIF IMMOBILIER' => 'SCPI',
271 'SOCIETE CIVILE PROFESSIONNELLE' => 'SCP',
272 'SOCIETE COOPERATIVE OUVRIERE DE PRODUCTION ET DE CREDIT' => 'SCOP',
273 'SOCIETE D\'AMENAGEMENT FONCIER ET D\'EQUIPEMENT RURAL' => 'SAFER',
274 'SOCIETE D\'ECONOMIE MIXTE' => 'SEM',
275 'SOCIETE D\'INTERET COLLECTIF AGRICOLE' => 'SICA',
276 'SOCIETE D\'INVESTISSEMENT A CAPITAL VARIABLE' => 'SICAV',
277 'SOCIETE EN NOM COLLECTIF' => 'SNC',
278 'SOCIETE IMMOBILIERE POUR LE COMMERCE ET L\'INDUSTRIE' => 'SICOMI',
279 'SOCIETE MIXTE D\'INTERET AGRICOLE' => 'SMIA',
280 'SYNDICAT INTERCOMMUNAL A VOCATION MULTIPLE' => 'SIVOM',
281 'SYNDICAT INTERCOMMUNAL A VOCATION UNIQUE' => 'SIVU'
282 );
283
eb54852e
SJ
284 // Primary key fields: the quadruplet ($pid, $jobid, $type, $id) defines a unique address.
285 public $pid = 0;
286 public $jobid = 0;
287 public $type = Address::LINK_PROFILE;
288 public $id = 0;
289
290 // Geocoding fields.
291 public $accuracy = 0;
292 public $text = '';
293 public $postalText = '';
294 public $postalCode = null;
295 public $localityId = null;
296 public $subAdministrativeAreaId = null;
297 public $administrativeAreaId = null;
298 public $localityName = null;
299 public $subAdministrativeAreaName = null;
300 public $administrativeAreaName = null;
301 public $countryId = null;
302 public $latitude = null;
303 public $longitude = null;
304 public $north = null;
305 public $south = null;
306 public $east = null;
307 public $west = null;
308 public $geocodedText = null;
eb54852e
SJ
309 public $geocodeChosen = null;
310
311 // Database's field required for both 'home' and 'job' addresses.
5f096c57 312 public $pub = 'ax';
eb54852e
SJ
313
314 // Database's fields required for 'home' addresses.
baee0f5a 315 public $flags = null; // 'current', 'temporary', 'secondary', 'mail', 'cedex', 'deliveryIssue'
eb54852e
SJ
316 public $comment = null;
317 public $current = null;
318 public $temporary = null;
319 public $secondary = null;
320 public $mail = null;
baee0f5a 321 public $deliveryIssue = null;
eb54852e
SJ
322
323 // Remaining fields that do not belong to profile_addresses.
324 public $phones = array();
325 public $error = false;
326 public $changed = 0;
327 public $removed = 0;
328
329 public function __construct(array $data = array())
330 {
331 if (count($data) > 0) {
332 foreach ($data as $key => $val) {
333 $this->$key = $val;
334 }
335 }
336
545bc699
FB
337 if (!is_null($this->flags)) {
338 $this->flags = new PlFlagSet($this->flags);
339 } else {
baee0f5a 340 static $flags = array('current', 'temporary', 'secondary', 'mail', 'deliveryIssue');
eb54852e 341
545bc699
FB
342 $this->flags = new PlFlagSet();
343 foreach ($flags as $flag) {
344 if (!is_null($this->$flag) && ($this->$flag == 1 || $this->$flag == 'on')) {
345 $this->flags->addFlag($flag, 1);
346 $this->$flag = null;
eb54852e 347 }
545bc699
FB
348 $this->flags->addFlag('cedex', (strpos(strtoupper(preg_replace(array("/[0-9,\"'#~:;_\- ]/", "/\r\n/"),
349 array('', "\n"), $this->text)), 'CEDEX')) !== false);
eb54852e
SJ
350 }
351 }
352 }
353
323ac187
SJ
354 public function setId($id)
355 {
356 $this->id = $id;
357 }
358
eb54852e
SJ
359 public function phones()
360 {
361 return $this->phones;
362 }
363
364 public function addPhone(Phone &$phone)
365 {
366 if ($phone->linkType() == Phone::LINK_ADDRESS && $phone->pid() == $this->pid) {
367 $this->phones[$phone->uniqueId()] = $phone;
368 }
369 }
370
371 public function hasFlag($flag)
372 {
622e7063 373 return ($this->flags != null && $this->flags->hasFlag($flag));
eb54852e
SJ
374 }
375
f5f68e07
SJ
376 /** Auxilary function for formatting postal addresses.
377 * If the needle is found in the haystack, it notifies the substitution's
378 * success, modifies the length accordingly and returns either the matching
379 * substitution or the needle.
380 */
381 private function substitute($needle, $haystack, &$length, &$success)
382 {
383 if (array_key_exists($needle, $haystack)) {
384 $success = true;
385 $length -= (strlen($needle) - strlen($haystack[$needle]));
386 return $haystack[$needle];
387 }
388 return $needle;
389 }
390
391 /** Checks if the line corresponds to a French street line.
392 * A line is considered a French street line if it starts by between 1 and 4 numbers.
393 */
394 private function isStreetFR($line)
395 {
396 return preg_match('/^\d{1,4}\D/', $line);
397 }
398
399 /** Retrieves a French street number and slit the rest of the line into an array.
400 * @param $words: array containing the rest of the line (a word per cell).
401 * @param $line: line to consider.
402 * Returns the street number.
403 */
404 private function getStreetNumberFR(&$line)
405 {
406 // First we define numbers and separators.
407 $numberReq = '(\d{1,4})\s*(BIS|TER|QUATER|[A-Z])?';
cce807c5
SJ
408 $separatorReq = '\s*(?:\\|-|&|A|ET)?\s*';
409
410 // Then we retrieve the number(s) and the rest of the line.
411 // $matches contains:
412 // -0: the full patern, here the given line,
413 // -1: the number,
414 // -2: its optionnal quantifier,
415 // -3: an optionnal second number,
416 // -4: the second number's optionnal quantifier,
417 // -5: the rest of the line.
418 preg_match('/^' . $numberReq . '(?:' . $separatorReq . $numberReq . ')?\s+(.*)/', $line, $matches);
f5f68e07
SJ
419 $number = $matches[1];
420 $line = $matches[5];
421
422 // If there is a precision on the address, we concatenate it to the number.
423 if ($matches[2] != '') {
cce807c5 424 $number .= $matches[2]{0};
f5f68e07 425 } elseif ($matches[4] != '') {
cce807c5 426 $number .= $matches[4]{0};
f5f68e07
SJ
427 }
428
429 return $number;
430 }
431
432 /** Checks if the line corresponds to a French locality line.
433 * A line is considered a French locality line if it starts by exactly a
434 * postal code of exactly 5 numbers.
435 */
436 private function isLocalityFR($line)
437 {
438 return preg_match('/^\d{5}\D/', $line);
439 }
440
441 /** Retrieves a French postal code and slit the rest of the line into an array.
442 * @param $words: array containing the rest of the line (a word per cell).
443 * @param $line: line to consider.
444 * Returns the postal code, and cuts it out from the line.
445 */
446 private function getPostalCodeFR(&$line)
447 {
448 $number = substr($line, 0, 5);
449 $line = trim(substr($line, 5));
450 return $number;
451 }
452
453 /** Returns the address formated for French postal use (cf AFNOR XPZ 10-011).
454 * A postal addresse containts at most 6 lines of at most 38 characters each:
455 * - addressee's identification ("MONSIEUR JEAN DURAND", "DURAND SA"…),
456 * - delivery point identification ("CHEZ TOTO APPARTEMENT 2", "SERVICE ACHAT"…),
457 * - building localisation complement ("ENTREE A BATIMENT DES JONQUILLES", "ZONE INDUSTRIELLE OUEST"…),
458 * - N° and street name ("25 RUE DES FLEURS", "LES VIGNES"…),
459 * - delivery service, street localisation complement ("BP 40122", "BP 40112 AREYRES"…),
460 * - postal code and locality or cedex code and cedex ("33500 LIBOURNE", "33506 LIBOURNE CEDEX"…).
461 * Punctuation must be removed, all leters must be uppercased.
462 * Both locality and street name must not take more than 32 characters.
463 *
464 * @param $arrayText: array containing the address to be formated, one
465 * address line per array line.
466 * @param $count: array size.
467 */
cce807c5 468 private function formatPostalAddressFR($arrayText)
f5f68e07
SJ
469 {
470 // First removes country if any.
cce807c5 471 $count = count($arrayText);
f5f68e07
SJ
472 if ($arrayText[$count - 1] == 'FRANCE') {
473 unset($arrayText[$count - 1]);
474 --$count;
475 }
476
477 // All the lines must have less than 38 characters but street and
478 // locality lines whose limit is 32 characters.
479 foreach ($arrayText as $lineNumber => $line) {
480 if ($isStreetLine = $this->isStreetFR($line)) {
481 $formattedLine = $this->getStreetNumberFR($line) . ' ';
482 $limit = 32;
483 } elseif ($this->isLocalityFR($line)) {
484 $formattedLine = $this->getPostalCodeFR($line) . ' ';
485 $limit = 32;
59e8fb00 486 } else {
f5f68e07
SJ
487 $formattedLine = '';
488 $limit = 38;
489 }
490
491 $words = explode(' ', $line);
492 $count = count($words);
493 $length = $count - 1;
494 foreach ($words as $word) {
495 $length += strlen($word);
59e8fb00 496 }
f5f68e07
SJ
497
498 // Checks is length is ok. Otherwise, we try to shorten words and
499 // update the length of the current line accordingly.
500 for ($i = 0; $i < $count && $length > $limit; ++$i) {
501 $success = false;
502 if ($isStreetLine) {
503 $sub = $this->substitute($words[$i], Address::$streetAbbreviations, $length, $success);
504 }
505 // Entreprises' substitution are only suitable for the first two lines.
506 if ($lineNumber <= 2 && !$success) {
507 $sub = $this->substitute($words[$i], Address::$entrepriseAbbreviations, $length, $success);
508 }
509 if (!$success) {
510 $sub = $this->substitute($words[$i], Address::$otherAbbreviations, $length, $success);
511 }
512
513 $formattedLine .= $sub . ' ';
514 }
515 for (; $i < $count; ++$i) {
516 $formattedLine .= $words[$i] . ' ';
517 }
518 $arrayText[$lineNumber] = trim($formattedLine);
59e8fb00 519 }
f5f68e07
SJ
520
521 return implode("\n", $arrayText);
522 }
523
524 // Formats postal addresses.
525 // First erases punctuation, accents… Then uppercase the address and finally
526 // calls the country's dedicated formatting function.
527 public function formatPostalAddress()
528 {
529 // Performs rough formatting.
530 $text = mb_strtoupper(replace_accent($this->text));
531 $text = str_replace(array(',', ';', '.', ':', '!', '?', '"', '«', '»'), '', $text);
532 $text = preg_replace('/( |\t)+/', ' ', $text);
533 $arrayText = explode("\n", $text);
534 $arrayText = array_map('trim', $arrayText);
535
536 // Search for country.
537 $countries = DirEnum::getOptions(DirEnum::COUNTRIES);
538 $countries = array_map('replace_accent', $countries);
539 $countries = array_map('strtoupper', $countries);
540 $count = count($arrayText);
541 if (in_array(strtoupper($address->country), Address::$formattings)) {
cce807c5 542 $text = call_user_func(array($this, 'formatPostalAddress' . Address::$formattings[strtoupper($address->country)]), $arrayText);
f5f68e07 543 } elseif (array_key_exists($arrayText[$count - 1], Address::$formattings)) {
cce807c5 544 $text = call_user_func(array($this, 'formatPostalAddress' . Address::$formattings[$arrayText[$count - 1]]), $arrayText);
f5f68e07 545 } elseif (!in_array($arrayText[$count - 1], $countries)) {
cce807c5 546 $text = $this->formatPostalAddressFR($arrayText);
f5f68e07
SJ
547 } else {
548 $text = implode("\n", $arrayText);
549 }
550
551 $this->postalText = $text;
59e8fb00
SJ
552 }
553
eb54852e
SJ
554 public function format(array $format = array())
555 {
556 if (empty($format)) {
557 $format['requireGeocoding'] = false;
558 $format['stripGeocoding'] = false;
59e8fb00 559 $format['postalText'] = false;
9783cb98
SJ
560 } else {
561 foreach (array('requireGeocoding', 'stripGeocoding', 'postalText') as $type) {
562 $format[$type] = (isset($format[$type])) ? $format[$type] : false;
563 }
eb54852e
SJ
564 }
565 $this->text = trim($this->text);
566 if ($this->removed == 1) {
567 $this->text = '';
568 return true;
569 }
570
eb54852e
SJ
571 if ($format['requireGeocoding'] || $this->changed == 1) {
572 $gmapsGeocoder = new GMapsGeocoder();
573 $gmapsGeocoder->getGeocodedAddress($this);
574 $this->changed = 0;
575 $this->error = !empty($this->geocodedText);
576 }
577 if ($format['stripGeocoding'] || ($this->type == self::LINK_COMPANY && $this->error) || $this->geocodeChosen === '0') {
578 $gmapsGeocoder = new GMapsGeocoder();
579 $gmapsGeocoder->stripGeocodingFromAddress($this);
580 if ($this->geocodeChosen === '0') {
581 $mailer = new PlMailer('profile/geocoding.mail.tpl');
582 $mailer->assign('text', $this->text);
583 $mailer->assign('geoloc', $this->geocodedText);
584 $mailer->send();
585 }
586 }
f2ac8f49
SJ
587 if ($this->countryId == '') {
588 $this->countryId = null;
589 }
eb54852e
SJ
590 $this->geocodeChosen = null;
591 $this->phones = Phone::formatFormArray($this->phones, $this->error);
f5f68e07
SJ
592 if ($format['postalText']) {
593 $this->formatPostalAddress();
594 }
eb54852e
SJ
595 return !$this->error;
596 }
597
598 public function toFormArray()
599 {
600 $address = array(
601 'accuracy' => $this->accuracy,
602 'text' => $this->text,
603 'postalText' => $this->postalText,
604 'postalCode' => $this->postalCode,
605 'localityId' => $this->localityId,
606 'subAdministrativeAreaId' => $this->subAdministrativeAreaId,
607 'administrativeAreaId' => $this->administrativeAreaId,
608 'countryId' => $this->countryId,
609 'localityName' => $this->localityName,
610 'subAdministrativeAreaName' => $this->subAdministrativeAreaName,
611 'administrativeAreaName' => $this->administrativeAreaName,
612 'latitude' => $this->latitude,
613 'longitude' => $this->longitude,
614 'north' => $this->north,
615 'south' => $this->south,
616 'east' => $this->east,
617 'west' => $this->west,
618 'error' => $this->error,
619 'changed' => $this->changed,
620 'removed' => $this->removed,
621 );
622 if (!is_null($this->geocodedText)) {
623 $address['geocodedText'] = $this->geocodedText;
eb54852e
SJ
624 $address['geocodeChosen'] = $this->geocodeChosen;
625 }
626
627 if ($this->type == self::LINK_PROFILE || $this->type == self::LINK_JOB) {
628 $address['pub'] = $this->pub;
629 }
630 if ($this->type == self::LINK_PROFILE) {
baee0f5a 631 static $flags = array('current', 'temporary', 'secondary', 'mail', 'cedex', 'deliveryIssue');
eb54852e
SJ
632
633 foreach ($flags as $flag) {
634 $address[$flag] = $this->flags->hasFlag($flag);
635 }
636 $address['comment'] = $this->comment;
637 $address['phones'] = Phone::formatFormArray($this->phones);
638 }
639
640 return $address;
641 }
642
643 private function toString()
644 {
14aba233 645 $address = $this->text;
eb54852e 646 if ($this->type == self::LINK_PROFILE || $this->type == self::LINK_JOB) {
14aba233
SJ
647 static $pubs = array('public' => 'publique', 'ax' => 'annuaire AX', 'private' => 'privé');
648 $address .= ' (affichage ' . $pubs[$this->pub];
eb54852e
SJ
649 }
650 if ($this->type == self::LINK_PROFILE) {
651 static $flags = array(
baee0f5a
SJ
652 'current' => 'actuelle',
653 'temporary' => 'temporaire',
654 'secondary' => 'secondaire',
655 'mail' => 'conctactable par courier',
656 'deliveryIssue' => 'n\'habite pas à l\'adresse indiquée',
657 'cedex' => 'type cédex',
eb54852e
SJ
658 );
659
14aba233
SJ
660 if (!$this->flags->hasFlag('temporary')) {
661 $address .= ', permanente';
662 }
663 if (!$this->flags->hasFlag('secondary')) {
664 $address .= ', principale';
665 }
eb54852e
SJ
666 foreach ($flags as $flag => $flagName) {
667 if ($this->flags->hasFlag($flag)) {
668 $address .= ', ' . $flagName;
669 }
670 }
14aba233
SJ
671 if ($this->comment) {
672 $address .= ', commentaire : ' . $this->comment;
673 }
eb54852e
SJ
674 if ($phones = Phone::formArrayToString($this->phones)) {
675 $address .= ', ' . $phones;
676 }
14aba233
SJ
677 } elseif ($this->type == self::LINK_JOB) {
678 $address .= ')';
eb54852e
SJ
679 }
680 return $address;
681 }
682
683 private function isEmpty()
684 {
685 return (!$this->text || $this->text == '');
686 }
687
688 public function save()
689 {
690 static $areas = array('administrativeArea', 'subAdministrativeArea', 'locality');
691
739c49b5 692 $this->format(array('postalText' => true));
eb54852e 693 if (!$this->isEmpty()) {
eb54852e
SJ
694 foreach ($areas as $area) {
695 Geocoder::getAreaId($this, $area);
696 }
697
698 XDB::execute('INSERT INTO profile_addresses (pid, jobid, type, id, flags, accuracy,
699 text, postalText, postalCode, localityId,
700 subAdministrativeAreaId, administrativeAreaId,
51d30e26 701 countryId, latitude, longitude, pub, comment,
eb54852e 702 north, south, east, west)
51d30e26
SJ
703 VALUES ({?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?},
704 {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?})',
eb54852e
SJ
705 $this->pid, $this->jobid, $this->type, $this->id, $this->flags, $this->accuracy,
706 $this->text, $this->postalText, $this->postalCode, $this->localityId,
707 $this->subAdministrativeAreaId, $this->administrativeAreaId,
708 $this->countryId, $this->latitude, $this->longitude,
51d30e26 709 $this->pub, $this->comment,
eb54852e
SJ
710 $this->north, $this->south, $this->east, $this->west);
711
712 if ($this->type == self::LINK_PROFILE) {
713 Phone::savePhones($this->phones, $this->pid, Phone::LINK_ADDRESS, $this->id);
714 }
715 }
716 }
717
781a24bc
SJ
718 public function delete()
719 {
720 XDB::execute('DELETE FROM profile_addresses
721 WHERE pid = {?} AND jobid = {?} AND type = {?} AND id = {?}',
722 $this->pid, $this->jobid, $this->type, $this->id);
723 }
724
725 static public function deleteAddresses($pid, $type, $jobid = null)
eb54852e
SJ
726 {
727 $where = '';
728 if (!is_null($pid)) {
729 $where = XDB::format(' AND pid = {?}', $pid);
730 }
731 if (!is_null($jobid)) {
732 $where = XDB::format(' AND jobid = {?}', $jobid);
733 }
734 XDB::execute('DELETE FROM profile_addresses
735 WHERE type = {?}' . $where,
736 $type);
737 if ($type == self::LINK_PROFILE) {
738 Phone::deletePhones($pid, Phone::LINK_ADDRESS);
739 }
740 }
741
742 /** Saves addresses into the database.
743 * @param $data: an array of form formatted addresses.
744 * @param $pid, $type, $linkid: pid, type and id concerned by the update.
745 */
746 static public function saveFromArray(array $data, $pid, $type = self::LINK_PROFILE, $linkid = null)
747 {
748 foreach ($data as $id => $value) {
749 if (!is_null($linkid)) {
750 $value['id'] = $linkid;
751 } else {
752 $value['id'] = $id;
753 }
754 if (!is_null($pid)) {
755 $value['pid'] = $pid;
756 }
757 if (!is_null($type)) {
758 $value['type'] = $type;
759 }
760 $address = new Address($value);
761 $address->save();
762 }
763 }
764
765 static private function formArrayWalk(array $data, $function, &$success = true, $requiresEmptyAddress = false)
766 {
767 $addresses = array();
768 foreach ($data as $item) {
769 $address = new Address($item);
770 $success = ($address->format() && $success);
771 if (!$address->isEmpty()) {
772 $addresses[] = call_user_func(array($address, $function));
773 }
774 }
775 if (count($address) == 0 && $requiresEmptyAddress) {
776 $address = new Address();
777 $addresses[] = call_user_func(array($address, $function));
778 }
779 return $addresses;
780 }
781
782 // Formats an array of form addresses into an array of form formatted addresses.
783 static public function formatFormArray(array $data, &$success = true)
784 {
785 // Only a single address can be the profile's current address and she must have one.
786 $hasCurrent = false;
787 foreach ($data as $key => &$address) {
788 if (isset($address['current']) && $address['current']) {
789 if ($hasCurrent) {
790 $address['current'] = false;
791 } else {
792 $hasCurrent = true;
793 }
794 }
795 }
796 if (!$hasCurrent && count($value) > 0) {
797 foreach ($value as &$address) {
798 $address['current'] = true;
799 break;
800 }
801 }
802
803 return self::formArrayWalk($data, 'toFormArray', $success, true);
804 }
805
806 static public function formArrayToString(array $data)
807 {
14aba233 808 return implode(', ', self::formArrayWalk($data, 'toString'));
eb54852e
SJ
809 }
810
811 static public function iterate(array $pids = array(), array $types = array(),
812 array $jobids = array(), array $pubs = array())
813 {
814 return new AddressIterator($pids, $types, $jobids, $pubs);
815 }
816}
817
818/** Iterator over a set of Phones
819 *
820 * @param $pid, $type, $jobid, $pub
821 *
822 * The iterator contains the phones that correspond to the value stored in the
823 * parameters' arrays.
824 */
825class AddressIterator implements PlIterator
826{
827 private $dbiter;
828
829 public function __construct(array $pids, array $types, array $jobids, array $pubs)
830 {
831 $where = array();
832 if (count($pids) != 0) {
833 $where[] = XDB::format('(pa.pid IN {?})', $pids);
834 }
835 if (count($types) != 0) {
836 $where[] = XDB::format('(pa.type IN {?})', $types);
837 }
838 if (count($jobids) != 0) {
839 $where[] = XDB::format('(pa.jobid IN {?})', $jobids);
840 }
841 if (count($pubs) != 0) {
842 $where[] = XDB::format('(pa.pub IN {?})', $pubs);
843 }
844 $sql = 'SELECT pa.pid, pa.jobid, pa.type, pa.id, pa.flags,
845 pa.accuracy, pa.text, pa.postalText, pa.postalCode,
846 pa.localityId, pa.subAdministrativeAreaId,
847 pa.administrativeAreaId, pa.countryId,
848 pa.latitude, pa.longitude, pa.north, pa.south, pa.east, pa.west,
849 pa.pub, pa.comment,
850 gl.name AS locality, gs.name AS subAdministrativeArea,
851 ga.name AS administrativeArea, gc.countryFR AS country
852 FROM profile_addresses AS pa
853 LEFT JOIN geoloc_localities AS gl ON (gl.id = pa.localityId)
854 LEFT JOIN geoloc_administrativeareas AS ga ON (ga.id = pa.administrativeAreaId)
855 LEFT JOIN geoloc_subadministrativeareas AS gs ON (gs.id = pa.subAdministrativeAreaId)
856 LEFT JOIN geoloc_countries AS gc ON (gc.iso_3166_1_a2 = pa.countryId)
323ac187 857 ' . ((count($where) > 0) ? 'WHERE ' . implode(' AND ', $where) : '') . '
eb54852e
SJ
858 ORDER BY pa.pid, pa.jobid, pa.id';
859 $this->dbiter = XDB::iterator($sql);
860 }
861
862 public function next()
863 {
864 if (is_null($this->dbiter)) {
865 return null;
866 }
867 $data = $this->dbiter->next();
868 if (is_null($data)) {
869 return null;
870 }
871 // Adds phones to addresses.
872 $it = Phone::iterate(array($data['pid']), array(Phone::LINK_ADDRESS), array($data['id']));
873 while ($phone = $it->next()) {
874 $data['phones'][$phone->id()] = $phone->toFormArray();
875 }
876 return new Address($data);
877 }
878
879 public function total()
880 {
881 return $this->dbiter->total();
882 }
883
884 public function first()
885 {
886 return $this->dbiter->first();
887 }
888
889 public function last()
890 {
891 return $this->dbiter->last();
892 }
893
894 public function value()
895 {
896 return $this->dbiter;
897 }
898}
899
900// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
901?>