Adds possibility to add a flag to an address.
[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
24ede2d2
SJ
376 public function addFlag($flag)
377 {
378 $this->flags->addFlag($flag);
379 }
380
f5f68e07
SJ
381 /** Auxilary function for formatting postal addresses.
382 * If the needle is found in the haystack, it notifies the substitution's
383 * success, modifies the length accordingly and returns either the matching
384 * substitution or the needle.
385 */
9070ff05 386 private function substitute($needle, $haystack, &$length, &$success, $trim = false)
f5f68e07
SJ
387 {
388 if (array_key_exists($needle, $haystack)) {
389 $success = true;
390 $length -= (strlen($needle) - strlen($haystack[$needle]));
391 return $haystack[$needle];
9070ff05
SJ
392 } elseif ($trim) {
393 $success = true;
394 if (strlen($needle) > 4) {
395 $length -= (strlen($needle) - 4);
396 $needle = $needle{4};
397 }
f5f68e07
SJ
398 }
399 return $needle;
400 }
401
402 /** Checks if the line corresponds to a French street line.
403 * A line is considered a French street line if it starts by between 1 and 4 numbers.
404 */
405 private function isStreetFR($line)
406 {
407 return preg_match('/^\d{1,4}\D/', $line);
408 }
409
410 /** Retrieves a French street number and slit the rest of the line into an array.
411 * @param $words: array containing the rest of the line (a word per cell).
412 * @param $line: line to consider.
413 * Returns the street number.
414 */
415 private function getStreetNumberFR(&$line)
416 {
417 // First we define numbers and separators.
418 $numberReq = '(\d{1,4})\s*(BIS|TER|QUATER|[A-Z])?';
cce807c5
SJ
419 $separatorReq = '\s*(?:\\|-|&|A|ET)?\s*';
420
421 // Then we retrieve the number(s) and the rest of the line.
422 // $matches contains:
423 // -0: the full patern, here the given line,
424 // -1: the number,
425 // -2: its optionnal quantifier,
426 // -3: an optionnal second number,
427 // -4: the second number's optionnal quantifier,
428 // -5: the rest of the line.
429 preg_match('/^' . $numberReq . '(?:' . $separatorReq . $numberReq . ')?\s+(.*)/', $line, $matches);
f5f68e07
SJ
430 $number = $matches[1];
431 $line = $matches[5];
432
433 // If there is a precision on the address, we concatenate it to the number.
434 if ($matches[2] != '') {
cce807c5 435 $number .= $matches[2]{0};
f5f68e07 436 } elseif ($matches[4] != '') {
cce807c5 437 $number .= $matches[4]{0};
f5f68e07
SJ
438 }
439
440 return $number;
441 }
442
443 /** Checks if the line corresponds to a French locality line.
444 * A line is considered a French locality line if it starts by exactly a
445 * postal code of exactly 5 numbers.
446 */
447 private function isLocalityFR($line)
448 {
449 return preg_match('/^\d{5}\D/', $line);
450 }
451
452 /** Retrieves a French postal code and slit the rest of the line into an array.
453 * @param $words: array containing the rest of the line (a word per cell).
454 * @param $line: line to consider.
455 * Returns the postal code, and cuts it out from the line.
456 */
457 private function getPostalCodeFR(&$line)
458 {
459 $number = substr($line, 0, 5);
460 $line = trim(substr($line, 5));
461 return $number;
462 }
463
464 /** Returns the address formated for French postal use (cf AFNOR XPZ 10-011).
465 * A postal addresse containts at most 6 lines of at most 38 characters each:
466 * - addressee's identification ("MONSIEUR JEAN DURAND", "DURAND SA"…),
467 * - delivery point identification ("CHEZ TOTO APPARTEMENT 2", "SERVICE ACHAT"…),
468 * - building localisation complement ("ENTREE A BATIMENT DES JONQUILLES", "ZONE INDUSTRIELLE OUEST"…),
469 * - N° and street name ("25 RUE DES FLEURS", "LES VIGNES"…),
470 * - delivery service, street localisation complement ("BP 40122", "BP 40112 AREYRES"…),
471 * - postal code and locality or cedex code and cedex ("33500 LIBOURNE", "33506 LIBOURNE CEDEX"…).
472 * Punctuation must be removed, all leters must be uppercased.
473 * Both locality and street name must not take more than 32 characters.
474 *
475 * @param $arrayText: array containing the address to be formated, one
476 * address line per array line.
477 * @param $count: array size.
478 */
cce807c5 479 private function formatPostalAddressFR($arrayText)
f5f68e07
SJ
480 {
481 // First removes country if any.
cce807c5 482 $count = count($arrayText);
f5f68e07
SJ
483 if ($arrayText[$count - 1] == 'FRANCE') {
484 unset($arrayText[$count - 1]);
485 --$count;
486 }
487
488 // All the lines must have less than 38 characters but street and
489 // locality lines whose limit is 32 characters.
490 foreach ($arrayText as $lineNumber => $line) {
491 if ($isStreetLine = $this->isStreetFR($line)) {
492 $formattedLine = $this->getStreetNumberFR($line) . ' ';
493 $limit = 32;
494 } elseif ($this->isLocalityFR($line)) {
495 $formattedLine = $this->getPostalCodeFR($line) . ' ';
496 $limit = 32;
59e8fb00 497 } else {
f5f68e07
SJ
498 $formattedLine = '';
499 $limit = 38;
500 }
501
502 $words = explode(' ', $line);
503 $count = count($words);
504 $length = $count - 1;
505 foreach ($words as $word) {
506 $length += strlen($word);
59e8fb00 507 }
f5f68e07
SJ
508
509 // Checks is length is ok. Otherwise, we try to shorten words and
510 // update the length of the current line accordingly.
511 for ($i = 0; $i < $count && $length > $limit; ++$i) {
512 $success = false;
513 if ($isStreetLine) {
9070ff05 514 $sub = $this->substitute($words[$i], Address::$streetAbbreviations, $length, $success, ($i == 0));
f5f68e07
SJ
515 }
516 // Entreprises' substitution are only suitable for the first two lines.
517 if ($lineNumber <= 2 && !$success) {
518 $sub = $this->substitute($words[$i], Address::$entrepriseAbbreviations, $length, $success);
519 }
520 if (!$success) {
521 $sub = $this->substitute($words[$i], Address::$otherAbbreviations, $length, $success);
522 }
523
524 $formattedLine .= $sub . ' ';
525 }
526 for (; $i < $count; ++$i) {
527 $formattedLine .= $words[$i] . ' ';
528 }
529 $arrayText[$lineNumber] = trim($formattedLine);
59e8fb00 530 }
f5f68e07
SJ
531
532 return implode("\n", $arrayText);
533 }
534
535 // Formats postal addresses.
536 // First erases punctuation, accents… Then uppercase the address and finally
537 // calls the country's dedicated formatting function.
538 public function formatPostalAddress()
539 {
540 // Performs rough formatting.
541 $text = mb_strtoupper(replace_accent($this->text));
542 $text = str_replace(array(',', ';', '.', ':', '!', '?', '"', '«', '»'), '', $text);
543 $text = preg_replace('/( |\t)+/', ' ', $text);
544 $arrayText = explode("\n", $text);
545 $arrayText = array_map('trim', $arrayText);
546
547 // Search for country.
548 $countries = DirEnum::getOptions(DirEnum::COUNTRIES);
549 $countries = array_map('replace_accent', $countries);
550 $countries = array_map('strtoupper', $countries);
551 $count = count($arrayText);
b925fb20
SJ
552 if (in_array(strtoupper($this->countryId), Address::$formattings)) {
553 $text = call_user_func(array($this, 'formatPostalAddress' . strtoupper($this->countryId)), $arrayText);
f5f68e07 554 } elseif (array_key_exists($arrayText[$count - 1], Address::$formattings)) {
cce807c5 555 $text = call_user_func(array($this, 'formatPostalAddress' . Address::$formattings[$arrayText[$count - 1]]), $arrayText);
f5f68e07 556 } elseif (!in_array($arrayText[$count - 1], $countries)) {
cce807c5 557 $text = $this->formatPostalAddressFR($arrayText);
f5f68e07
SJ
558 } else {
559 $text = implode("\n", $arrayText);
560 }
561
562 $this->postalText = $text;
59e8fb00
SJ
563 }
564
eb54852e
SJ
565 public function format(array $format = array())
566 {
567 if (empty($format)) {
568 $format['requireGeocoding'] = false;
569 $format['stripGeocoding'] = false;
59e8fb00 570 $format['postalText'] = false;
9783cb98
SJ
571 } else {
572 foreach (array('requireGeocoding', 'stripGeocoding', 'postalText') as $type) {
573 $format[$type] = (isset($format[$type])) ? $format[$type] : false;
574 }
eb54852e
SJ
575 }
576 $this->text = trim($this->text);
577 if ($this->removed == 1) {
578 $this->text = '';
579 return true;
580 }
581
eb54852e
SJ
582 if ($format['requireGeocoding'] || $this->changed == 1) {
583 $gmapsGeocoder = new GMapsGeocoder();
584 $gmapsGeocoder->getGeocodedAddress($this);
585 $this->changed = 0;
586 $this->error = !empty($this->geocodedText);
587 }
588 if ($format['stripGeocoding'] || ($this->type == self::LINK_COMPANY && $this->error) || $this->geocodeChosen === '0') {
589 $gmapsGeocoder = new GMapsGeocoder();
590 $gmapsGeocoder->stripGeocodingFromAddress($this);
591 if ($this->geocodeChosen === '0') {
592 $mailer = new PlMailer('profile/geocoding.mail.tpl');
593 $mailer->assign('text', $this->text);
594 $mailer->assign('geoloc', $this->geocodedText);
595 $mailer->send();
596 }
597 }
f2ac8f49
SJ
598 if ($this->countryId == '') {
599 $this->countryId = null;
600 }
eb54852e 601 $this->geocodeChosen = null;
e7027060 602 $this->phones = Phone::formatFormArray($this->phones, $this->error, new ProfileVisibility($this->pub));
f5f68e07
SJ
603 if ($format['postalText']) {
604 $this->formatPostalAddress();
605 }
eb54852e
SJ
606 return !$this->error;
607 }
608
609 public function toFormArray()
610 {
611 $address = array(
612 'accuracy' => $this->accuracy,
613 'text' => $this->text,
614 'postalText' => $this->postalText,
615 'postalCode' => $this->postalCode,
616 'localityId' => $this->localityId,
617 'subAdministrativeAreaId' => $this->subAdministrativeAreaId,
618 'administrativeAreaId' => $this->administrativeAreaId,
619 'countryId' => $this->countryId,
620 'localityName' => $this->localityName,
621 'subAdministrativeAreaName' => $this->subAdministrativeAreaName,
622 'administrativeAreaName' => $this->administrativeAreaName,
623 'latitude' => $this->latitude,
624 'longitude' => $this->longitude,
625 'north' => $this->north,
626 'south' => $this->south,
627 'east' => $this->east,
628 'west' => $this->west,
629 'error' => $this->error,
630 'changed' => $this->changed,
631 'removed' => $this->removed,
632 );
633 if (!is_null($this->geocodedText)) {
634 $address['geocodedText'] = $this->geocodedText;
eb54852e
SJ
635 $address['geocodeChosen'] = $this->geocodeChosen;
636 }
637
638 if ($this->type == self::LINK_PROFILE || $this->type == self::LINK_JOB) {
639 $address['pub'] = $this->pub;
640 }
641 if ($this->type == self::LINK_PROFILE) {
baee0f5a 642 static $flags = array('current', 'temporary', 'secondary', 'mail', 'cedex', 'deliveryIssue');
eb54852e
SJ
643
644 foreach ($flags as $flag) {
645 $address[$flag] = $this->flags->hasFlag($flag);
646 }
647 $address['comment'] = $this->comment;
648 $address['phones'] = Phone::formatFormArray($this->phones);
649 }
650
651 return $address;
652 }
653
654 private function toString()
655 {
14aba233 656 $address = $this->text;
eb54852e 657 if ($this->type == self::LINK_PROFILE || $this->type == self::LINK_JOB) {
14aba233
SJ
658 static $pubs = array('public' => 'publique', 'ax' => 'annuaire AX', 'private' => 'privé');
659 $address .= ' (affichage ' . $pubs[$this->pub];
eb54852e
SJ
660 }
661 if ($this->type == self::LINK_PROFILE) {
662 static $flags = array(
baee0f5a
SJ
663 'current' => 'actuelle',
664 'temporary' => 'temporaire',
665 'secondary' => 'secondaire',
666 'mail' => 'conctactable par courier',
667 'deliveryIssue' => 'n\'habite pas à l\'adresse indiquée',
668 'cedex' => 'type cédex',
eb54852e
SJ
669 );
670
14aba233
SJ
671 if (!$this->flags->hasFlag('temporary')) {
672 $address .= ', permanente';
673 }
674 if (!$this->flags->hasFlag('secondary')) {
675 $address .= ', principale';
676 }
eb54852e
SJ
677 foreach ($flags as $flag => $flagName) {
678 if ($this->flags->hasFlag($flag)) {
679 $address .= ', ' . $flagName;
680 }
681 }
14aba233
SJ
682 if ($this->comment) {
683 $address .= ', commentaire : ' . $this->comment;
684 }
eb54852e
SJ
685 if ($phones = Phone::formArrayToString($this->phones)) {
686 $address .= ', ' . $phones;
687 }
14aba233
SJ
688 } elseif ($this->type == self::LINK_JOB) {
689 $address .= ')';
eb54852e
SJ
690 }
691 return $address;
692 }
693
694 private function isEmpty()
695 {
696 return (!$this->text || $this->text == '');
697 }
698
699 public function save()
700 {
701 static $areas = array('administrativeArea', 'subAdministrativeArea', 'locality');
702
739c49b5 703 $this->format(array('postalText' => true));
eb54852e 704 if (!$this->isEmpty()) {
eb54852e
SJ
705 foreach ($areas as $area) {
706 Geocoder::getAreaId($this, $area);
707 }
708
709 XDB::execute('INSERT INTO profile_addresses (pid, jobid, type, id, flags, accuracy,
710 text, postalText, postalCode, localityId,
711 subAdministrativeAreaId, administrativeAreaId,
51d30e26 712 countryId, latitude, longitude, pub, comment,
eb54852e 713 north, south, east, west)
51d30e26
SJ
714 VALUES ({?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?},
715 {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?})',
eb54852e
SJ
716 $this->pid, $this->jobid, $this->type, $this->id, $this->flags, $this->accuracy,
717 $this->text, $this->postalText, $this->postalCode, $this->localityId,
718 $this->subAdministrativeAreaId, $this->administrativeAreaId,
719 $this->countryId, $this->latitude, $this->longitude,
51d30e26 720 $this->pub, $this->comment,
eb54852e
SJ
721 $this->north, $this->south, $this->east, $this->west);
722
723 if ($this->type == self::LINK_PROFILE) {
724 Phone::savePhones($this->phones, $this->pid, Phone::LINK_ADDRESS, $this->id);
725 }
726 }
727 }
728
781a24bc
SJ
729 public function delete()
730 {
731 XDB::execute('DELETE FROM profile_addresses
732 WHERE pid = {?} AND jobid = {?} AND type = {?} AND id = {?}',
733 $this->pid, $this->jobid, $this->type, $this->id);
734 }
735
6592a264 736 static public function deleteAddresses($pid, $type, $jobid = null, $deletePrivate = true)
eb54852e
SJ
737 {
738 $where = '';
739 if (!is_null($pid)) {
740 $where = XDB::format(' AND pid = {?}', $pid);
741 }
742 if (!is_null($jobid)) {
743 $where = XDB::format(' AND jobid = {?}', $jobid);
744 }
745 XDB::execute('DELETE FROM profile_addresses
6592a264 746 WHERE type = {?}' . $where . (($deletePrivate) ? '' : ' AND pub IN (\'public\', \'ax\')'),
eb54852e
SJ
747 $type);
748 if ($type == self::LINK_PROFILE) {
6592a264 749 Phone::deletePhones($pid, Phone::LINK_ADDRESS, null, $deletePrivate);
eb54852e
SJ
750 }
751 }
752
753 /** Saves addresses into the database.
754 * @param $data: an array of form formatted addresses.
755 * @param $pid, $type, $linkid: pid, type and id concerned by the update.
756 */
757 static public function saveFromArray(array $data, $pid, $type = self::LINK_PROFILE, $linkid = null)
758 {
759 foreach ($data as $id => $value) {
760 if (!is_null($linkid)) {
761 $value['id'] = $linkid;
762 } else {
763 $value['id'] = $id;
764 }
765 if (!is_null($pid)) {
766 $value['pid'] = $pid;
767 }
768 if (!is_null($type)) {
769 $value['type'] = $type;
770 }
771 $address = new Address($value);
772 $address->save();
773 }
774 }
775
776 static private function formArrayWalk(array $data, $function, &$success = true, $requiresEmptyAddress = false)
777 {
778 $addresses = array();
779 foreach ($data as $item) {
780 $address = new Address($item);
781 $success = ($address->format() && $success);
782 if (!$address->isEmpty()) {
783 $addresses[] = call_user_func(array($address, $function));
784 }
785 }
786 if (count($address) == 0 && $requiresEmptyAddress) {
787 $address = new Address();
788 $addresses[] = call_user_func(array($address, $function));
789 }
790 return $addresses;
791 }
792
793 // Formats an array of form addresses into an array of form formatted addresses.
794 static public function formatFormArray(array $data, &$success = true)
795 {
796 // Only a single address can be the profile's current address and she must have one.
797 $hasCurrent = false;
798 foreach ($data as $key => &$address) {
799 if (isset($address['current']) && $address['current']) {
800 if ($hasCurrent) {
801 $address['current'] = false;
802 } else {
803 $hasCurrent = true;
804 }
805 }
806 }
807 if (!$hasCurrent && count($value) > 0) {
808 foreach ($value as &$address) {
809 $address['current'] = true;
810 break;
811 }
812 }
813
814 return self::formArrayWalk($data, 'toFormArray', $success, true);
815 }
816
817 static public function formArrayToString(array $data)
818 {
14aba233 819 return implode(', ', self::formArrayWalk($data, 'toString'));
eb54852e
SJ
820 }
821
822 static public function iterate(array $pids = array(), array $types = array(),
823 array $jobids = array(), array $pubs = array())
824 {
825 return new AddressIterator($pids, $types, $jobids, $pubs);
826 }
827}
828
829/** Iterator over a set of Phones
830 *
831 * @param $pid, $type, $jobid, $pub
832 *
833 * The iterator contains the phones that correspond to the value stored in the
834 * parameters' arrays.
835 */
836class AddressIterator implements PlIterator
837{
838 private $dbiter;
839
840 public function __construct(array $pids, array $types, array $jobids, array $pubs)
841 {
842 $where = array();
843 if (count($pids) != 0) {
844 $where[] = XDB::format('(pa.pid IN {?})', $pids);
845 }
846 if (count($types) != 0) {
847 $where[] = XDB::format('(pa.type IN {?})', $types);
848 }
849 if (count($jobids) != 0) {
850 $where[] = XDB::format('(pa.jobid IN {?})', $jobids);
851 }
852 if (count($pubs) != 0) {
853 $where[] = XDB::format('(pa.pub IN {?})', $pubs);
854 }
855 $sql = 'SELECT pa.pid, pa.jobid, pa.type, pa.id, pa.flags,
856 pa.accuracy, pa.text, pa.postalText, pa.postalCode,
857 pa.localityId, pa.subAdministrativeAreaId,
858 pa.administrativeAreaId, pa.countryId,
859 pa.latitude, pa.longitude, pa.north, pa.south, pa.east, pa.west,
860 pa.pub, pa.comment,
861 gl.name AS locality, gs.name AS subAdministrativeArea,
862 ga.name AS administrativeArea, gc.countryFR AS country
863 FROM profile_addresses AS pa
864 LEFT JOIN geoloc_localities AS gl ON (gl.id = pa.localityId)
865 LEFT JOIN geoloc_administrativeareas AS ga ON (ga.id = pa.administrativeAreaId)
866 LEFT JOIN geoloc_subadministrativeareas AS gs ON (gs.id = pa.subAdministrativeAreaId)
867 LEFT JOIN geoloc_countries AS gc ON (gc.iso_3166_1_a2 = pa.countryId)
323ac187 868 ' . ((count($where) > 0) ? 'WHERE ' . implode(' AND ', $where) : '') . '
eb54852e
SJ
869 ORDER BY pa.pid, pa.jobid, pa.id';
870 $this->dbiter = XDB::iterator($sql);
871 }
872
873 public function next()
874 {
875 if (is_null($this->dbiter)) {
876 return null;
877 }
878 $data = $this->dbiter->next();
879 if (is_null($data)) {
880 return null;
881 }
882 // Adds phones to addresses.
883 $it = Phone::iterate(array($data['pid']), array(Phone::LINK_ADDRESS), array($data['id']));
884 while ($phone = $it->next()) {
885 $data['phones'][$phone->id()] = $phone->toFormArray();
886 }
887 return new Address($data);
888 }
889
890 public function total()
891 {
892 return $this->dbiter->total();
893 }
894
895 public function first()
896 {
897 return $this->dbiter->first();
898 }
899
900 public function last()
901 {
902 return $this->dbiter->last();
903 }
904
905 public function value()
906 {
907 return $this->dbiter;
908 }
909}
910
911// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
912?>