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