Added contact offers.
[readifood.git] / lib / functions.php
1 <?php
2
3   function parse_parameters($parameters) {
4     $name = null;
5     $id = null;
6     $args = array();
7
8     if (count($parameters) > 0) {
9       $name = array_shift($parameters);
10
11       /* Recall that we shifted. */
12       if (count($parameters) > 0) {
13         if (is_numeric($parameters[0])) {
14           $id = array_shift($parameters);
15         }
16       }
17
18       $args = $parameters;
19     }
20
21     return array($name, $id, $args);
22   }
23
24   function pagination() {
25     $first_page = 1;
26     $per_page = $GLOBALS['default_page_size'];
27
28     parse_str($_SERVER['QUERY_STRING'], $params);
29     if (array_key_exists('page', $params)) if (is_numeric($params['page'])) $first_page = $params['page'];
30     if (array_key_exists('size', $params)) if (is_numeric($params['size'])) $per_page = $params['size'];
31
32     return array($first_page, $per_page);
33   }
34
35   function page_link($alt, $n, $cur, $max, $size) {
36     $links = array();
37     if ($n < 1 || $n == $cur || $n > $max) return $alt;
38     $params = array('page' => $n);
39     if ($size != $GLOBALS['default_page_size']) $params['size'] = $size;
40     $url = http_build_query($params);
41     return "<a href=\"?$url\">$alt</a> ";
42   }
43
44   function show_pagination($pager, $n = 5) {
45     if (! $pager->haveToPaginate()) return;
46
47     list($first_page, $per_page) = pagination();
48
49     $pages = ceil($pager->getNbResults() / $per_page);
50
51     /* Highlight the fact we skipped some pages. */
52     $linked_pages = $pager->getLinks($n);
53     $first_link = $linked_pages[0];
54     $last_link = end($linked_pages);
55
56     $links = array();
57     $links[] = page_link('First', 1, $first_page, $pages, $per_page);
58     $links[] = page_link('Previous', $first_page - 1, $first_page, $pages, $per_page);
59     if ($first_link > 1) $links[] = page_link('...', $first_page, $pages, $per_page);
60     foreach ($linked_pages as $link) $links[] = page_link($link, $link, $first_page, $pages, $per_page);
61     if ($last_link < $pages) $links[] = page_link('...', $first_page, $pages, $per_page);
62     $links[] = page_link('Next', $first_page + 1, $first_page, $pages, $per_page);
63     $links[] = page_link('Last', $pages, $first_page, $pages, $per_page);
64
65     echo "<p>Page: ";
66     echo implode(' / ', $links);
67     echo "</p>\n";
68   }
69
70   function get_city_by_name($name, $postcode_area = null, $verbose = true) {
71     $q = new CityQuery;
72
73     $m = $q->filterByName(urldecode($name));
74     if (isset($postcode_area)) {
75       $m->filterByPostcodeArea($postcode_area);
76     }
77     $cities = $m->find();
78
79     switch ($m->count()) {
80       case 0:
81         if ($verbose) echo "<p>No such city!</p>\n";
82         return null;
83
84       case 1:
85         return $cities[0];
86
87       default:
88         if ($verbose) echo "<p>Can't identify city uniquely.</p>!\n";
89         return null;
90     }
91   }
92
93   function get_city_by_id($id, $verbose = true) {
94     $q = new CityQuery;
95     $city = $q->findOneById($id);
96
97     if (! $q->count()) {
98       if ($verbose) echo "<p>No such city!</p>\n";
99       return null;
100     }
101
102     return $city;
103   }
104
105   function get_area_by_name($name, $verbose = true) {
106     $q = new AreaQuery;
107     $areas = $q->filterByName(urldecode($name))->find();
108
109     switch ($q->count()) {
110       case 0:
111         if ($verbose) echo "<p>No such area!</p>\n";
112         return null;
113
114       case 1:
115         return $areas[0];
116
117       default:
118         if ($verbose) echo "<p>Can't identify area uniquely.</p>!\n";
119         return null;
120     }
121   }
122
123   function get_area_by_id($id, $verbose = true) {
124     $q = new AreaQuery;
125     $area = $q->findOneById($id);
126
127     if (! $q->count()) {
128       if ($verbose) echo "<p>No such area!</p>\n";
129       return null;
130     }
131
132     return $area;
133   }
134
135   function get_area_city($area) {
136     $q = new CityQuery;
137     return $q->findOneById($area->getCityId());
138   }
139
140   function get_contact_by_name($name, $verbose = true) {
141     $q = new ContactQuery;
142     $contact = $q->filterByDisplayname(urldecode($name))->find();
143
144     switch ($q->count()) {
145       case 0:
146         if ($verbose) echo "<p>No such contact!</p>\n";
147         return null;
148
149       case 1:
150         return $contacts[0];
151
152       default:
153         if ($verbose) echo "<p>Can't identify contact uniquely.</p>!\n";
154         return null;
155     }
156   }
157
158   function get_contact_by_id($id, $verbose = true) {
159     $q = new ContactQuery;
160     $contact = $q->findOneById($id);
161
162     if (! $q->count()) {
163       if ($verbose) echo "<p>No such contact!</p>\n";
164       return null;
165     }
166
167     return $contact;
168   }
169
170   function get_hub_by_name($name, $verbose = true) {
171     $q = new HubQuery;
172     $hubs = $q->filterByDisplayname(urldecode($name))->find();
173
174     switch ($q->count()) {
175       case 0:
176         if ($verbose) echo "<p>No such hub!</p>\n";
177         return null;
178
179       case 1:
180         return $hubs[0];
181
182       default:
183         if ($verbose) echo "<p>Can't identify hub uniquely.</p>!\n";
184         return null;
185     }
186   }
187
188   function get_hub_by_id($id, $verbose = true) {
189     $q = new HubQuery;
190     $hub = $q->findOneById($id);
191
192     if (! $q->count()) {
193       if ($verbose) echo "<p>No such hub!</p>\n";
194       return null;
195     }
196
197     return $hub;
198   }
199
200   function get_donation_by_id($id, $verbose = true) {
201     $q = new DonationQuery;
202     $donation = $q->findOneById($id);
203
204     if (! $q->count()) {
205       if ($verbose) echo "<p>No such donation!</p>\n";
206       return null;
207     }
208
209     return $donation;
210   }
211
212   function get_order_by_id($id, $verbose = true) {
213     $q = new OrderQuery;
214     $order = $q->findOneById($id);
215
216     if (! $q->count()) {
217       if ($verbose) echo "<p>No such order!</p>\n";
218       return null;
219     }
220
221     return $order;
222   }
223
224   function get_order_ids_by_state($state_mask) {
225     $order_ids = array();
226     $dbh = Propel::getConnection();
227     $sth = $dbh->prepare("select * from OrderState o where updated=(select max(updated) from OrderState where order_id=o.order_id) and state & $state_mask");
228     $sth->execute();
229     $order_states = OrderStatePeer::populateObjects($sth);
230     foreach ($order_states as $order_state) $order_ids[] = $order_state->getOrderId();
231     return $order_ids;
232   }
233
234   function get_beneficiary_orders($contact, $state_mask = null) {
235     $q = new OrderQuery;
236     $q->filterByBeneficiaryId($contact->getId());
237     if ($state_mask) $q->filterById(get_order_ids_by_state($state_mask));
238     return $q->orderByDate()->find();
239   }
240
241   function get_requester_orders($contact, $state_mask = null) {
242     $q = new OrderQuery;
243     $q->filterByRequesterId($contact->getId());
244     if ($state_mask) $q->filterById(get_order_ids_by_state($state_mask));
245     return $q->orderByDate()->find();
246   }
247
248   function get_contact_orders($contact, $state_mask = null) {
249     $q = new OrderQuery;
250     $q->filterByBeneficiaryId($contact->getId())->_or()->filterByRequesterId($contact->getId());
251     if ($state_mask) $q->filterById(get_order_ids_by_state($state_mask));
252     return $q->orderByDate()->find();
253   }
254
255   function get_available_offers($contact = null) {
256     $q = new OfferQuery;
257     $q->where("(valid_from is null or valid_from<=now())");
258     $q->where("(valid_to is null or valid_to>now())");
259     if (! is_null($contact)) {
260       $qq = new OfferStateQuery;
261       $qq->filterByContactId($contact->getId());
262       $ids = Array();
263       foreach ($qq->find() as $state) $ids[] = $state->getOfferId();
264       if (count($ids)) $q->_or()->filterById($ids);
265     }
266     return $q->orderById()->find();
267   }
268
269   function get_offer_state($contact, $offer) {
270     $q = new OfferStateQuery;
271     $q->filterByOfferId($offer->getId());
272     $q->filterByContactId($contact->getId());
273     return $q->orderByUpdated('desc')->findOne();
274   }
275
276   function get_user_by_contact_id($id, $verbose = true) {
277     $q = new UserQuery;
278     $user = $q->findOneByContactId($id);
279
280     if (! $q->count()) {
281       if ($verbose) echo "<p>No such user!</p>\n";
282       return null;
283     }
284
285     return $user;
286   }
287
288   function get_city_displayname($city) {
289     return $city->getName() . ", " . $city->getPostcodeArea();
290   }
291
292   function get_area_displayname($area) {
293     return $area->getName() . " in " . get_city_displayname(CityQuery::create()->findOneById($area->getCityId()));
294   }
295
296   function get_donation_displayname($donation) {
297     return sprintf("%0.2fkg on %s", $donation->getQuantity() / 1000, $donation->getDate());
298   }
299
300   function get_order_parcel_string($order) {
301     global $parcel_sizes, $parcel_contents;
302
303     $parcel_size = null;
304     for ($i = 0 ; $i < count($parcel_sizes); $i++) {
305       if ($order->getParcel() & (1 << $i)) {
306         $parcel_size = $parcel_sizes[$i];
307         break;
308       }
309     }
310
311     $selected = array();
312     for ($i = count($parcel_sizes); $i < count($parcel_contents); $i++) {
313       if ($order->getParcel() & (1 << $i)) $selected[] = $parcel_contents[$i];
314     }
315
316     $ret = implode(": ", array($parcel_size, implode(", ", $selected)));
317     $ret = preg_replace('/^: /', '', $ret);
318     $ret = preg_replace('/: $/', '', $ret);
319
320     return $ret;
321   }
322
323   function get_order_displayname($order) {
324     return sprintf("<span class=\"small\">%s</span> on %s", get_order_parcel_string($order), $order->getDate());
325   }
326
327   function get_order_state_string($order_state = null) {
328     global $states;
329
330     if (is_null($order_state)) return null;
331
332     for ($i = 0; $i < count($states); $i++) {
333       if ($order_state->getState() & (1 << $i)) {
334         return $states[$i];
335       }
336     }
337
338     return "unknown";
339   }
340
341   function get_order_state($order) {
342     $q = new OrderStateQuery();
343     return $q->filterByOrderId($order->getId())->orderByUpdated('desc')->findOne();
344   }
345
346   function get_order_summary($order) {
347     $ret = "Order ";
348     $order_state = get_order_state($order);
349     if ($order_state) $ret = "<strong>" . ucfirst(get_order_state_string($order_state)) . "</strong> order ";
350     $ret .= $order->getStrongLink($order->getId()) . ": " . get_order_displayname($order);
351
352     if (check_admin(1)) $ret .= " " . $order->getDeleteLink();
353
354     /* XXX: Should pull from query. */
355     $q = new ContactQuery;
356     $contact = $q->findOneById($order->getRequesterId());
357     if ($contact) {
358       $ret .= " referred by " . $contact->getLink();
359       $area = get_contact_area($contact);
360       if ($area) $ret .= " in " . $area->getLink();
361     }
362
363     $q = new ContactQuery;
364     $contact = $q->findOneById($order->getBeneficiaryId());
365     if ($contact) {
366       $ret .= " for " . $contact->getLink();
367       $area = get_contact_area($contact);
368       if ($area) $ret .= " in " . $area->getLink();
369     }
370
371     if ($order->getHubId()) {
372       $q = new HubQuery;
373       $hub = $q->findOneById($order->getHubId());
374       if ($hub) $ret .= " to hub " . $hub->getLink();
375       $area = get_hub_area($hub);
376       if ($area) $ret .= " in " . $area->getLink();
377     }
378
379     return $ret;
380   }
381
382   function get_address_area($address) {
383     $q = new AreaQuery;
384     return $q->findOneById($address->getAreaId());
385   }
386
387   function get_address_map_link($address) {
388     $postcode = trim($address->getPostcode());
389     if ($postcode) {
390       # mrt=loc specifies a location search.
391       $map = "maps.google.co.uk/maps?q=" . urlencode($postcode) . "&mrt=loc";
392       $url = "http://$map";
393       # output=embed allows display in an iframe.
394       # iwloc=near hides the popup window for the embedded view.
395       $embed = $GLOBALS['http'] . "://$map&output=embed&iwloc=near";
396       $html = " ";
397       $html .= get_small_link_with_id("map", "Map", $url);
398       $html .= "<script>\n  $(function() {\n";
399       $html .= "    var x = 0;\n";
400       $html .= "    var y = 0;\n";
401       $html .= "    var loaded = false;\n";
402       $html .= "    $(\"#map\").hover(function(e) {\n";
403       $html .= "      x = $(this).outerWidth();\n";
404       $html .= "      y = $(this).outerHeight() / 2;\n";
405       $html .= "      $(\"#popup\").css(\"left\", e.pageX + x).css(\"top\", e.pageY + y);;\n";
406       $html .= "      $(\"#popup\").show();\n";
407       $html .= "      if (! loaded) {\n";
408       $html .= "        $(\"#popup\").html(\"<iframe width='100%' height='100%' src='$embed'></iframe>\");\n";
409       $html .= "        loaded = true;\n";
410       $html .= "      }\n";
411       $html .= "    },function() {\n";
412       $html .= "      $(\"#popup\").hide();\n";
413       $html .= "    })\n";
414       $html .= "  });</script>";
415       return $html;
416     }
417   }
418
419   function get_contact_address($contact) {
420     $q = new AddressQuery;
421     return $q->findOneById($contact->getAddressId());
422   }
423
424   function get_contact_area($contact) {
425     $address = get_contact_address($contact);
426     if (! $address) return null;
427
428     return get_address_area($address);
429   }
430
431   function get_contact_city($contact) {
432     $area = get_contact_area($contact);
433     if (! $area) return null;
434
435     return get_area_city($area);
436   }
437
438   /* Parcel strings are the same so this can work. */
439   function get_contact_parcel_string($contact) {
440     return get_order_parcel_string($contact);
441   }
442
443   /* Hub and Contact are similar enough that this can work. */
444   function get_hub_address($hub) {
445     return get_contact_address($hub);
446   }
447
448   /* Hub and Contact are similar enough that this can work. */
449   function get_hub_area($hub) {
450     return get_contact_area($hub);
451   }
452
453   /* Hub and Contact are similar enough that this can work. */
454   function get_hub_city($hub) {
455     return get_contact_city($hub);
456   }
457
458   function get_area_contacts($area_id = null, $role = null) {
459     $q = new ContactQuery;
460     if (isset($area_id)) $q->useAddressQuery()->filterByAreaId($area_id)->endUse();
461     if (isset($role)) $q->where("role & $role");
462     return $q->orderByDisplayname()->find();
463   }
464
465   function get_area_requesters($area_id = null) {
466     return get_area_contacts($area_id, $GLOBALS['ROLE_REQUESTER']);
467   }
468
469   function get_area_beneficiaries($area_id = null) {
470     return get_area_contacts($area_id, $GLOBALS['ROLE_BENEFICIARY']);
471   }
472
473   function get_area_donors($area_id = null) {
474     return get_area_contacts($area_id, $GLOBALS['ROLE_DONOR']);
475   }
476
477   function get_city_contacts($city_id = null, $role = null) {
478     /* XXX */
479     $area_ids = array();
480     $areas = get_city_areas($city_id);
481     foreach ($areas as $area) $area_ids[] = $area->getId();
482     return get_area_contacts($area_ids, $role);
483   }
484
485   function get_city_requesters($city_id = null) {
486     return get_city_contacts($city_id, $GLOBALS['ROLE_REQUESTER']);
487   }
488
489   function get_city_beneficiaries($city_id = null) {
490     return get_city_contacts($city_id, $GLOBALS['ROLE_BENEFICIARY']);
491   }
492
493   function get_city_donors($city_id = null) {
494     return get_city_contacts($city_id, $GLOBALS['ROLE_DONOR']);
495   }
496
497   function get_city_drivers($city_id = null) {
498     return get_city_contacts($city_id, $GLOBALS['ROLE_DRIVER']);
499   }
500
501   function get_role_string($object, $roles) {
502     $role = $object->getRole();
503
504     $selected = array();
505
506     for ($i =0; $i < count($roles); $i++) {
507       if ($role & (1 << $i)) $selected[] = $roles[$i];
508     }
509
510     return implode(", ", $selected);
511   }
512
513   function get_contact_role_string($contact) {
514     return get_role_string($contact, $GLOBALS['contact_roles']);
515   }
516
517   function get_hub_role_string($hub) {
518     return get_role_string($hub, $GLOBALS['hub_roles']);
519   }
520
521   function show_role_form($role, $roles) {
522     for ($i = 0; $i < count($roles); $i++) {
523       echo " <input type=\"checkbox\" id=\"role_$i\" name=\"role_$i\"";
524       if ($role & (1 << $i)) echo " checked";
525       echo "><label for=\"role_$i\">$roles[$i]</label>\n";
526     }
527   }
528
529   function get_area_hubs($area_id = null) {
530     $q = new HubQuery;
531     if (isset($area_id)) $q->useAddressQuery()->filterByAreaId($area_id)->endUse();
532     return $q->orderByDisplayname()->find();
533   }
534
535   function get_city_areas($city_id = null) {
536     $q = new AreaQuery;
537     $q->join("City")->orderBy("City.Name");
538     if (isset($city_id)) $q->filterByCityId($city_id);
539     return $q->orderByName()->find();
540   }
541
542   function get_city_areas_with_contacts($city_id = null, $role = null) {
543     $q = new AreaQuery;
544     $q->join("City")->orderBy("City.Name");
545     if (isset($city_id)) $q->filterByCityId($city_id);
546     /* XXX */
547     if (isset($role)) $q->useAddressQuery()->join("Contact")->useContactQuery()->where("role & $role")->endUse()->endUse();
548     else $q->useAddressQuery()->join("Contact")->endUse();
549     return $q->orderByName()->distinct()->find();
550   }
551
552   function get_city_areas_with_hubs($city_id = null) {
553     $q = new AreaQuery;
554     $q->join("City")->orderBy("City.Name");
555     if (isset($city_id)) $q->filterByCityId($city_id);
556     $q->useAddressQuery()->join("Hub")->endUse();
557     return $q->orderByName()->distinct()->find();
558   }
559
560   function get_city_hubs($city_id = null) {
561     $q = new HubQuery;
562     if (isset($city_id)) $q->useAddressQuery()->useAreaQuery()->filterByCityId($city_id)->endUse()->endUse();
563     return $q->orderByDisplayname()->find();
564   }
565
566   function iso8601_to_ymd($iso8601) {
567     return split("-", $iso8601);
568   }
569
570   function ymd_to_iso8601($name) {
571     $y = $_POST[$name . "_y"];
572     if (! $y) return null;
573     $m = $_POST[$name . "_m"];
574     if (! $m) $m = 1;
575     $d = $_POST[$name . "_d"];
576     if (! $d) $d = 1;
577     return sprintf("%04d-%02d-%02d", $y, $m, $d);
578   }
579
580   function show_date_form($name, $date = null) {
581     if (! isset($date)) $date = date('Y-m-d', time());
582     datepicker($name, $date);
583   }
584
585   function validate_postcode($postcode, &$outward = null, &$inward = null) {
586     /*
587       Valid postcode formats (BS7666):
588
589         AN NLL
590         ABN NLL
591         ANN NLL
592         ABNN NLL
593         ABND NLL
594         ANC NLL
595
596       Where N is a number; A is a letter not including Q, V, X;
597       B is a letter not including I, J, Z; C is a letter from the set
598       ABCDEFGHJKSTUW; D is a letter from the set ABEHMNPRVWXY;
599       L is a letter from the set ABDEFGHJLNPQRSTUWXYZ.
600
601       The postcode GIR 0AA is also valid.
602     */
603     $outward = $inward = null;
604
605     /* Treat blank as valid for convenience. */
606     $postcode = trim($postcode);
607     if (! $postcode) return true;
608
609     $A = '[ABCDEFGHIJKLMNOPRSTUWYZ]';
610     $B = '[ABCDEFGHKLMNOPQRSTUVWXY]';
611     $C = '[ABCDEFGHJKSTUW]';
612     $D = '[ABEHMNPRVWXY]';
613     $L = '[ABDEFGHJLNPQRSTUWXYZ]';
614     $N = '\d';
615     if (! preg_match("/^($A$N|$A$B$N|$A$N$N|$A$B$N$N|$A$B$N$D|$A$N$C|GIR)\s*($N$L$L)$/", $postcode, $m)) return false;
616     if ($m[1] == "GIR" && $m[2] != "0AA") return false;
617     list($ignored, $outward, $inward) = $m;
618     return true;
619   }
620
621   function format_postcode($postcode, $complain = true) {
622     if (validate_postcode($postcode, $outward, $inward)) {
623       return "$outward $inward";
624     }
625     if ($complain) {
626       echo "<p>Invalid postcode!</p>\n";
627       return null;
628     }
629   }
630
631   function get_small_link_with_id() {
632     /* Args are <id>, <alt text>, <format>, [<stuff> ...] */
633     $args = func_get_args();
634     $id = array_shift($args);
635     if (isset($id)) $id = " id=\"$id\"";
636     $html = htmlspecialchars(array_shift($args));
637     $url = array_shift($args);
638     return vsprintf("<a$id class=\"small noprint\" href=\"$url\">$html</a>\n", $args);
639   }
640
641   function get_small_link() {
642     /* Args are <alt text>, <format>, [<stuff> ...] */
643     $args = func_get_args();
644     array_unshift($args, null);
645     return call_user_func_array("get_small_link_with_id", $args);
646   }
647
648   function small_link() {
649     echo call_user_func_array("get_small_link", func_get_args());
650   }
651
652   function check_dates($description, $from, $to, $mandatory_from = true, $mandatory_to = true) {
653     $Description = ucfirst($description);
654     if ($from || $mandatory_from) {
655       list($y, $m, $d) = explode('-', $from);
656       if (! checkdate($m, $d, $y)) {
657         echo "<p>Invalid $description start date!</p>\n";
658         return false;
659       }
660       $start = mktime(0, 0, 0, $m, $d, $y);
661     }
662     else $start = 0;
663
664     if ($to || $mandatory_to) {
665       list($y, $m, $d) = explode('-', $to);
666       if (! checkdate($m, $d, $y)) {
667         echo "<p>Invalid $description end date!</p>\n";
668         return false;
669       }
670       $end = mktime(0, 0, 0, $m, $d, $y);
671     }
672     else $end = PHP_INT_MAX;
673
674     if ($end < $start) {
675       echo "<p>$Description end date is earlier than start date!</p>\n";
676       return false;
677     }
678
679     return true;
680   }
681
682   include_once(join(DIRECTORY_SEPARATOR, array($lib_root, "admin.php")));
683   include_once(join(DIRECTORY_SEPARATOR, array($lib_root, "auth0.php")));
684   include_once(join(DIRECTORY_SEPARATOR, array($lib_root, "forms.php")));
685
686 ?>