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. | |
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 | */ | |
384 | class 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 | ?> |