Do not force addresses to be translated into French.
[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;
77 public $geocodedPostalText = null;
78 public $geocodeChosen = null;
79
80 // Database's field required for both 'home' and 'job' addresses.
81 public $pub = 'private';
82
83 // Database's fields required for 'home' addresses.
84 public $flags = null; // 'current', 'temporary', 'secondary', 'mail', 'cedex'
85 public $comment = null;
86 public $current = null;
87 public $temporary = null;
88 public $secondary = null;
89 public $mail = null;
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 {
108 static $flags = array('current', 'temporary', 'secondary', 'mail');
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
144 public function format(array $format = array())
145 {
146 if (empty($format)) {
147 $format['requireGeocoding'] = false;
148 $format['stripGeocoding'] = false;
149 }
150 $this->text = trim($this->text);
151 if ($this->removed == 1) {
152 $this->text = '';
153 return true;
154 }
155
eb54852e
SJ
156 if ($format['requireGeocoding'] || $this->changed == 1) {
157 $gmapsGeocoder = new GMapsGeocoder();
158 $gmapsGeocoder->getGeocodedAddress($this);
159 $this->changed = 0;
160 $this->error = !empty($this->geocodedText);
161 }
162 if ($format['stripGeocoding'] || ($this->type == self::LINK_COMPANY && $this->error) || $this->geocodeChosen === '0') {
163 $gmapsGeocoder = new GMapsGeocoder();
164 $gmapsGeocoder->stripGeocodingFromAddress($this);
165 if ($this->geocodeChosen === '0') {
166 $mailer = new PlMailer('profile/geocoding.mail.tpl');
167 $mailer->assign('text', $this->text);
168 $mailer->assign('geoloc', $this->geocodedText);
169 $mailer->send();
170 }
171 }
f2ac8f49
SJ
172 if ($this->countryId == '') {
173 $this->countryId = null;
174 }
eb54852e
SJ
175 $this->geocodeChosen = null;
176 $this->phones = Phone::formatFormArray($this->phones, $this->error);
177 return !$this->error;
178 }
179
180 public function toFormArray()
181 {
182 $address = array(
183 'accuracy' => $this->accuracy,
184 'text' => $this->text,
185 'postalText' => $this->postalText,
186 'postalCode' => $this->postalCode,
187 'localityId' => $this->localityId,
188 'subAdministrativeAreaId' => $this->subAdministrativeAreaId,
189 'administrativeAreaId' => $this->administrativeAreaId,
190 'countryId' => $this->countryId,
191 'localityName' => $this->localityName,
192 'subAdministrativeAreaName' => $this->subAdministrativeAreaName,
193 'administrativeAreaName' => $this->administrativeAreaName,
194 'latitude' => $this->latitude,
195 'longitude' => $this->longitude,
196 'north' => $this->north,
197 'south' => $this->south,
198 'east' => $this->east,
199 'west' => $this->west,
200 'error' => $this->error,
201 'changed' => $this->changed,
202 'removed' => $this->removed,
203 );
204 if (!is_null($this->geocodedText)) {
205 $address['geocodedText'] = $this->geocodedText;
206 $address['geocodedPostalText'] = $this->geocodedPostalText;
207 $address['geocodeChosen'] = $this->geocodeChosen;
208 }
209
210 if ($this->type == self::LINK_PROFILE || $this->type == self::LINK_JOB) {
211 $address['pub'] = $this->pub;
212 }
213 if ($this->type == self::LINK_PROFILE) {
214 static $flags = array('current', 'temporary', 'secondary', 'mail', 'cedex');
215
216 foreach ($flags as $flag) {
217 $address[$flag] = $this->flags->hasFlag($flag);
218 }
219 $address['comment'] = $this->comment;
220 $address['phones'] = Phone::formatFormArray($this->phones);
221 }
222
223 return $address;
224 }
225
226 private function toString()
227 {
228 $address = 'Adresse : ' . $this->text;
229 if ($this->type == self::LINK_PROFILE || $this->type == self::LINK_JOB) {
230 $address .= ', affichage : ' . $this->pub;
231 }
232 if ($this->type == self::LINK_PROFILE) {
233 static $flags = array(
234 'current' => 'actuelle',
235 'temporary' => 'temporaire',
236 'secondary' => 'secondaire',
237 'mail' => 'conctactable par courier',
238 'cedex' => 'type cédex',
239 );
240
241 $address .= ', commentaire : ' . $this->comment;
242 foreach ($flags as $flag => $flagName) {
243 if ($this->flags->hasFlag($flag)) {
244 $address .= ', ' . $flagName;
245 }
246 }
247 if ($phones = Phone::formArrayToString($this->phones)) {
248 $address .= ', ' . $phones;
249 }
250 }
251 return $address;
252 }
253
254 private function isEmpty()
255 {
256 return (!$this->text || $this->text == '');
257 }
258
259 public function save()
260 {
261 static $areas = array('administrativeArea', 'subAdministrativeArea', 'locality');
262
263 $this->format();
264 if (!$this->isEmpty()) {
eb54852e
SJ
265 foreach ($areas as $area) {
266 Geocoder::getAreaId($this, $area);
267 }
268
269 XDB::execute('INSERT INTO profile_addresses (pid, jobid, type, id, flags, accuracy,
270 text, postalText, postalCode, localityId,
271 subAdministrativeAreaId, administrativeAreaId,
51d30e26 272 countryId, latitude, longitude, pub, comment,
eb54852e 273 north, south, east, west)
51d30e26
SJ
274 VALUES ({?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?},
275 {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?})',
eb54852e
SJ
276 $this->pid, $this->jobid, $this->type, $this->id, $this->flags, $this->accuracy,
277 $this->text, $this->postalText, $this->postalCode, $this->localityId,
278 $this->subAdministrativeAreaId, $this->administrativeAreaId,
279 $this->countryId, $this->latitude, $this->longitude,
51d30e26 280 $this->pub, $this->comment,
eb54852e
SJ
281 $this->north, $this->south, $this->east, $this->west);
282
283 if ($this->type == self::LINK_PROFILE) {
284 Phone::savePhones($this->phones, $this->pid, Phone::LINK_ADDRESS, $this->id);
285 }
286 }
287 }
288
781a24bc
SJ
289 public function delete()
290 {
291 XDB::execute('DELETE FROM profile_addresses
292 WHERE pid = {?} AND jobid = {?} AND type = {?} AND id = {?}',
293 $this->pid, $this->jobid, $this->type, $this->id);
294 }
295
296 static public function deleteAddresses($pid, $type, $jobid = null)
eb54852e
SJ
297 {
298 $where = '';
299 if (!is_null($pid)) {
300 $where = XDB::format(' AND pid = {?}', $pid);
301 }
302 if (!is_null($jobid)) {
303 $where = XDB::format(' AND jobid = {?}', $jobid);
304 }
305 XDB::execute('DELETE FROM profile_addresses
306 WHERE type = {?}' . $where,
307 $type);
308 if ($type == self::LINK_PROFILE) {
309 Phone::deletePhones($pid, Phone::LINK_ADDRESS);
310 }
311 }
312
313 /** Saves addresses into the database.
314 * @param $data: an array of form formatted addresses.
315 * @param $pid, $type, $linkid: pid, type and id concerned by the update.
316 */
317 static public function saveFromArray(array $data, $pid, $type = self::LINK_PROFILE, $linkid = null)
318 {
319 foreach ($data as $id => $value) {
320 if (!is_null($linkid)) {
321 $value['id'] = $linkid;
322 } else {
323 $value['id'] = $id;
324 }
325 if (!is_null($pid)) {
326 $value['pid'] = $pid;
327 }
328 if (!is_null($type)) {
329 $value['type'] = $type;
330 }
331 $address = new Address($value);
332 $address->save();
333 }
334 }
335
336 static private function formArrayWalk(array $data, $function, &$success = true, $requiresEmptyAddress = false)
337 {
338 $addresses = array();
339 foreach ($data as $item) {
340 $address = new Address($item);
341 $success = ($address->format() && $success);
342 if (!$address->isEmpty()) {
343 $addresses[] = call_user_func(array($address, $function));
344 }
345 }
346 if (count($address) == 0 && $requiresEmptyAddress) {
347 $address = new Address();
348 $addresses[] = call_user_func(array($address, $function));
349 }
350 return $addresses;
351 }
352
353 // Formats an array of form addresses into an array of form formatted addresses.
354 static public function formatFormArray(array $data, &$success = true)
355 {
356 // Only a single address can be the profile's current address and she must have one.
357 $hasCurrent = false;
358 foreach ($data as $key => &$address) {
359 if (isset($address['current']) && $address['current']) {
360 if ($hasCurrent) {
361 $address['current'] = false;
362 } else {
363 $hasCurrent = true;
364 }
365 }
366 }
367 if (!$hasCurrent && count($value) > 0) {
368 foreach ($value as &$address) {
369 $address['current'] = true;
370 break;
371 }
372 }
373
374 return self::formArrayWalk($data, 'toFormArray', $success, true);
375 }
376
377 static public function formArrayToString(array $data)
378 {
379 return implode(' ; ', self::formArrayWalk($data, 'toString'));
380 }
381
382 static public function iterate(array $pids = array(), array $types = array(),
383 array $jobids = array(), array $pubs = array())
384 {
385 return new AddressIterator($pids, $types, $jobids, $pubs);
386 }
387}
388
389/** Iterator over a set of Phones
390 *
391 * @param $pid, $type, $jobid, $pub
392 *
393 * The iterator contains the phones that correspond to the value stored in the
394 * parameters' arrays.
395 */
396class AddressIterator implements PlIterator
397{
398 private $dbiter;
399
400 public function __construct(array $pids, array $types, array $jobids, array $pubs)
401 {
402 $where = array();
403 if (count($pids) != 0) {
404 $where[] = XDB::format('(pa.pid IN {?})', $pids);
405 }
406 if (count($types) != 0) {
407 $where[] = XDB::format('(pa.type IN {?})', $types);
408 }
409 if (count($jobids) != 0) {
410 $where[] = XDB::format('(pa.jobid IN {?})', $jobids);
411 }
412 if (count($pubs) != 0) {
413 $where[] = XDB::format('(pa.pub IN {?})', $pubs);
414 }
415 $sql = 'SELECT pa.pid, pa.jobid, pa.type, pa.id, pa.flags,
416 pa.accuracy, pa.text, pa.postalText, pa.postalCode,
417 pa.localityId, pa.subAdministrativeAreaId,
418 pa.administrativeAreaId, pa.countryId,
419 pa.latitude, pa.longitude, pa.north, pa.south, pa.east, pa.west,
420 pa.pub, pa.comment,
421 gl.name AS locality, gs.name AS subAdministrativeArea,
422 ga.name AS administrativeArea, gc.countryFR AS country
423 FROM profile_addresses AS pa
424 LEFT JOIN geoloc_localities AS gl ON (gl.id = pa.localityId)
425 LEFT JOIN geoloc_administrativeareas AS ga ON (ga.id = pa.administrativeAreaId)
426 LEFT JOIN geoloc_subadministrativeareas AS gs ON (gs.id = pa.subAdministrativeAreaId)
427 LEFT JOIN geoloc_countries AS gc ON (gc.iso_3166_1_a2 = pa.countryId)
323ac187 428 ' . ((count($where) > 0) ? 'WHERE ' . implode(' AND ', $where) : '') . '
eb54852e
SJ
429 ORDER BY pa.pid, pa.jobid, pa.id';
430 $this->dbiter = XDB::iterator($sql);
431 }
432
433 public function next()
434 {
435 if (is_null($this->dbiter)) {
436 return null;
437 }
438 $data = $this->dbiter->next();
439 if (is_null($data)) {
440 return null;
441 }
442 // Adds phones to addresses.
443 $it = Phone::iterate(array($data['pid']), array(Phone::LINK_ADDRESS), array($data['id']));
444 while ($phone = $it->next()) {
445 $data['phones'][$phone->id()] = $phone->toFormArray();
446 }
447 return new Address($data);
448 }
449
450 public function total()
451 {
452 return $this->dbiter->total();
453 }
454
455 public function first()
456 {
457 return $this->dbiter->first();
458 }
459
460 public function last()
461 {
462 return $this->dbiter->last();
463 }
464
465 public function value()
466 {
467 return $this->dbiter;
468 }
469}
470
471// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
472?>