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