Properly displays moderated jobs (Closes #1369).
[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.
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 */
398class 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?>