Fixes unset viriables warnings.
[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
52 // Primary key fields: the quadruplet ($pid, $jobid, $type, $id) defines a unique address.
53 public $pid = 0;
54 public $jobid = 0;
55 public $type = Address::LINK_PROFILE;
56 public $id = 0;
57
58 // Geocoding fields.
59 public $accuracy = 0;
60 public $text = '';
61 public $postalText = '';
62 public $postalCode = null;
63 public $localityId = null;
64 public $subAdministrativeAreaId = null;
65 public $administrativeAreaId = null;
66 public $localityName = null;
67 public $subAdministrativeAreaName = null;
68 public $administrativeAreaName = null;
69 public $countryId = null;
70 public $latitude = null;
71 public $longitude = null;
72 public $north = null;
73 public $south = null;
74 public $east = null;
75 public $west = null;
76 public $geocodedText = null;
eb54852e
SJ
77 public $geocodeChosen = null;
78
79 // Database's field required for both 'home' and 'job' addresses.
5f096c57 80 public $pub = 'ax';
eb54852e
SJ
81
82 // Database's fields required for 'home' addresses.
baee0f5a 83 public $flags = null; // 'current', 'temporary', 'secondary', 'mail', 'cedex', 'deliveryIssue'
eb54852e
SJ
84 public $comment = null;
85 public $current = null;
86 public $temporary = null;
87 public $secondary = null;
88 public $mail = null;
baee0f5a 89 public $deliveryIssue = null;
eb54852e
SJ
90
91 // Remaining fields that do not belong to profile_addresses.
92 public $phones = array();
93 public $error = false;
94 public $changed = 0;
95 public $removed = 0;
96
97 public function __construct(array $data = array())
98 {
99 if (count($data) > 0) {
100 foreach ($data as $key => $val) {
101 $this->$key = $val;
102 }
103 }
104
545bc699
FB
105 if (!is_null($this->flags)) {
106 $this->flags = new PlFlagSet($this->flags);
107 } else {
baee0f5a 108 static $flags = array('current', 'temporary', 'secondary', 'mail', 'deliveryIssue');
eb54852e 109
545bc699
FB
110 $this->flags = new PlFlagSet();
111 foreach ($flags as $flag) {
112 if (!is_null($this->$flag) && ($this->$flag == 1 || $this->$flag == 'on')) {
113 $this->flags->addFlag($flag, 1);
114 $this->$flag = null;
eb54852e 115 }
545bc699
FB
116 $this->flags->addFlag('cedex', (strpos(strtoupper(preg_replace(array("/[0-9,\"'#~:;_\- ]/", "/\r\n/"),
117 array('', "\n"), $this->text)), 'CEDEX')) !== false);
eb54852e
SJ
118 }
119 }
120 }
121
323ac187
SJ
122 public function setId($id)
123 {
124 $this->id = $id;
125 }
126
eb54852e
SJ
127 public function phones()
128 {
129 return $this->phones;
130 }
131
132 public function addPhone(Phone &$phone)
133 {
134 if ($phone->linkType() == Phone::LINK_ADDRESS && $phone->pid() == $this->pid) {
135 $this->phones[$phone->uniqueId()] = $phone;
136 }
137 }
138
139 public function hasFlag($flag)
140 {
622e7063 141 return ($this->flags != null && $this->flags->hasFlag($flag));
eb54852e
SJ
142 }
143
59e8fb00
SJ
144 // Returns the address formated for postal use.
145 // The main rules are (cf AFNOR XPZ 10-011):
146 // -everything in upper case;
147 // -if there are more then than 38 characters in a line, split it;
148 // -if there are more then than 32 characters in the description of the "street", use abbreviations.
149 public function formatPostalAddress() {
150 static $abbreviations = array(
151 'IMPASSE' => 'IMP',
152 'RUE' => 'R',
153 'AVENUE' => 'AV',
154 'BOULEVARD' => 'BVD',
155 'ROUTE' => 'R',
156 'STREET' => 'ST',
157 'ROAD' => 'RD',
158 );
159
160 $text = strtoupper($text);
161 $arrayText = explode("\n", $text);
162 $postalText = '';
163
164 foreach ($arrayText as $i => $line) {
165 $postalText .= (($i == 0) ? '' : "\n");
166 if (($length = strlen($line)) > 32) {
167 $words = explode(' ', $line);
168 $count = 0;
169 foreach ($words as $word) {
170 if (isset($abbreviations[$word])) {
171 $word = $abbreviations[$word];
172 }
173 if ($count + ($wordLength = strlen($word)) <= 38) {
174 $postalText .= (($count == 0) ? '' : ' ') . $word;
175 $count += (($count == 0) ? 0 : 1) + $wordLength;
176 } else {
177 $postalText .= "\n" . $word;
178 $count = strlen($word);
179 }
180 }
181 } else {
182 $postalText .= $line;
183 }
184 }
185 $this->postalText = $postalText;
186 }
187
eb54852e
SJ
188 public function format(array $format = array())
189 {
190 if (empty($format)) {
191 $format['requireGeocoding'] = false;
192 $format['stripGeocoding'] = false;
59e8fb00 193 $format['postalText'] = false;
9783cb98
SJ
194 } else {
195 foreach (array('requireGeocoding', 'stripGeocoding', 'postalText') as $type) {
196 $format[$type] = (isset($format[$type])) ? $format[$type] : false;
197 }
eb54852e
SJ
198 }
199 $this->text = trim($this->text);
200 if ($this->removed == 1) {
201 $this->text = '';
202 return true;
203 }
204
eb54852e
SJ
205 if ($format['requireGeocoding'] || $this->changed == 1) {
206 $gmapsGeocoder = new GMapsGeocoder();
207 $gmapsGeocoder->getGeocodedAddress($this);
208 $this->changed = 0;
209 $this->error = !empty($this->geocodedText);
210 }
211 if ($format['stripGeocoding'] || ($this->type == self::LINK_COMPANY && $this->error) || $this->geocodeChosen === '0') {
212 $gmapsGeocoder = new GMapsGeocoder();
213 $gmapsGeocoder->stripGeocodingFromAddress($this);
214 if ($this->geocodeChosen === '0') {
215 $mailer = new PlMailer('profile/geocoding.mail.tpl');
216 $mailer->assign('text', $this->text);
217 $mailer->assign('geoloc', $this->geocodedText);
218 $mailer->send();
219 }
220 }
59e8fb00
SJ
221 if ($format['postalText']) {
222 $this->formatPostalAddress();
223 }
f2ac8f49
SJ
224 if ($this->countryId == '') {
225 $this->countryId = null;
226 }
eb54852e
SJ
227 $this->geocodeChosen = null;
228 $this->phones = Phone::formatFormArray($this->phones, $this->error);
229 return !$this->error;
230 }
231
232 public function toFormArray()
233 {
234 $address = array(
235 'accuracy' => $this->accuracy,
236 'text' => $this->text,
237 'postalText' => $this->postalText,
238 'postalCode' => $this->postalCode,
239 'localityId' => $this->localityId,
240 'subAdministrativeAreaId' => $this->subAdministrativeAreaId,
241 'administrativeAreaId' => $this->administrativeAreaId,
242 'countryId' => $this->countryId,
243 'localityName' => $this->localityName,
244 'subAdministrativeAreaName' => $this->subAdministrativeAreaName,
245 'administrativeAreaName' => $this->administrativeAreaName,
246 'latitude' => $this->latitude,
247 'longitude' => $this->longitude,
248 'north' => $this->north,
249 'south' => $this->south,
250 'east' => $this->east,
251 'west' => $this->west,
252 'error' => $this->error,
253 'changed' => $this->changed,
254 'removed' => $this->removed,
255 );
256 if (!is_null($this->geocodedText)) {
257 $address['geocodedText'] = $this->geocodedText;
eb54852e
SJ
258 $address['geocodeChosen'] = $this->geocodeChosen;
259 }
260
261 if ($this->type == self::LINK_PROFILE || $this->type == self::LINK_JOB) {
262 $address['pub'] = $this->pub;
263 }
264 if ($this->type == self::LINK_PROFILE) {
baee0f5a 265 static $flags = array('current', 'temporary', 'secondary', 'mail', 'cedex', 'deliveryIssue');
eb54852e
SJ
266
267 foreach ($flags as $flag) {
268 $address[$flag] = $this->flags->hasFlag($flag);
269 }
270 $address['comment'] = $this->comment;
271 $address['phones'] = Phone::formatFormArray($this->phones);
272 }
273
274 return $address;
275 }
276
277 private function toString()
278 {
279 $address = 'Adresse : ' . $this->text;
280 if ($this->type == self::LINK_PROFILE || $this->type == self::LINK_JOB) {
281 $address .= ', affichage : ' . $this->pub;
282 }
283 if ($this->type == self::LINK_PROFILE) {
284 static $flags = array(
baee0f5a
SJ
285 'current' => 'actuelle',
286 'temporary' => 'temporaire',
287 'secondary' => 'secondaire',
288 'mail' => 'conctactable par courier',
289 'deliveryIssue' => 'n\'habite pas à l\'adresse indiquée',
290 'cedex' => 'type cédex',
eb54852e
SJ
291 );
292
293 $address .= ', commentaire : ' . $this->comment;
294 foreach ($flags as $flag => $flagName) {
295 if ($this->flags->hasFlag($flag)) {
296 $address .= ', ' . $flagName;
297 }
298 }
299 if ($phones = Phone::formArrayToString($this->phones)) {
300 $address .= ', ' . $phones;
301 }
302 }
303 return $address;
304 }
305
306 private function isEmpty()
307 {
308 return (!$this->text || $this->text == '');
309 }
310
311 public function save()
312 {
313 static $areas = array('administrativeArea', 'subAdministrativeArea', 'locality');
314
739c49b5 315 $this->format(array('postalText' => true));
eb54852e 316 if (!$this->isEmpty()) {
eb54852e
SJ
317 foreach ($areas as $area) {
318 Geocoder::getAreaId($this, $area);
319 }
320
321 XDB::execute('INSERT INTO profile_addresses (pid, jobid, type, id, flags, accuracy,
322 text, postalText, postalCode, localityId,
323 subAdministrativeAreaId, administrativeAreaId,
51d30e26 324 countryId, latitude, longitude, pub, comment,
eb54852e 325 north, south, east, west)
51d30e26
SJ
326 VALUES ({?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?},
327 {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?})',
eb54852e
SJ
328 $this->pid, $this->jobid, $this->type, $this->id, $this->flags, $this->accuracy,
329 $this->text, $this->postalText, $this->postalCode, $this->localityId,
330 $this->subAdministrativeAreaId, $this->administrativeAreaId,
331 $this->countryId, $this->latitude, $this->longitude,
51d30e26 332 $this->pub, $this->comment,
eb54852e
SJ
333 $this->north, $this->south, $this->east, $this->west);
334
335 if ($this->type == self::LINK_PROFILE) {
336 Phone::savePhones($this->phones, $this->pid, Phone::LINK_ADDRESS, $this->id);
337 }
338 }
339 }
340
781a24bc
SJ
341 public function delete()
342 {
343 XDB::execute('DELETE FROM profile_addresses
344 WHERE pid = {?} AND jobid = {?} AND type = {?} AND id = {?}',
345 $this->pid, $this->jobid, $this->type, $this->id);
346 }
347
348 static public function deleteAddresses($pid, $type, $jobid = null)
eb54852e
SJ
349 {
350 $where = '';
351 if (!is_null($pid)) {
352 $where = XDB::format(' AND pid = {?}', $pid);
353 }
354 if (!is_null($jobid)) {
355 $where = XDB::format(' AND jobid = {?}', $jobid);
356 }
357 XDB::execute('DELETE FROM profile_addresses
358 WHERE type = {?}' . $where,
359 $type);
360 if ($type == self::LINK_PROFILE) {
361 Phone::deletePhones($pid, Phone::LINK_ADDRESS);
362 }
363 }
364
365 /** Saves addresses into the database.
366 * @param $data: an array of form formatted addresses.
367 * @param $pid, $type, $linkid: pid, type and id concerned by the update.
368 */
369 static public function saveFromArray(array $data, $pid, $type = self::LINK_PROFILE, $linkid = null)
370 {
371 foreach ($data as $id => $value) {
372 if (!is_null($linkid)) {
373 $value['id'] = $linkid;
374 } else {
375 $value['id'] = $id;
376 }
377 if (!is_null($pid)) {
378 $value['pid'] = $pid;
379 }
380 if (!is_null($type)) {
381 $value['type'] = $type;
382 }
383 $address = new Address($value);
384 $address->save();
385 }
386 }
387
388 static private function formArrayWalk(array $data, $function, &$success = true, $requiresEmptyAddress = false)
389 {
390 $addresses = array();
391 foreach ($data as $item) {
392 $address = new Address($item);
393 $success = ($address->format() && $success);
394 if (!$address->isEmpty()) {
395 $addresses[] = call_user_func(array($address, $function));
396 }
397 }
398 if (count($address) == 0 && $requiresEmptyAddress) {
399 $address = new Address();
400 $addresses[] = call_user_func(array($address, $function));
401 }
402 return $addresses;
403 }
404
405 // Formats an array of form addresses into an array of form formatted addresses.
406 static public function formatFormArray(array $data, &$success = true)
407 {
408 // Only a single address can be the profile's current address and she must have one.
409 $hasCurrent = false;
410 foreach ($data as $key => &$address) {
411 if (isset($address['current']) && $address['current']) {
412 if ($hasCurrent) {
413 $address['current'] = false;
414 } else {
415 $hasCurrent = true;
416 }
417 }
418 }
419 if (!$hasCurrent && count($value) > 0) {
420 foreach ($value as &$address) {
421 $address['current'] = true;
422 break;
423 }
424 }
425
426 return self::formArrayWalk($data, 'toFormArray', $success, true);
427 }
428
429 static public function formArrayToString(array $data)
430 {
431 return implode(' ; ', self::formArrayWalk($data, 'toString'));
432 }
433
434 static public function iterate(array $pids = array(), array $types = array(),
435 array $jobids = array(), array $pubs = array())
436 {
437 return new AddressIterator($pids, $types, $jobids, $pubs);
438 }
439}
440
441/** Iterator over a set of Phones
442 *
443 * @param $pid, $type, $jobid, $pub
444 *
445 * The iterator contains the phones that correspond to the value stored in the
446 * parameters' arrays.
447 */
448class AddressIterator implements PlIterator
449{
450 private $dbiter;
451
452 public function __construct(array $pids, array $types, array $jobids, array $pubs)
453 {
454 $where = array();
455 if (count($pids) != 0) {
456 $where[] = XDB::format('(pa.pid IN {?})', $pids);
457 }
458 if (count($types) != 0) {
459 $where[] = XDB::format('(pa.type IN {?})', $types);
460 }
461 if (count($jobids) != 0) {
462 $where[] = XDB::format('(pa.jobid IN {?})', $jobids);
463 }
464 if (count($pubs) != 0) {
465 $where[] = XDB::format('(pa.pub IN {?})', $pubs);
466 }
467 $sql = 'SELECT pa.pid, pa.jobid, pa.type, pa.id, pa.flags,
468 pa.accuracy, pa.text, pa.postalText, pa.postalCode,
469 pa.localityId, pa.subAdministrativeAreaId,
470 pa.administrativeAreaId, pa.countryId,
471 pa.latitude, pa.longitude, pa.north, pa.south, pa.east, pa.west,
472 pa.pub, pa.comment,
473 gl.name AS locality, gs.name AS subAdministrativeArea,
474 ga.name AS administrativeArea, gc.countryFR AS country
475 FROM profile_addresses AS pa
476 LEFT JOIN geoloc_localities AS gl ON (gl.id = pa.localityId)
477 LEFT JOIN geoloc_administrativeareas AS ga ON (ga.id = pa.administrativeAreaId)
478 LEFT JOIN geoloc_subadministrativeareas AS gs ON (gs.id = pa.subAdministrativeAreaId)
479 LEFT JOIN geoloc_countries AS gc ON (gc.iso_3166_1_a2 = pa.countryId)
323ac187 480 ' . ((count($where) > 0) ? 'WHERE ' . implode(' AND ', $where) : '') . '
eb54852e
SJ
481 ORDER BY pa.pid, pa.jobid, pa.id';
482 $this->dbiter = XDB::iterator($sql);
483 }
484
485 public function next()
486 {
487 if (is_null($this->dbiter)) {
488 return null;
489 }
490 $data = $this->dbiter->next();
491 if (is_null($data)) {
492 return null;
493 }
494 // Adds phones to addresses.
495 $it = Phone::iterate(array($data['pid']), array(Phone::LINK_ADDRESS), array($data['id']));
496 while ($phone = $it->next()) {
497 $data['phones'][$phone->id()] = $phone->toFormArray();
498 }
499 return new Address($data);
500 }
501
502 public function total()
503 {
504 return $this->dbiter->total();
505 }
506
507 public function first()
508 {
509 return $this->dbiter->first();
510 }
511
512 public function last()
513 {
514 return $this->dbiter->last();
515 }
516
517 public function value()
518 {
519 return $this->dbiter;
520 }
521}
522
523// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
524?>