Merge branch 'xorg/master' into xorg/f/geocoding
authorStéphane Jacob <sj@m4x.org>
Fri, 13 May 2011 12:31:20 +0000 (14:31 +0200)
committerStéphane Jacob <sj@m4x.org>
Fri, 13 May 2011 12:31:20 +0000 (14:31 +0200)
32 files changed:
bin/export_sql.bash
bin/formatAddresses.php
classes/address.php
classes/direnum.php
classes/geocoder.php
classes/gmapsgeocoder.php
classes/profile.php
classes/userfilter.php
classes/userfilter/conditions.inc.php
configs/mails.conf
configs/platal.ini
htdocs/javascript/profile.js
include/profilefields.inc.php
include/ufbuilder.inc.php
include/validations.inc.php
include/validations/address.inc.php [new file with mode: 0644]
include/webservices/manageurs.server.inc.php
modules/payment/money/paypal.inc.php
modules/profile.php
modules/profile/addresses.inc.php
modules/search.php
templates/geoloc/form.address.tpl
templates/include/form.valid.address.tpl [new file with mode: 0644]
templates/include/form.valid.edit-address.tpl [new file with mode: 0644]
templates/profile/adresses.address.tpl
templates/profile/geocoding.mail.tpl
templates/profile/jobs.job.tpl
templates/search/adv.form.tpl
upgrade/1.1.2/01_geocoding.sql [new file with mode: 0644]
upgrade/1.1.2/README [new file with mode: 0644]
upgrade/1.1.2/retrieve_address_tables.sh [new file with mode: 0755]
upgrade/1.1.2/xx_retrieve_geocoding.sql [new file with mode: 0644]

index 2ecc94f..9b85b2a 100755 (executable)
@@ -7,10 +7,8 @@
 # Developers: list 'public' tables here.
 SHARED_TABLES="account_types \
 forums \
-geoloc_administrativeareas \
 geoloc_countries \
-geoloc_localities \
-geoloc_subadministrativeareas \
+geoloc_languages \
 log_actions \
 newsletter_cat \
 profile_binet_enum \
index 4ab19d2..6b8a8dd 100755 (executable)
 
 require './connect.db.inc.php';
 require_once '../classes/address.php';
+require_once '../classes/geocoder.php';
+require_once '../classes/gmapsgeocoder.php';
 
 $globals->debug = 0; // Do not store backtraces
 
-print "(Re)Formats postal addresses for all addresses in the database.\n";
+$targets = array(
+    'g' => 'formatted_address',
+    'p' => 'postalText'
+);
+$ranges = array(
+    'f' => ' != \'\'',
+    'e' => ' = \'\'',
+    'a' => 'IS NOT NULL'
+);
+
+$options = getopt('g::t:r:h::', array('geocode::', 'target:', 'range:', 'help::'));
+
+$help_required = isset($options['h']) || isset($options['help']);
+$geocoding_required = isset($options['g']) || isset($options['geocode']);
+$target = '';
+$range = '';
+
+if (isset($options['t'])) {
+    $target = $options['t'];
+} elseif (isset($options['target'])) {
+    $target = $options['target'];
+}
+
+if (isset($options['r'])) {
+    $range = $options['r'];
+} elseif ($options['range']) {
+    $range = $options['range'];
+}
+
+$missing_option = !array_key_exists($target, $targets) || !array_key_exists($range, $ranges);
+
+if ($missing_option || $help_required) {
+    echo <<<EOF
+SYNOPSIS
+    formatAddresses.php [-g] -t [g|p] -r [f|e|a]
+
+DESCRIPTION
+    formatAddresses.php formats addresses. If the addresses need geocoding, this
+    must be specified (-g). The targetted group of addresses must be specified
+    (non formatted addresses, formatted addresses, all addresses).
+
+OPTIONS
+    -g, --geocode
+        Geocodes the adresses. If not required, the address will not be
+        geolocated.
+    -t, --target [ g | p ]
+        The selection will be made either on the geocoding
+        (formatted_address) or the postal address (postalText).
+    -r, --range [ f | e | a ]
+        The selection will include the addresses corresponding to the right
+        target, which are formatted, empty (non formatted) or all addresses.
+    -h, --help
+        displays this help
+
+EOF;
+    exit;
+}
+
+print "Formats addresses addresses.\n";
+
+$where = '';
+if ($range != 'a') {
+    $where = 'WHERE  ' . $targets[$target] . $ranges[$range];
+}
+
+if ($geocoding_required) {
+    // Waiting time is computed as follows: 3600 * 24 / LIMIT,
+    // where LIMIT is google's limit reduced to take into account site geocoding.
+    $wait = ceil(3600 * 24 / 2000);
+    $display_limit = 1;
+} else {
+    $wait = 0;
+    $display_limit = 100;
+}
+
 $it = XDB::rawIterator('SELECT  *
                           FROM  profile_addresses
+                        ' . $where . '
                       ORDER BY  pid, jobid, type, id');
+
 $total = $it->total();
 $i = 0;
 $j = 0;
+$skipped = 0;
 printf("\r%u / %u",  $i, $total);
+
 while ($item = $it->next()) {
     $address = new Address($item);
-    $address->format(array('postalText' => true));
-    $address->delete();
-    $address->save();
+    $address->changed = ($geocoding_required ? 1 : 0);
+    $address->format();
+    if ($address->delete()) {
+        $address->save();
+    } else {
+        ++$skipped;
+    }
 
     ++$i;
-    if ($i == 100) {
+    if ($i == $display_limit) {
         ++$j;
         $i = 0;
-        printf("\r%u / %u",  $i + 100 * $j, $total);
+        printf("\r%u / %u",  $i + $display_limit * $j, $total);
     }
+    sleep($wait * $address->geocoding_calls);
 }
-print "Done.\n";
+printf("\r%u / %u",  $i + $display_limit * $j, $total);
+
+if ($skipped != 0) {
+    printf("\n%u addresses skipped.\n", $skipped);
+}
+
+print "\nDone.\n";
 
 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
 ?>
index fc438e5..bca9e5f 100644 (file)
@@ -296,28 +296,23 @@ class Address
     public $id = 0;
 
     // Geocoding fields.
-    public $accuracy = 0;
     public $text = '';
     public $postalText = '';
-    public $postalCode = null;
-    public $localityId = null;
-    public $subAdministrativeAreaId = null;
-    public $administrativeAreaId = null;
-    public $localityName = null;
-    public $subAdministrativeAreaName = null;
-    public $administrativeAreaName = null;
-    public $localityNameLocal = null;
-    public $subAdministrativeAreaNameLocal = null;
-    public $administrativeAreaNameLocal = null;
-    public $countryId = null;
+    public $types = '';
+    public $formatted_address = '';
+    public $components = array();
     public $latitude = null;
     public $longitude = null;
-    public $north = null;
-    public $south = null;
-    public $east = null;
-    public $west = null;
-    public $geocodedText = null;
-    public $geocodeChosen = null;
+    public $southwest_latitude = null;
+    public $southwest_longitude = null;
+    public $northeast_latitude = null;
+    public $northeast_longitude = null;
+    public $location_type = '';
+    public $partial_match = false;
+    public $componentsIds = '';
+    public $request = false;
+    public $geocoding_date = null;
+    public $geocoding_calls = 0;
 
     // Database's field required for both 'home' and 'job' addresses.
     public $pub = 'ax';
@@ -360,6 +355,7 @@ class Address
                                                                                array('', "\n"), $this->text)), 'CEDEX')) !== false);
             }
         }
+        $this->request = !is_null(AddressReq::get_request($this->pid, $this->jobid, $this->groupid, $this->type, $this->text));
     }
 
     public function setId($id)
@@ -559,38 +555,25 @@ class Address
         // country, then apply corresponding formatting or translate country
         // into default language.
         $count = count($arrayText);
-        if (in_array(strtoupper($this->countryId), Address::$formattings)) {
-            $text = call_user_func(array($this, 'formatPostalAddress' . strtoupper($this->countryId)), $arrayText);
+        list($countryId, $country) = XDB::fetchOneRow('SELECT  gc.iso_3166_1_a2, gc.country
+                                                         FROM  geoloc_countries AS gc
+                                                   INNER JOIN  geoloc_languages AS gl ON (gc.iso_3166_1_a2 = gl.iso_3166_1_a2)
+                                                        WHERE  gl.countryPlain = {?} OR gc.countryPlain = {?}',
+                                                      $arrayText[$count - 1], $arrayText[$count - 1]);
+        if (is_null($countryId)) {
+            $text = $this->formatPostalAddressFR($arrayText);
+        } elseif (in_array(strtoupper($countryId), Address::$formattings)) {
+            $text = call_user_func(array($this, 'formatPostalAddress' . strtoupper($countryId)), $arrayText);
         } else {
-            list($countryId, $country) = XDB::fetchOneRow('SELECT  gc.iso_3166_1_a2, gc.country
-                                                             FROM  geoloc_countries AS gc
-                                                       INNER JOIN  geoloc_languages AS gl ON (gc.iso_3166_1_a2 = gl.iso_3166_1_a2)
-                                                            WHERE  gc.iso_3166_1_a2 = {?} OR gl.countryPlain = {?} OR gc.countryPlain = {?}',
-                                                          $this->countryId, $arrayText[$count - 1], $arrayText[$count - 1]);
-            if (is_null($countryId)) {
-                $text = $this->formatPostalAddressFR($arrayText);
-            } elseif (in_array(strtoupper($countryId), Address::$formattings)) {
-                $text = call_user_func(array($this, 'formatPostalAddress' . strtoupper($countryId)), $arrayText);
-            } else {
-                $arrayText[$count - 1] = mb_strtoupper(replace_accent($country));
-                $text = implode("\n", $arrayText);
-            }
+            $arrayText[$count - 1] = mb_strtoupper(replace_accent($country));
+            $text = implode("\n", $arrayText);
         }
 
         $this->postalText = $text;
     }
 
-    public function format(array $format = array())
+    public function format()
     {
-        if (empty($format)) {
-            $format['requireGeocoding'] = false;
-            $format['stripGeocoding'] = false;
-            $format['postalText'] = false;
-        } else {
-            foreach (array('requireGeocoding', 'stripGeocoding', 'postalText') as $type) {
-                $format[$type] = (isset($format[$type])) ? $format[$type] : false;
-            }
-        }
         $this->text = trim($this->text);
         $this->phones = Phone::formatFormArray($this->phones, $this->error, new ProfileVisibility($this->pub));
         if ($this->removed == 1) {
@@ -602,70 +585,47 @@ class Address
             }
         }
 
-        if ($format['requireGeocoding'] || $this->changed == 1) {
+        $this->formatPostalAddress();
+        if ($this->changed == 1) {
             $gmapsGeocoder = new GMapsGeocoder();
             $gmapsGeocoder->getGeocodedAddress($this);
-            $this->changed = 0;
-            $this->error = !empty($this->geocodedText);
-        }
-        if ($format['stripGeocoding'] || ($this->type == self::LINK_COMPANY && $this->error) || $this->geocodeChosen === '0') {
-            if ($this->geocodeChosen === '0') {
-                $mailer = new PlMailer('profile/geocoding.mail.tpl');
-                $mailer->assign('text', $this->text);
-                $mailer->assign('geoloc', $this->geocodedText);
-                $mailer->send();
-            }
-            $gmapsGeocoder = new GMapsGeocoder();
-            $gmapsGeocoder->stripGeocodingFromAddress($this);
         }
-        if ($this->countryId == '') {
-            $this->countryId = null;
-        }
-        $this->geocodeChosen = null;
-        if ($format['postalText']) {
-            $this->formatPostalAddress();
+
+        $componants = array();
+        foreach ($this->components as $component) {
+            $componants[] = Geocoder::getComponentId($component);
         }
-        return !$this->error;
+        $this->componentsIds = implode(',', $componants);
+
+        return true;
     }
 
     public function toFormArray()
     {
         $address = array(
-            'accuracy'                       => $this->accuracy,
-            'text'                           => $this->text,
-            'postalText'                     => $this->postalText,
-            'postalCode'                     => $this->postalCode,
-            'localityId'                     => $this->localityId,
-            'subAdministrativeAreaId'        => $this->subAdministrativeAreaId,
-            'administrativeAreaId'           => $this->administrativeAreaId,
-            'countryId'                      => $this->countryId,
-            'localityName'                   => $this->localityName,
-            'subAdministrativeAreaName'      => $this->subAdministrativeAreaName,
-            'administrativeAreaName'         => $this->administrativeAreaName,
-            'localityNameLocal'              => $this->localityNameLocal,
-            'subAdministrativeAreaNameLocal' => $this->subAdministrativeAreaNameLocal,
-            'administrativeAreaNameLocal'    => $this->administrativeAreaNameLocal,
-            'latitude'                       => $this->latitude,
-            'longitude'                      => $this->longitude,
-            'north'                          => $this->north,
-            'south'                          => $this->south,
-            'east'                           => $this->east,
-            'west'                           => $this->west,
-            'error'                          => $this->error,
-            'changed'                        => $this->changed,
-            'removed'                        => $this->removed,
+            'text'                => $this->text,
+            'postalText'          => $this->postalText,
+            'types'               => $this->types,
+            'formatted_address'   => $this->formatted_address,
+            'latitude'            => $this->latitude,
+            'longitude'           => $this->longitude,
+            'southwest_latitude'  => $this->southwest_latitude,
+            'southwest_longitude' => $this->southwest_longitude,
+            'northeast_latitude'  => $this->northeast_latitude,
+            'northeast_longitude' => $this->northeast_longitude,
+            'location_type'       => $this->location_type,
+            'partial_match'       => $this->partial_match,
+            'componentsIds'       => $this->componentsIds,
+            'geocoding_date'      => $this->geocoding_date,
+            'geocoding_calls'     => $this->geocoding_calls,
+            'request'             => $this->request
         );
-        if (!is_null($this->geocodedText)) {
-            $address['geocodedText'] = $this->geocodedText;
-            $address['geocodeChosen'] = $this->geocodeChosen;
-        }
 
         if ($this->type == self::LINK_PROFILE || $this->type == self::LINK_JOB) {
             $address['pub'] = $this->pub;
         }
         if ($this->type == self::LINK_PROFILE) {
             static $flags = array('current', 'temporary', 'secondary', 'mail', 'cedex', 'deliveryIssue');
-
             foreach ($flags as $flag) {
                 $address[$flag] = $this->flags->hasFlag($flag);
             }
@@ -723,39 +683,80 @@ class Address
 
     public function save()
     {
-        static $areas = array('administrativeArea', 'subAdministrativeArea', 'locality');
-
-        $this->format(array('postalText' => true));
         if (!$this->isEmpty()) {
-            foreach ($areas as $area) {
-                Geocoder::getAreaId($this, $area);
+            XDB::execute('INSERT IGNORE INTO  profile_addresses (pid, jobid, groupid, type, id, flags, text, postalText, pub, comment,
+                                                                 types, formatted_address, location_type, partial_match, latitude, longitude,
+                                                                 southwest_latitude, southwest_longitude, northeast_latitude, northeast_longitude,
+                                                                 geocoding_date, geocoding_calls)
+                                      VALUES  ({?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?},
+                                               {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, NOW(), {?})',
+                         $this->pid, $this->jobid, $this->groupid, $this->type, $this->id, $this->flags, $this->text, $this->postalText, $this->pub, $this->comment,
+                         $this->types, $this->formatted_address, $this->location_type, $this->partial_match, $this->latitude, $this->longitude,
+                         $this->southwest_latitude, $this->southwest_longitude, $this->northeast_latitude, $this->northeast_longitude, $this->geocoding_calls);
+
+            if ($this->componentsIds) {
+                foreach (explode(',', $this->componentsIds) as $component_id) {
+                    XDB::execute('INSERT IGNORE INTO  profile_addresses_components (pid, jobid, groupid, type, id, component_id)
+                                              VALUES  ({?}, {?}, {?}, {?}, {?}, {?})',
+                                 $this->pid, $this->jobid, $this->groupid, $this->type, $this->id, $component_id);
+                }
             }
 
-            XDB::execute('INSERT IGNORE INTO  profile_addresses (pid, jobid, groupid, type, id, flags, accuracy,
-                                                                 text, postalText, postalCode, localityId,
-                                                                 subAdministrativeAreaId, administrativeAreaId,
-                                                                 countryId, latitude, longitude, pub, comment,
-                                                                 north, south, east, west)
-                                      VALUES  ({?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?},
-                                               {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?}, {?})',
-                         $this->pid, $this->jobid, $this->groupid, $this->type, $this->id, $this->flags, $this->accuracy,
-                         $this->text, $this->postalText, $this->postalCode, $this->localityId,
-                         $this->subAdministrativeAreaId, $this->administrativeAreaId,
-                         $this->countryId, $this->latitude, $this->longitude,
-                         $this->pub, $this->comment,
-                         $this->north, $this->south, $this->east, $this->west);
-
             if ($this->type == self::LINK_PROFILE) {
                 Phone::savePhones($this->phones, $this->pid, Phone::LINK_ADDRESS, $this->id);
             }
         }
     }
 
+    public function updateGeocoding($text)
+    {
+        $id = null;
+        $texts = XDB::fetchAllAssoc('id', 'SELECT  id, text
+                                   FROM  profile_addresses
+                                  WHERE  pid = {?} AND jobid = {?} AND groupid = {?} AND type = {?}',
+                                $this->pid, $this->jobid, $this->groupid, $this->type);
+        $text = preg_replace('/\s+/', ' ', $text);
+        foreach ($texts as $key => $value) {
+            if (strcmp($text, preg_replace('/\s+/', ' ', $value)) == 0) {
+                $id = $key;
+                break;
+            }
+        }
+        if (!is_null($id)) {
+            XDB::execute('UPDATE  profile_addresses
+                             SET  text = {?}, postalText = {?}, types = {?}, formatted_address = {?},
+                                  location_type = {?}, partial_match = {?}, latitude = {?}, longitude = {?},
+                                  southwest_latitude = {?}, southwest_longitude = {?}, northeast_latitude = {?}, northeast_longitude = {?},
+                                  geocoding_date = {?}, geocoding_calls = NOW()
+                           WHERE  pid = {?} AND jobid = {?} AND groupid = {?} AND type = {?} AND id = {?}',
+                         $this->text, $this->postalText, $this->types, $this->formatted_address,
+                         $this->location_type, $this->partial_match, $this->latitude, $this->longitude,
+                         $this->southwest_latitude, $this->southwest_longitude, $this->northeast_latitude, $this->northeast_longitude,
+                         $this->pid, $this->jobid, $this->groupid, $this->type, $id, $this->geocoding_calls);
+
+            XDB::execute('DELETE FROM  profile_addresses_components
+                                WHERE  pid = {?} AND jobid = {?} AND groupid = {?} AND type = {?} AND id = {?}',
+                         $this->pid, $this->jobid, $this->groupid, $this->type, $id);
+            if ($this->componentsIds) {
+                foreach (explode(',', $this->componentsIds) as $component_id) {
+                    XDB::execute('INSERT IGNORE INTO  profile_addresses_components (pid, jobid, groupid, type, id, component_id)
+                                              VALUES  ({?}, {?}, {?}, {?}, {?}, {?})',
+                                 $this->pid, $this->jobid, $this->groupid, $this->type, $id, $component_id);
+                }
+            }
+        }
+    }
+
     public function delete()
     {
+        XDB::execute('DELETE FROM  profile_addresses_components
+                            WHERE  pid = {?} AND jobid = {?} AND groupid = {?} AND type = {?} AND id = {?}',
+                     $this->pid, $this->jobid, $this->groupid, $this->type, $this->id);
         XDB::execute('DELETE FROM  profile_addresses
                             WHERE  pid = {?} AND jobid = {?} AND groupid = {?} AND type = {?} AND id = {?}',
                      $this->pid, $this->jobid, $this->groupid, $this->type, $this->id);
+
+        return XDB::affectedRows();
     }
 
     static public function deleteAddresses($pid, $type, $jobid = null, $groupid = null, $deletePrivate = true)
@@ -908,22 +909,30 @@ class AddressIterator implements PlIterator
         if (count($pubs) != 0) {
             $where[] = XDB::format('(pa.pub IN {?})', $pubs);
         }
-        $sql = 'SELECT  pa.pid, pa.jobid, pa.type, pa.id, pa.flags,
-                        pa.accuracy, pa.text, pa.postalText, pa.postalCode,
-                        pa.localityId, pa.subAdministrativeAreaId,
-                        pa.administrativeAreaId, pa.countryId,
-                        pa.latitude, pa.longitude, pa.north, pa.south, pa.east, pa.west,
-                        pa.pub, pa.comment,
-                        gl.name AS locality, gl.nameLocal AS localityLocal,
-                        gs.name AS subAdministrativeArea, gs.nameLocal AS subAdministrativeAreaLocal,
-                        ga.name AS administrativeArea, ga.nameLocal AS administrativeAreaLocal,
-                        gc.country
-                  FROM  profile_addresses             AS pa
-             LEFT JOIN  geoloc_localities             AS gl ON (gl.id = pa.localityId)
-             LEFT JOIN  geoloc_administrativeareas    AS ga ON (ga.id = pa.administrativeAreaId)
-             LEFT JOIN  geoloc_subadministrativeareas AS gs ON (gs.id = pa.subAdministrativeAreaId)
-             LEFT JOIN  geoloc_countries              AS gc ON (gc.iso_3166_1_a2 = pa.countryId)
+        $sql = 'SELECT  pa.pid, pa.jobid, pa.groupid, pa.type, pa.id, pa.flags, pa.text, pa.postalText, pa.pub, pa.comment,
+                        pa.types, pa.formatted_address, pa.location_type, pa.partial_match, pa.latitude, pa.longitude,
+                        pa.southwest_latitude, pa.southwest_longitude, pa.northeast_latitude, pa.northeast_longitude,
+                        pa.geocoding_date, pa.geocoding_calls,
+                        GROUP_CONCAT(DISTINCT pc.component_id SEPARATOR \',\') AS componentsIds,
+                        pace1.long_name AS postalCode, pace2.long_name AS locality, pace3.long_name AS administrativeArea, pace4.long_name AS country
+                  FROM  profile_addresses                 AS pa
+             LEFT JOIN  profile_addresses_components      AS pc    ON (pa.pid = pc.pid AND pa.jobid = pc.jobid AND pa.groupid = pc.groupid
+                                                                       AND pa.type = pc.type AND pa.id = pc.id)
+             LEFT JOIN  profile_addresses_components_enum AS pace1 ON (FIND_IN_SET(\'postal_code\', pace1.types))
+             LEFT JOIN  profile_addresses_components_enum AS pace2 ON (FIND_IN_SET(\'locality\', pace2.types))
+             LEFT JOIN  profile_addresses_components_enum AS pace3 ON (FIND_IN_SET(\'administrative_area_level_1\', pace3.types))
+             LEFT JOIN  profile_addresses_components_enum AS pace4 ON (FIND_IN_SET(\'country\', pace4.types))
+             LEFT JOIN  profile_addresses_components      AS pac1  ON (pa.pid = pac1.pid AND pa.jobid = pac1.jobid AND pa.groupid = pac1.groupid
+                                                                       AND pa.id = pac1.id AND pa.type = pac1.type AND pace1.id = pac1.component_id)
+             LEFT JOIN  profile_addresses_components      AS pac2  ON (pa.pid = pac2.pid AND pa.jobid = pac2.jobid AND pa.groupid = pac2.groupid
+                                                                       AND pa.id = pac2.id AND pa.type = pac2.type AND pace2.id = pac2.component_id)
+             LEFT JOIN  profile_addresses_components      AS pac3  ON (pa.pid = pac3.pid AND pa.jobid = pac3.jobid AND pa.groupid = pac3.groupid
+                                                                       AND pa.id = pac3.id AND pa.type = pac3.type AND pace3.id = pac3.component_id)
+             LEFT JOIN  profile_addresses_components      AS pac4  ON (pa.pid = pac4.pid AND pa.jobid = pac4.jobid AND pa.groupid = pac4.groupid
+                                                                       AND pa.id = pac4.id AND pa.type = pac4.type AND pace4.id = pac4.component_id)
+
                  ' . ((count($where) > 0) ? 'WHERE  ' . implode(' AND ', $where) : '') . '
+              GROUP BY  pa.pid, pa.jobid, pa.groupid, pa.type, pa.id
               ORDER BY  pa.pid, pa.jobid, pa.id';
         $this->dbiter = XDB::iterator($sql);
     }
index a616061..bf18312 100644 (file)
@@ -44,11 +44,13 @@ class DirEnum
     const ORIGINCORPS    = 'origincorps';
     const CORPSRANKS     = 'corpsranks';
 
-    const NATIONALITIES  = 'nationalities';
-    const COUNTRIES      = 'countries';
-    const ADMINAREAS     = 'adminareas';
-    const SUBADMINAREAS  = 'subadminareas';
-    const LOCALITIES     = 'localities';
+    const NATIONALITIES       = 'nationalities';
+    const SUBLOCALITIES       = 'sublocalities';
+    const LOCALITIES          = 'localities';
+    const ADMNISTRATIVEAREAS3 = 'admnistrativeareas3';
+    const ADMNISTRATIVEAREAS2 = 'admnistrativeareas2';
+    const ADMNISTRATIVEAREAS1 = 'admnistrativeareas1';
+    const COUNTRIES           = 'countries';
 
     const COMPANIES      = 'companies';
     const JOBDESCRIPTION = 'jobdescription';
@@ -590,56 +592,49 @@ class DE_Nationalities extends DirEnumeration
 }
 // }}}
 
-// {{{ class DE_Countries
-class DE_Countries extends DirEnumeration
+// {{{ class DE_AddressesComponents
+class DE_AddressesComponents extends DirEnumeration
 {
-    protected $idfield   = 'geoloc_countries.iso_3166_1_a2';
-    protected $valfield  = 'geoloc_countries.country';
-    protected $valfield2 = 'geoloc_countries.countryEn';
-    protected $from      = 'geoloc_countries';
+    protected $idfield   = 'profile_addresses_components_enum.id';
+    protected $valfield  = 'profile_addresses_components_enum.long_name';
+    protected $from      = 'profile_addresses_components_enum';
 
-    protected $ac_join   = 'INNER JOIN profile_addresses ON (geoloc_countries.iso_3166_1_a2 = profile_addresses.countryId)';
-    protected $ac_unique = 'profile_addresses.pid';
-    protected $ac_where  = 'profile_addresses.type = \'home\'';
+    protected $ac_join   = 'INNER JOIN profile_addresses_components ON (profile_addresses_components.component_id = profile_addresses_components_enum.id)';
+    protected $ac_unique = 'profile_addresses_components.pid';
+    protected $ac_where  = 'profile_addresses_components.type = \'home\'';
 }
 // }}}
-
-// {{{ class DE_AdminAreas
-class DE_AdminAreas extends DE_WithSuboption
+// {{{ class DE_AddressesComponents extensions
+class DE_Countries extends DE_AddressesComponents
 {
-    protected $idfield   = 'geoloc_administrativeareas.id';
-    protected $optfield  = 'geoloc_administrativeareas.country';
-    protected $valfield  = 'geoloc_administrativeareas.name';
-    protected $from      = 'geoloc_administrativeareas';
+    protected $where = 'WHERE  FIND_IN_SET(\'country\', profile_addresses_components_enum.types)';
+}
 
-    protected $ac_join   = 'INNER JOIN profile_addresses ON (profile_addresses.administrativeAreaId = geoloc_administrativeareas.id)';
-    protected $ac_unique = 'profile_addresses.pid';
+class DE_Admnistrativeareas1 extends DE_AddressesComponents
+{
+    protected $where = 'WHERE  FIND_IN_SET(\'admnistrative_area_1\', profile_addresses_components_enum.types)';
 }
-// }}}
 
-// {{{ class DE_SubAdminAreas
-class DE_SubAdminAreas extends DE_WithSuboption
+class DE_Admnistrativeareas2 extends DE_AddressesComponents
 {
-    protected $idfield   = 'geoloc_subadministrativeareas.id';
-    protected $optfield  = 'geoloc_subadministrativeareas.administrativearea';
-    protected $valfield  = 'geoloc_subadministrativeareas.name';
-    protected $from      = 'geoloc_subadministrativeareas';
+    protected $where = 'WHERE  FIND_IN_SET(\'admnistrative_area_2\', profile_addresses_components_enum.types)';
+}
 
-    protected $ac_join   = 'INNER JOIN profile_addresses ON (profile_addresses.subadministrativeAreaId = geoloc_subadministrativeareas.id)';
-    protected $ac_unique = 'profile_addresses.pid';
+class DE_Admnistrativeareas3 extends DE_AddressesComponents
+{
+    protected $where = 'WHERE  FIND_IN_SET(\'admnistrative_area_3\', profile_addresses_components_enum.types)';
 }
-// }}}
 
-// {{{ class DE_Localities
-class DE_Localities extends DirEnumeration
+class DE_Localities extends DE_AddressesComponents
 {
-    protected $idfield   = 'geoloc_localities.id';
-    protected $valfield  = 'geoloc_localities.name';
-    protected $from      = 'geoloc_localities';
+    protected $where = 'WHERE  FIND_IN_SET(\'locality\', profile_addresses_components_enum.types)';
+}
 
-    protected $ac_join   = 'INNER JOIN profile_addresses ON (profile_addresses.localityID = geoloc_localities.id)';
-    protected $ac_unique = 'profile_addresses.pid';
+class DE_Sublocalities extends DE_AddressesComponents
+{
+    protected $where = 'WHERE  FIND_IN_SET(\'sublocality\', profile_addresses_components_enum.types)';
 }
+
 // }}}
 
 /** JOBS
index 825b920..f404304 100644 (file)
@@ -29,54 +29,26 @@ abstract class Geocoder {
     // Cleans the address from its geocoded data
     abstract public function stripGeocodingFromAddress(Address $address);
 
-    // Updates geoloc_administrativeareas, geoloc_subadministrativeareas and
-    // geoloc_localities databases with new geocoded data and returns the
-    // corresponding id.
-    static public function getAreaId(Address $address, $area)
+    // Updates profile_addresses_components_enum, if needed, with new
+    // geocoded data and returns the corresponding id.
+    static public function getComponentId(array $component)
     {
-        static $databases = array(
-            'administrativeArea'    => 'geoloc_administrativeareas',
-            'subAdministrativeArea' => 'geoloc_subadministrativeareas',
-            'locality'              => 'geoloc_localities',
-        );
-        static $extras = array(
-            'subAdministrativeArea' => array(
-                'field' => 'administrativearea',
-                'name'  => 'administrativeAreaName'
-            )
-        );
-
-        $areaName = $area . 'Name';
-        $areaNameLocal = $areaName . 'Local';
-        $areaId = $area . 'Id';
-        if (!is_null($address->$areaName) && isset($databases[$area])) {
-            $extra = (isset($extras[$area]) ? $extras[$area]['administrativeAreaName'] : false);
+        $where_types = array();
+        foreach ($component['types'] as $type) {
+            $where_types[] = XDB::format('FIND_IN_SET({?}, types)', $type);
+        }
 
-            $res = XDB::query('SELECT  id, nameLocal
-                                 FROM  ' . $databases[$area] . '
-                                WHERE  name = {?}',
-                              $address->$areaName);
-            if ($res->numRows() == 0) {
-                XDB::execute('INSERT INTO  ' . $databases[$area] . ' (name, nameLocal, country' .
-                                           ($extra ? ', ' . $extras[$area]['field'] : '') . ')
-                                   VALUES  ({?}, {?}, {?}' . ($extra ? ', {?}' : '') . ')',
-                             $address->$areaName, $address->$areaNameLocal, $address->countryId,
-                             ($extra ? $address->$extra : null));
-                $address->$areaId = XDB::insertId();
-            } else {
-                // XXX: remove this once all areas have both nameLocal and name.
-                list($id, $name) = $res->fetchOneRow();
-                if (is_null($name) && !is_null($address->$areaNameLocal)) {
-                    XDB::execute('UPDATE  ' . $databases[$area] . '
-                                     SET  nameLocal = {?}
-                                   WHERE  id = {?}',
-                                 $address->$areaNameLocal, $id);
-                }
-                $address->$areaId = $id;
-            }
-        } elseif (empty($address->$areaId)) {
-            $address->$areaId = null;
+        $id = XDB::fetchOneCell('SELECT  id
+                                   FROM  profile_addresses_components_enum
+                                  WHERE  short_name = {?} AND long_name = {?} AND ' . implode(' AND ', $where_types),
+                                $component['short_name'], $component['long_name']);
+        if (is_null($id)) {
+            XDB::execute('INSERT INTO  profile_addresses_components_enum (short_name, long_name, types)
+                               VALUES  ({?}, {?}, {?})',
+                         $component['short_name'], $component['long_name'], implode(',', $component['types']));
+            $id = XDB::insertId();
         }
+        return $id;
     }
 
     // Returns the part of the text preceeding the line with the postal code
@@ -101,24 +73,6 @@ abstract class Geocoder {
         }
         return $firstLines;
     }
-
-    // Returns the number of non geocoded addresses for a user.
-    static public function countNonGeocoded($pid, $jobid = null, $type = Address::LINK_PROFILE)
-    {
-        $where = array();
-        if (!is_null($pid)) {
-            $where[] = XDB::format('pid = {?}', $pid);
-        }
-        if (!is_null($jobid)) {
-            $where[] = XDB::format('jobid = {?}', $jobid);
-        }
-        $where[] = XDB::format('FIND_IN_SET({?}, type) AND accuracy = 0', $type);
-        $res = XDB::query('SELECT  COUNT(*)
-                             FROM  profile_addresses
-                            WHERE  ' . implode(' AND ', $where),
-                          $pid);
-        return $res->fetchOneCell();
-    }
 }
 
 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
index ec42adf..5321c72 100644 (file)
  *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA                *
  ***************************************************************************/
 
-// Implementation of a Geocoder using the Google Maps API. Please refer to
-// the following links for details:
-// http://code.google.com/apis/maps/documentation/services.html#Geocoding
-// http://code.google.com/intl/en/apis/maps/documentation/geocoding/
-// http://code.google.com/apis/maps/documentation/reference.html#GGeoAddressAccuracy
+// Implementation of a Geocoder using the Google Maps API v3. Please refer
+// to the following link for details:
+// http://code.google.com/apis/maps/documentation/geocoding/
 //
-// It requires the properties gmaps_key and gmaps_url to be defined in section
-// Geocoder in plat/al's configuration (platal.ini & platal.conf).
+// It requires the properties gmaps_url to be defined in section Geocoder
+// in plat/al's configuration (platal.ini & platal.conf).
 class GMapsGeocoder extends Geocoder {
 
     // Maximum number of Geocoding calls to the Google Maps API.
     const MAX_GMAPS_RPC_CALLS = 5;
-    // Maximum levenshtein distance authorized between input and geocoded text in a single line.
-    const MAX_LINE_DISTANCE = 5;
-    // Maximum levenshtein distance authorized between input and geocoded text in the whole text.
-    const MAX_TOTAL_DISTANCE = 6;
 
     public function getGeocodedAddress(Address $address, $defaultLanguage = null, $forceLanguage = false) {
         $this->prepareAddress($address);
         $textAddress = $this->getTextToGeocode($address->text);
         if (is_null($defaultLanguage)) {
-            $defaultLanguage = Platal::globals()->geocoder->gmaps_hl;
+            $defaultLanguage = Platal::globals()->geocoder->gmaps_language;
         }
 
         // Try to geocode the full address.
+        $address->geocoding_calls = 1;
         if (($geocodedData = $this->getPlacemarkForAddress($textAddress, $defaultLanguage))) {
             $this->getUpdatedAddress($address, $geocodedData, null, $forceLanguage);
             return;
@@ -58,6 +53,7 @@ class GMapsGeocoder extends Geocoder {
         for ($i = max(1, $linesCount - self::MAX_GMAPS_RPC_CALLS + 1); $i < $linesCount; ++$i) {
             $extraLines = implode("\n", array_slice($addressLines, 0, $i));
             $toGeocode  = implode("\n", array_slice($addressLines, $i));
+            ++$address->geocoding_calls;
             if (($geocodedData = $this->getPlacemarkForAddress($toGeocode, $defaultLanguage))) {
                 $this->getUpdatedAddress($address, $geocodedData, $extraLines, $forceLanguage);
                 return;
@@ -66,16 +62,16 @@ class GMapsGeocoder extends Geocoder {
     }
 
     public function stripGeocodingFromAddress(Address $address) {
-        $address->geocodedText = null;
-        $address->geoloc_choice = null;
-        $address->countryId = null;
-        $address->country = null;
-        $address->administrativeAreaName = null;
-        $address->subAdministrativeAreaName = null;
-        $address->localityName = null;
-        $address->thoroughfareName = null;
-        $address->postalCode = null;
-        $address->accuracy = 0;
+        $address->formatted_address = '';
+        $address->types = '';
+        $address->latitude = null;
+        $address->longitude = null;
+        $address->southwest_latitude = null;
+        $address->southwest_longitude = null;
+        $address->northeast_latitude = null;
+        $address->northeast_longitude = null;
+        $address->location_type = null;
+        $address->partial_match = false;
     }
 
     // Updates the address with the geocoded information from Google Maps. Also
@@ -92,7 +88,7 @@ class GMapsGeocoder extends Geocoder {
         $url     = $this->getGeocodingUrl($address, $defaultLanguage);
         $geoData = $this->getGeoJsonFromUrl($url);
 
-        return ($geoData ? $this->getPlacemarkFromJson($geoData) : null);
+        return ($geoData ? $this->getPlacemarkFromJson($geoData, $url) : null);
     }
 
     // Prepares address to be geocoded
@@ -106,16 +102,13 @@ class GMapsGeocoder extends Geocoder {
         global $globals;
 
         $parameters = array(
-            'key'    => $globals->geocoder->gmaps_key,
-            'sensor' => 'false',   // The queried address wasn't obtained from a GPS sensor.
-            'hl'     => $defaultLanguage,
-            'oe'     => 'utf8',    // Output encoding.
-            'output' => 'json',    // Output format.
-            'gl'     => $globals->geocoder->gmaps_gl,
-            'q'      => $address,  // The queries address.
+            'language' => $defaultLanguage,
+            'region'   => $globals->geocoder->gmaps_region,
+            'sensor'   => 'false',  // The queried address wasn't obtained from a GPS sensor.
+            'address'  => $address, // The queries address.
         );
 
-        return $globals->geocoder->gmaps_url . '?' . http_build_query($parameters);
+        return $globals->geocoder->gmaps_url . 'json?' . http_build_query($parameters);
     }
 
     // Fetches JSON-encoded data from a Google Maps API url, and decode them.
@@ -152,131 +145,43 @@ class GMapsGeocoder extends Geocoder {
     }
 
     // Extracts the most appropriate placemark from the JSON data fetched from
-    // Google Maps. Returns a Placemark array on success, and null otherwise. See
-    // http://code.google.com/apis/maps/documentation/services.html#Geocoding_Structured
-    // for details on the Placemark structure.
-    private function getPlacemarkFromJson(array $data) {
-        // Check for geocoding failures.
-        if (!isset($data['Status']['code']) || $data['Status']['code'] != 200) {
-            // TODO: handle non-200 codes in a better way, since the code might
-            // indicate a temporary error on Google's side.
+    // Google Maps. Returns a Placemark array on success, and null otherwise.
+    // http://code.google.com/apis/maps/documentation/geocoding/#StatusCodes
+    private function getPlacemarkFromJson(array $data, $url) {
+        // Check for geocoding status.
+        $status = $data['status'];
+
+        // If no result, return null.
+        if ($status == 'ZERO_RESULTS') {
             return null;
         }
 
-        // Check that at least one placemark was found.
-        if (count($data['Placemark']) == 0) {
-            return null;
+        // If there are results return the first one.
+        if ($status == 'OK') {
+            return $data['results'][0];
         }
 
-        // Extract the placemark with the best accuracy. This is not always the
-        // best result (since the same address may yield two different placemarks).
-        $result = $data['Placemark'][0];
-        foreach ($data['Placemark'] as $place) {
-            if ($place['AddressDetails']['Accuracy'] > $result['AddressDetails']['Accuracy']) {
-                $result = $place;
-            }
-        }
-
-        return $result;
+        // Report the error.
+        $mailer = new PlMailer('profile/geocoding.mail.tpl');
+        $mailer->assign('status', $status);
+        $mailer->assign('url', $url);
+        $mailer->send();
+        return null;
     }
 
     // Fills the address with the geocoded data
     private function fillAddressWithGeocoding(Address $address, $geocodedData, $isLocal) {
-        // The geocoded address three is
-        // Country -> AdministrativeArea -> SubAdministrativeArea -> Locality -> Thoroughfare
-        // with all the possible shortcuts
-        // The address is formatted as xAL, or eXtensible Address Language, an international
-        // standard for address formatting.
-        // xAL documentation: http://www.oasis-open.org/committees/ciq/ciq.html#6
-        if ($isLocal) {
-            $ext = 'Local';
-        } else {
-            $ext = ucfirst(Platal::globals()->geocoder->gmaps_hl);
-            $address->geocodedText = str_replace(', ', "\n", $geocodedData['address']);
-        }
-
-        if (isset($geocodedData['AddressDetails']['Accuracy'])) {
-            $address->accuracy = $geocodedData['AddressDetails']['Accuracy'];
-        }
-
-        $currentPosition = $geocodedData['AddressDetails'];
-        if (isset($currentPosition['Country'])) {
-            $country = 'country' . $ext;
-            $currentPosition    = $currentPosition['Country'];
-            $address->countryId = $currentPosition['CountryNameCode'];
-            $address->$country  = $currentPosition['CountryName'];
-        }
-        if (isset($currentPosition['AdministrativeArea'])) {
-            $administrativeAreaName = 'administrativeAreaName' . $ext;
-            $currentPosition                  = $currentPosition['AdministrativeArea'];
-            $address->$administrativeAreaName = $currentPosition['AdministrativeAreaName'];
-        }
-        if (isset($currentPosition['SubAdministrativeArea'])) {
-            $subAdministrativeAreaName = 'subAdministrativeAreaName' . $ext;
-            $currentPosition                     = $currentPosition['SubAdministrativeArea'];
-            $address->$subAdministrativeAreaName = $currentPosition['SubAdministrativeAreaName'];
-        }
-        if (isset($currentPosition['Locality'])) {
-            $localityName = 'localityName' . $ext;
-            $currentPosition        = $currentPosition['Locality'];
-            $address->$localityName = $currentPosition['LocalityName'];
-        }
-        if (isset($currentPosition['PostalCode'])) {
-            $address->postalCode = $currentPosition['PostalCode']['PostalCodeNumber'];
-        }
-
-        // Gets coordinates.
-        if (isset($geocodedData['Point']['coordinates'][0])) {
-            $address->latitude = $geocodedData['Point']['coordinates'][0];
-        }
-        if (isset($geocodedData['Point']['coordinates'][1])) {
-            $address->longitude = $geocodedData['Point']['coordinates'][1];
-        }
-        if (isset($geocodedData['ExtendedData']['LatLonBox']['north'])) {
-            $address->north = $geocodedData['ExtendedData']['LatLonBox']['north'];
-        }
-        if (isset($geocodedData['ExtendedData']['LatLonBox']['south'])) {
-            $address->south = $geocodedData['ExtendedData']['LatLonBox']['south'];
-        }
-        if (isset($geocodedData['ExtendedData']['LatLonBox']['east'])) {
-            $address->east = $geocodedData['ExtendedData']['LatLonBox']['east'];
-        }
-        if (isset($geocodedData['ExtendedData']['LatLonBox']['west'])) {
-            $address->west = $geocodedData['ExtendedData']['LatLonBox']['west'];
-        }
-    }
-
-    // Compares the geocoded address with the given address and returns true
-    // iff their are close enough to be considered as equals or not.
-    private function compareAddress($address)
-    {
-        $same = true;
-        $geoloc = strtoupper(preg_replace(array("/[0-9,\"'#~:;_\- ]/", "/\r\n/"),
-                                          array('', "\n"), $address->geocodedText));
-        $text   = strtoupper(preg_replace(array("/[0-9,\"'#~:;_\- ]/", "/\r\n/"),
-                                          array('', "\n"), $address->text));
-        $arrayGeoloc = explode("\n", $geoloc);
-        $arrayText   = explode("\n", $text);
-        $countGeoloc = count($arrayGeoloc);
-        $countText   = count($arrayText);
-
-        $totalDistance = 0;
-        if (($countText > $countGeoloc) || ($countText < $countGeoloc - 1)
-            || (($countText == $countGeoloc - 1)
-                && ($arrayText[$countText - 1] == strtoupper($address->country)))) {
-            $same = false;
-        } else {
-            for ($i = 0; $i < $countGeoloc && $i < $countText; ++$i) {
-                $lineDistance = levenshtein($arrayText[$i], trim($arrayGeoloc[$i]));
-                $totalDistance += $lineDistance;
-                if ($lineDistance > self::MAX_LINE_DISTANCE || $totalDistance > self::MAX_TOTAL_DISTANCE) {
-                    $same = false;
-                    break;
-                }
-            }
-        }
-
-        return $same;
+        $address->types               = implode(',', $geocodedData['types']);
+        $address->formatted_address   = $geocodedData['formatted_address'];
+        $address->components          = $geocodedData['address_components'];
+        $address->latitude            = $geocodedData['geometry']['location']['lat'];
+        $address->longitude           = $geocodedData['geometry']['location']['lng'];
+        $address->southwest_latitude  = $geocodedData['geometry']['viewport']['southwest']['lat'];
+        $address->southwest_longitude = $geocodedData['geometry']['viewport']['southwest']['lng'];
+        $address->northeast_latitude  = $geocodedData['geometry']['viewport']['northeast']['lat'];
+        $address->northeast_longitude = $geocodedData['geometry']['viewport']['northeast']['lng'];
+        $address->location_type       = $geocodedData['geometry']['location_type'];
+        $address->partial_match       = isset($geocodedData['partial_match']) ? true : false;
     }
 
     // Formats the text of the geocoded address using the unused data and
@@ -284,13 +189,8 @@ class GMapsGeocoder extends Geocoder {
     // will be asked to choose between them.
     private function formatAddress(Address $address, $extraLines, $forceLanguage)
     {
-        if ($extraLines) {
-            $address->geocodedText = $extraLines . "\n" . $address->geocodedText;
-        }
-
-        if ($this->compareAddress($address)) {
-            $address->geocodedText = null;
-        } elseif (!$forceLanguage) {
+        /* XXX: Check how to integrate this in the new geocoding system.
+        if (!$forceLanguage) {
             $languages = XDB::fetchOneCell('SELECT  IF(ISNULL(gc1.belongsTo), gl1.language, gl2.language)
                                               FROM  geoloc_countries AS gc1
                                         INNER JOIN  geoloc_languages AS gl1 ON (gc1.iso_3166_1_a2 = gl1.iso_3166_1_a2)
@@ -300,21 +200,13 @@ class GMapsGeocoder extends Geocoder {
                                            $address->countryId);
             $toGeocode = substr($address->text, strlen($extraLines));
             foreach (explode(',', $languages) as $language) {
-                if ($language != Platal::globals()->geocoder->gmaps_hl) {
+                if ($language != Platal::globals()->geocoder->gmaps_language) {
                     $geocodedData = $this->getPlacemarkForAddress($toGeocode, $language);
-                    $address->geocodedText = str_replace(', ', "\n", $geocodedData['address']);
-                    if ($extraLines) {
-                        $address->geocodedText = $extraLines . "\n" . $address->geocodedText;
-                    }
-                    if ($this->compareAddress($address)) {
-                        $this->fillAddressWithGeocoding($address, $geocodedData, true);
-                        $address->geocodedText = null;
-                        break;
-                    }
+                    $this->fillAddressWithGeocoding($address, $geocodedData, true);
+                    break;
                 }
             }
-            $address->geocodedText = str_replace("\n", "\r\n", $address->geocodedText);
-        }
+        }*/
         $address->text = str_replace("\n", "\r\n", $address->text);
     }
 
index 177155a..9c7cc3e 100644 (file)
@@ -285,6 +285,25 @@ class Profile implements PlExportable
         return 0;
     }
 
+    // Returns the profile's color.
+    public function promoColor()
+    {
+        switch ($this->mainEducation()) {
+          case 'X':
+            if (($this->yearpromo() % 2) === 0) {
+                return 'red';
+            } else {
+                return 'yellow';
+            }
+          case 'M':
+            return 'green';
+          case 'D':
+            return 'blue';
+          default:
+            return 'gray';
+        }
+    }
+
     /** Print a name with the given formatting:
      * %s = • for women
      * %f = firstname
@@ -380,15 +399,15 @@ class Profile implements PlExportable
     public function nationalities()
     {
         $nats = array();
-        $countries = DirEnum::getOptions(DirEnum::COUNTRIES);
+        $nationalities = DirEnum::getOptions(DirEnum::NATIONALITIES);
         if ($this->nationality1) {
-            $nats[$this->nationality1] = $countries[$this->nationality1];
+            $nats[$this->nationality1] = $nationalities[$this->nationality1];
         }
         if ($this->nationality2) {
-            $nats[$this->nationality2] = $countries[$this->nationality2];
+            $nats[$this->nationality2] = $nationalities[$this->nationality2];
         }
         if ($this->nationality3) {
-            $nats[$this->nationality3] = $countries[$this->nationality3];
+            $nats[$this->nationality3] = $nationalities[$this->nationality3];
         }
         return $nats;
     }
index 2834636..963f208 100644 (file)
@@ -993,44 +993,28 @@ class UserFilter extends PlFilter
 
     /** ADDRESSES
      */
-    private $with_pa = false;
-    public function addAddressFilter()
+    private $types = array();
+    public function addAddressFilter($type)
     {
         $this->requireProfiles();
         $this->with_pa = true;
-        return 'pa';
-    }
-
-    private $with_pac = false;
-    public function addAddressCountryFilter()
-    {
-        $this->requireProfiles();
-        $this->addAddressFilter();
-        $this->with_pac = true;
-        return 'gc';
-    }
 
-    private $with_pal = false;
-    public function addAddressLocalityFilter()
-    {
-        $this->requireProfiles();
-        $this->addAddressFilter();
-        $this->with_pal = true;
-        return 'gl';
+        $sub = '_' . $this->option++;
+        $this->types[$type] = $sub;
+        return $sub;
     }
 
     protected function addressJoins()
     {
         $joins = array();
-        if ($this->with_pa) {
-            $joins['pa'] = PlSqlJoin::left('profile_addresses', '$ME.pid = $PID');
-        }
-        if ($this->with_pac) {
-            $joins['gc'] = PlSqlJoin::left('geoloc_countries', '$ME.iso_3166_1_a2 = pa.countryID');
-        }
-        if ($this->with_pal) {
-            $joins['gl'] = PlSqlJoin::left('geoloc_localities', '$ME.id = pa.localityID');
+        foreach ($this->types as $type => $sub) {
+            $joins['pa' . $sub] = PlSqlJoin::inner('profile_addresses', '$ME.pid = $PID');
+            $joins['pac' . $sub] = PlSqlJoin::inner('profile_addresses_components',
+                                                    '$ME.pid = pa' . $sub . '.pid AND $ME.jobid = pa' . $sub . '.jobid AND $ME.groupid = pa' . $sub . '.groupid AND $ME.type = pa' . $sub . '.type AND $ME.id = pa' . $sub . '.id');
+            $joins['pace' . $sub] = PlSqlJoin::inner('profile_addresses_components_enum',
+                                                     '$ME.id = pac' . $sub . '.component_id AND FIND_IN_SET({?}, $ME.types)', $type);
         }
+
         return $joins;
     }
 
index 729c911..c798f9d 100644 (file)
@@ -1006,17 +1006,20 @@ class UFC_Email extends UserFilterCondition
 // {{{ class UFC_Address
 abstract class UFC_Address extends UserFilterCondition
 {
-    /** Valid address type ('hq' is reserved for company addresses)
+    /** Valid address type
      */
-    const TYPE_HOME = 1;
-    const TYPE_PRO  = 2;
-    const TYPE_ANY  = 3;
+    const TYPE_HOME     = 1;
+    const TYPE_PRO      = 2;
+    const TYPE_NON_HQ   = 3;
+    const TYPE_HQ       = 4;
+    const TYPE_ANY      = 7;
 
     /** Text for these types
      */
     protected static $typetexts = array(
         self::TYPE_HOME => 'home',
         self::TYPE_PRO  => 'pro',
+        self::TYPE_HQ   => 'hq',
     );
 
     protected $type;
@@ -1061,13 +1064,13 @@ abstract class UFC_Address extends UserFilterCondition
             }
         }
         if (count($types)) {
-            $conds[] = XDB::format($sub . '.type IN {?}', $types);
+            $conds[] = XDB::format('pa' . $sub . '.type IN {?}', $types);
         }
 
         if ($this->flags != self::FLAG_ANY) {
             foreach(self::$flagtexts as $flag => $text) {
                 if ($flag & $this->flags) {
-                    $conds[] = 'FIND_IN_SET(' . XDB::format('{?}', $text) . ', ' . $sub . '.flags)';
+                    $conds[] = 'FIND_IN_SET(' . XDB::format('{?}', $text) . ', pa' . $sub . '.flags)';
                 }
             }
         }
@@ -1076,61 +1079,6 @@ abstract class UFC_Address extends UserFilterCondition
 
 }
 // }}}
-// {{{ class UFC_AddressText
-/** Select users based on their address, using full text search
- * @param $text Text for filter in fulltext search
- * @param $textSearchMode Mode for search (one of XDB::WILDCARD_*)
- * @param $type Filter on address type
- * @param $flags Filter on address flags
- * @param $country Filter on address country
- * @param $locality Filter on address locality
- */
-class UFC_AddressText extends UFC_Address
-{
-
-    private $text;
-    private $textSearchMode;
-
-    public function __construct($text = null, $textSearchMode = XDB::WILDCARD_CONTAINS,
-        $type = null, $flags = self::FLAG_ANY, $country = null, $locality = null)
-    {
-        parent::__construct($type, $flags);
-        $this->text           = $text;
-        $this->textSearchMode = $textSearchMode;
-        $this->country        = $country;
-        $this->locality       = $locality;
-    }
-
-    private function mkMatch($txt)
-    {
-        return XDB::formatWildcards($this->textSearchMode, $txt);
-    }
-
-    public function buildCondition(PlFilter $uf)
-    {
-        $sub = $uf->addAddressFilter();
-        $conds = $this->initConds($sub, $uf->getVisibilityCondition($sub . '.pub'));
-        if ($this->text != null) {
-            $conds[] = $sub . '.text' . $this->mkMatch($this->text);
-        }
-
-        if ($this->country != null) {
-            $subc = $uf->addAddressCountryFilter();
-            $subconds = array();
-            $subconds[] = $subc . '.country' . $this->mkMatch($this->country);
-            $subconds[] = $subc . '.countryFR' . $this->mkMatch($this->country);
-            $conds[] = implode(' OR ', $subconds);
-        }
-
-        if ($this->locality != null) {
-            $subl = $uf->addAddressLocalityFilter();
-            $conds[] = $subl . '.name' . $this->mkMatch($this->locality);
-        }
-
-        return implode(' AND ', $conds);
-    }
-}
-// }}}
 // {{{ class UFC_AddressField
 /** Filters users based on their address,
  * @param $val Either a code for one of the fields, or an array of such codes
@@ -1138,55 +1086,36 @@ class UFC_AddressText extends UFC_Address
  * @param $type Filter on address type
  * @param $flags Filter on address flags
  */
-class UFC_AddressField extends UFC_Address
+class UFC_AddressComponent extends UFC_Address
 {
-    const FIELD_COUNTRY    = 1;
-    const FIELD_ADMAREA    = 2;
-    const FIELD_SUBADMAREA = 3;
-    const FIELD_LOCALITY   = 4;
-    const FIELD_ZIPCODE    = 5;
+    static $components = array('sublocality', 'locality', 'administrative_area_level_3', 'administrative_area_level_2', 'administrative_area_level_1', 'country');
 
     /** Data of the filter
      */
     private $val;
     private $fieldtype;
+    private $exact;
 
-    public function __construct($val, $fieldtype, $type = null, $flags = self::FLAG_ANY)
+    public function __construct($val, $fieldtype, $exact = true, $type = null, $flags = self::FLAG_ANY)
     {
-        parent::__construct($type, $flags);
+        if (!in_array($fieldtype, self::$components)) {
+            Platal::page()->killError('Invalid address field type: ' . $this->fieldtype);
+        }
 
+        parent::__construct($type, $flags);
         if (!is_array($val)) {
             $val = array($val);
         }
         $this->val       = $val;
         $this->fieldtype = $fieldtype;
+        $this->exact     = $exact;
     }
 
     public function buildCondition(PlFilter $uf)
     {
-        $sub = $uf->addAddressFilter();
-        $conds = $this->initConds($sub, $uf->getVisibilityCondition($sub . '.pub'));
-
-        switch ($this->fieldtype) {
-        case self::FIELD_COUNTRY:
-            $field = 'countryId';
-            break;
-        case self::FIELD_ADMAREA:
-            $field = 'administrativeAreaId';
-            break;
-        case self::FIELD_SUBADMAREA:
-            $field = 'subAdministrativeAreaId';
-            break;
-        case self::FIELD_LOCALITY:
-            $field = 'localityId';
-            break;
-        case self::FIELD_ZIPCODE:
-            $field = 'postalCode';
-            break;
-        default:
-            Platal::page()->killError('Invalid address field type: ' . $this->fieldtype);
-        }
-        $conds[] = XDB::format($sub . '.' . $field . ' IN {?}', $this->val);
+        $sub = $uf->addAddressFilter($this->fieldtype);
+        $conds = $this->initConds($sub, $uf->getVisibilityCondition('pa' . $sub . '.pub'));
+        $conds[] = XDB::format('pace' . $sub . '.id IN {?}', $this->val);
 
         return implode(' AND ', $conds);
     }
index 9ded13a..4ecaa81 100644 (file)
@@ -34,7 +34,7 @@ to=hotliners@staff.polytechnique.org
 
 [geoloc_error]
 from=webmaster@polytechnique.org
-to=non-geoloc@staff.polytechnique.org
+to=br@staff.polytechnique.org
 
 [mails_ax]
 from="Amicale des Anciens de l'X" <reponses@amicale.polytechnique.org>
index ebdb03a..6a5ce41 100644 (file)
@@ -225,21 +225,17 @@ first_promo_young = 2007
 ; Unused parameter.
 email = ""
 
-; $globals->geocoder->gmaps_key
-; API key to use when querying google maps web service
-gmaps_key = ""
-
 ; $globals->geocoder->gmaps_url
 ; URL of geocoding webservice
-gmaps_url = "http://maps.google.com/maps/geo"
+gmaps_url = "https://maps.googleapis.com/maps/api/geocode/"
 
-; $globals->geocoder->gmaps_hl
+; $globals->geocoder->gmaps_language
 ; Default output language.
-gmaps_hl = "fr"
+gmaps_language = "fr"
 
-; $globals->geocoder->gmaps_gl
+; $globals->geocoder->gmaps_region
 ; Default location preference.
-gmaps_gl = "fr"
+gmaps_region = "fr"
 
 
 ; The lists section contains parameters used to interact with mailman.
index d4899ec..3a10c17 100644 (file)
@@ -333,18 +333,22 @@ function addressChanged(prefid)
     $('#' + prefid + '_cont').find('[name*=changed]').val("1");
 }
 
-function validGeoloc(prefid, id, geoloc)
-{
-    if (geoloc == 1) {
-        $('#' + prefid + '_cont').find('[name*=text]').val($('#' + prefid + '_cont').find('[name*=geocodedText]').val());
-        $('#' + prefid + '_cont').find('[name*=postalText]').val('');
-    }
-    if (geoloc > 0) {
-        $('#' + prefid + '_cont').find("[name*='[geocodedText]']").remove();
+function deleteGeocoding(prefid, hrpid)
+{
+    var confirmation = confirm(
+        "La localisation de l'adresse sert à deux choses : te placer dans "
+        + "le planisphère et te faire apparaître dans la recherche avancée par "
+        + "pays, région, département, ville... La supprimer t'en fera disparaître. "
+        + "\nIl ne faut le faire que si cette localisation "
+        + "est réellement erronée. Avant de supprimer cette localisation, l'équipe de "
+        + "Polytechnique.org tentera de la réparer.\n\nConfirmes-tu ta "
+        + "demande de suppression de cette localisation ?");
+
+    if (confirmation) {
+        var address = $('#' + prefid).find("[name*='[text]']").val();
+        $.xpost('profile/ajax/address/del/' + hrpid, { address:address });
+        $('#' + prefid + '_geocoding_removal').html('Localisation en attente de validation.');
     }
-    $('#' + prefid + '_cont').find('[name*=text]').removeClass('error');
-    $('#' + prefid + '_cont').find('[name*=geocodeChosen]').val(geoloc);
-    $('.' + prefid + '_geoloc').remove();
 }
 
 // {{{1 Phones
index 2de1e6b..8d4ef1b 100644 (file)
@@ -735,8 +735,7 @@ class CompanyList
         }
 
         $it = XDB::iterator('SELECT  pje.id, pje.name, pje.acronym, pje.url,
-                                     pa.flags, pa.text, pa.postalCode, pa.countryId,
-                                     pa.type, pa.pub
+                                     pa.flags, pa.text, pa.type, pa.pub
                                FROM  profile_job_enum AS pje
                           LEFT JOIN  profile_addresses AS pa ON (pje.id = pa.jobid AND pa.type = \'hq\')
                                   ' . $join . '
index 7b8a979..84f354a 100644 (file)
@@ -347,10 +347,12 @@ class UFB_AdvancedSearch extends UserFilterBuilder
             new UFBF_HasEmailRedirect('has_email_redirect', 'A une redirection active'),
             new UFBF_Dead('alive', 'En vie'),
 
-            new UFBF_Town('city', 'Ville / Code Postal'),
-            new UFBF_Country('countryTxt', 'country', 'Pays'),
-            new UFBF_AdminArea('administrativearea', 'Région'),
-            new UFBF_SubAdminArea('subadministrativearea', 'Département'),
+            new UFBF_AddressIndex('sublocality', 'Arrondissement', 'SUBLOCALITIES'),
+            new UFBF_AddressIndex('administrative_area_level_3', 'Canton', 'ADMNISTRATIVEAREAS3'),
+            new UFBF_AddressIndex('administrative_area_level_2', 'Département', 'ADMNISTRATIVEAREAS2'),
+            new UFBF_AddressIndex('administrative_area_level_1', 'Région', 'ADMNISTRATIVEAREAS1'),
+            new UFBF_AddressMixed('localityTxt', 'locality', 'Ville', 'LOCALITIES'),
+            new UFBF_AddressMixed('countryTxt', 'country', 'Pays', 'COUNTRIES'),
 
             new UFBF_JobCompany('entreprise', 'Entreprise'),
             new UFBF_JobDescription('jobdescription', 'Fonction'),
@@ -1065,69 +1067,16 @@ class UFBF_Dead extends UFBF_Enum
 }
 // }}}
 
-// {{{ class UFBF_Town
-/** Retrieves a town, either from a postal code or a town name
- */
-class UFBF_Town extends UFBF_Text
-{
-    const TYPE_TEXT = 1;
-    const TYPE_ZIP  = 2;
-    const TYPE_ANY  = 3;
-
-    private $type;
-    private $onlycurrentfield;
-
-    public function __construct($envfield, $formtext = '', $type = self::TYPE_ANY, $onlycurrentfield = 'only_current')
-    {
-        $this->type = $type;
-        $this->onlycurrentfield = $onlycurrentfield;
-        parent::__construct($envfield, $formtext, 2, 30);
-    }
-
-    protected function buildUFC(UserFilterBuilder $ufb)
-    {
-        if ($ufb->isOn($this->onlycurrentfield)) {
-            $flags = UFC_Address::FLAG_CURRENT;
-        } else {
-            $flags = UFC_Address::FLAG_ANY;
-        }
-
-        if (preg_match('/[0-9]/', $this->val)) {
-            if ($this->type & self::TYPE_ZIP) {
-                return new UFC_AddressField($this->val, UFC_AddressField::FIELD_ZIPCODE, UFC_Address::TYPE_ANY, $flags);
-            } else {
-                return new PFC_False();
-            }
-        } else {
-            $byname = new UFC_AddressText(null, XDB::WILDCARD_CONTAINS, UFC_Address::TYPE_ANY, $flags, null, $this->val);
-            $byzip  = new UFC_AddressField($this->val, UFC_AddressField::FIELD_ZIPCODE, UFC_Address::TYPE_ANY, $flags);
-            if ($this->type & self::TYPE_ANY) {
-                return new PFC_Or($byname, $byzip);
-            } else if ($this->type & self::TYPE_TEXT) {
-                return $byname;
-            } else {
-                return $byzip;
-            }
-        }
-    }
-
-    public function getEnvFieldNames()
-    {
-        return array($this->envfield, $this->onlycurrentfield);
-    }
-}
-// }}}
-
-// {{{ class UFBF_Country
-class UFBF_Country extends UFBF_Mixed
+// {{{ class UFBF_AddressMixed
+class UFBF_AddressMixed extends UFBF_Mixed
 {
-    protected $direnum = DirEnum::COUNTRIES;
     protected $onlycurrentfield;
 
-    public function __construct($envfieldtext, $envfieldindex, $formtext = '', $onlycurrentfield = 'only_current')
+    public function __construct($envfieldtext, $envfieldindex, $formtext = '', $addressfield, $onlycurrentfield = 'only_current')
     {
         parent::__construct($envfieldtext, $envfieldindex, $formtext);
         $this->onlycurrentfield = $onlycurrentfield;
+        $this->direnum = constant('DirEnum::' . $addressfield);
     }
 
     protected function buildUFC(UserFilterBuilder $ufb)
@@ -1138,7 +1087,7 @@ class UFBF_Country extends UFBF_Mixed
             $flags = UFC_Address::FLAG_ANY;
         }
 
-        return new UFC_AddressField($this->val, UFC_AddressField::FIELD_COUNTRY, UFC_Address::TYPE_ANY, $flags);
+        return new UFC_AddressComponent($this->val, $this->envfieldindex, UFC_Address::TYPE_NON_HQ, $flags);
     }
 
     public function getEnvFieldNames()
@@ -1148,47 +1097,17 @@ class UFBF_Country extends UFBF_Mixed
 }
 // }}}
 
-// {{{ class UFBF_AdminArea
-class UFBF_AdminArea extends UFBF_Index
-{
-    protected $direnum = DirEnum::ADMINAREAS;
-    protected $onlycurrentfield;
-
-    public function __construct($envfield, $formtext = '', $onlycurrentfield = 'only_current')
-    {
-        parent::__construct($envfield, $formtext);
-        $this->onlycurrentfield = $onlycurrentfield;
-    }
-
-
-    protected function buildUFC(UserFilterBuilder $ufb)
-    {
-        if ($ufb->isOn($this->onlycurrentfield)) {
-            $flags = UFC_Address::FLAG_CURRENT;
-        } else {
-            $flags = UFC_Address::FLAG_ANY;
-        }
-
-        return new UFC_AddressField($this->val, UFC_AddressField::FIELD_ADMAREA, UFC_Address::TYPE_ANY, $flags);
-    }
-
-    public function getEnvFieldNames()
-    {
-        return array($this->envfield, $this->onlycurrentfield);
-    }
-}
-// }}}
-
-// {{{ class UFBF_SubAdminArea
-class UFBF_SubAdminArea extends UFBF_Index
+// {{{ class UFBF_AddressIndex
+class UFBF_AddressIndex extends UFBF_Index
 {
-    protected $direnum = DirEnum::SUBADMINAREAS;
+    protected $direnum;
     protected $onlycurrentfield;
 
-    public function __construct($envfield, $formtext = '', $onlycurrentfield = 'only_current')
+    public function __construct($envfield, $formtext = '', $addressfield, $onlycurrentfield = 'only_current')
     {
         parent::__construct($envfield, $formtext);
         $this->onlycurrentfield = $onlycurrentfield;
+        $this->direnum = constant('DirEnum::' . $addressfield);
     }
 
 
@@ -1200,7 +1119,7 @@ class UFBF_SubAdminArea extends UFBF_Index
             $flags = UFC_Address::FLAG_ANY;
         }
 
-        return new UFC_AddressField($this->val, UFC_AddressField::FIELD_SUBADMAREA, UFC_Address::TYPE_ANY, $flags);
+        return new UFC_AddressComponent($this->val, $this->envfield, UFC_Address::TYPE_NON_HQ, $flags);
     }
 
     public function getEnvFieldNames()
index eb2cd4c..35746bd 100644 (file)
@@ -624,6 +624,25 @@ abstract class ProfileValidate extends Validate
     }
 
     // }}}
+    // {{{ function get_all_typed_requests()
+
+    /** Same as get_typed_request() but return an array of objects.
+     */
+    static public function get_all_typed_requests($type)
+    {
+        $res = XDB::iterRow('SELECT  data
+                               FROM  requests
+                              WHERE  type = {?}
+                           ORDER BY  stamp',
+                            $type);
+        $array = array();
+        while (list($data) = $res->next()) {
+            $array[] = Validate::unserialize($data);
+        }
+        return $array;
+    }
+
+    // }}}
     // {{{ function get_typed_requests_count()
 
     /** Same as get_typed_requests() but returns the count of available requests.
diff --git a/include/validations/address.inc.php b/include/validations/address.inc.php
new file mode 100644 (file)
index 0000000..349674a
--- /dev/null
@@ -0,0 +1,174 @@
+<?php
+/***************************************************************************
+ *  Copyright (C) 2003-2011 Polytechnique.org                              *
+ *  http://opensource.polytechnique.org/                                   *
+ *                                                                         *
+ *  This program is free software; you can redistribute it and/or modify   *
+ *  it under the terms of the GNU General Public License as published by   *
+ *  the Free Software Foundation; either version 2 of the License, or      *
+ *  (at your option) any later version.                                    *
+ *                                                                         *
+ *  This program is distributed in the hope that it will be useful,        *
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
+ *  GNU General Public License for more details.                           *
+ *                                                                         *
+ *  You should have received a copy of the GNU General Public License      *
+ *  along with this program; if not, write to the Free Software            *
+ *  Foundation, Inc.,                                                      *
+ *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA                *
+ ***************************************************************************/
+
+// {{{ class AddressReq
+
+class AddressReq extends ProfileValidate
+{
+    // {{{ properties
+
+    // Address primary field that are not in its formatted array.
+    public $pid;
+    public $jobid;
+    public $groupid;
+    public $type;
+
+    // We need the text given by the user, and the toy version to try to improve
+    // the geocoding.
+    public $address;
+    public $given_text;
+    public $toy_text = '';
+    public $modified = false;
+
+    public $rules = 'Si la localisation est bonne, refuser. Sinon, si le texte est faux, le corriger. Si la géolocaliastion ne marche toujours pas, utiliser la version jouet qui ne sera pas stockée, mais dont les données de localisation le seront.';
+
+    // }}}
+    // {{{ constructor
+
+    public function __construct(User $_user, Profile $_profile, $_text, $_pid, $_jobid, $_groupid, $_type, $_stamp = 0)
+    {
+        parent::__construct($_user, $_profile, false, 'address', $_stamp);
+        $this->key_pid = $_pid;
+        $this->key_jobid = $_jobid;
+        $this->key_groupid = $_groupid;
+        $this->key_type = $_type;
+        $this->given_text = $_text;
+        $address = new Address(array('changed' => 1, 'text' => $_text));
+        $address->format();
+        $this->address = $address->toFormArray();
+    }
+
+    // }}}
+    // {{{ function formu()
+
+    public function formu()
+    {
+        return 'include/form.valid.address.tpl';
+    }
+
+    // }}}
+    // {{{ function editor()
+
+    public function editor()
+    {
+        return 'include/form.valid.edit-address.tpl';
+    }
+
+    // }}}
+    // {{{ function handle_editor()
+
+    protected function handle_editor()
+    {
+        $data = Post::v('valid');
+        if (isset($data['text']) && $data['text'] != $this->toy_text && $data['text'] != $this->given_text) {
+            $this->toy_text = $data['text'];
+            $address = new Address(array('changed' => 1, 'text' => $this->toy_text));
+            $address->format();
+            $this->address = $address->toFormArray();
+        }
+        $this->modified = isset($data['modified']);
+
+        return true;
+    }
+
+    // }}}
+    // {{{ function _mail_subj
+
+    protected function _mail_subj()
+    {
+        return '[Polytechnique.org/Adresse] Demande d\'amélioration de la localisation d\'une adresse';
+    }
+
+    // }}}
+    // {{{ function _mail_body
+
+    protected function _mail_body($isok)
+    {
+        if ($isok) {
+            return "  Nous avons réussit à mieux localiser l'adresse.";
+        } else {
+            return "  L'adresse est suffisemment bien localisée pour les besoins du site (recherche avancée, planisphère), nous avons donc choisi de ne pas la modifier.";
+        }
+    }
+
+    // }}}
+    // {{{ function commit()
+
+    public function commit()
+    {
+        $this->address = array_merge($this->address, array(
+            'pid' => $this->key_pid,
+            'jobid' => $this->key_jobid,
+            'groupid' => $this->key_groupid,
+            'type' => $this->key_type
+        ));
+        $this->address['text'] = ($this->modified ? $this->toy_text : $this->given_text);;
+        $this->address['changed'] = 0;
+        $address = new Address($this->address);
+        $address->format();
+        $address->updateGeocoding($this->given_text);
+
+        return true;
+    }
+
+    // }}}
+    // {{{ function get_request()
+
+    static public function get_request($pid, $jobid, $groupid, $type, $address)
+    {
+        $reqs = parent::get_typed_requests($pid, 'address');
+        foreach ($reqs as &$req) {
+            if ($req->key_pid == $pid && $req->key_jobid == $jobid && $req->key_groupid == $groupid
+                && $req->key_type == $type && $req->address['text'] == $address) {
+                return $req;
+            }
+        }
+        return null;
+    }
+
+    // }}}
+    // {{{ function purge_requests()
+
+    // Purges address localization requests based on deleted addresses.
+    static public function purge_requests($pid, $jobid, $groupid, $type)
+    {
+        $requests = parent::get_all_typed_requests('address');
+        foreach ($requests as &$req) {
+            if ($req->key_pid == $pid && $req->key_jobid == $jobid && $req->key_groupid == $groupid && $req->key_type == $type) {
+                $count = XDB::fetchOneCell('SELECT  COUNT(*)
+                                              FROM  profile_addresses
+                                             WHERE  pid = {?} AND jobid = {?} AND groupid = {?}
+                                                    AND type = {?} AND text = {?}',
+                                           $pid, $jobid, $groupid, $type, $req->address['text']);
+                if ($count == 0) {
+                    $req->clean();
+                }
+            }
+        }
+    }
+
+    // }}}
+}
+
+// }}}
+
+// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
+?>
index fa56a3c..0d1eb60 100644 (file)
@@ -38,15 +38,23 @@ function get_annuaire_infos($method, $params) {
                                 $params[1]);
             $array = $res->next();
         } else {
-            $res = XDB::iterRow("SELECT  p.birthdate, pa.text, pa.postalCode
-                                         gl.name, pa.countryId, p.pid, pa.id
-                                   FROM  profiles          AS p
-                              LEFT JOIN  profile_addresses AS pa ON (pa.pid = p.pid)
-                              LEFT JOIN  geoloc_localities AS gl ON (pl.id = pa.localityId)
+            $res = XDB::iterRow("SELECT  p.birthdate, pa.text, pace3.short_name, pace2.short_name, pace1.short_name, p.pid, pa.id
+                                   FROM  profiles                          AS p
+                              LEFT JOIN  profile_addresses                 AS pa    ON (pa.pid = p.pid)
+                              LEFT JOIN  profile_addresses_components_enum AS pace1 ON (FIND_IN_SET('country', pace1.types))
+                              LEFT JOIN  profile_addresses_components_enum AS pace2 ON (FIND_IN_SET('locality', pace2.types))
+                              LEFT JOIN  profile_addresses_components_enum AS pace3 ON (FIND_IN_SET('postal_code', pace3.types))
+                              LEFT JOIN  profile_addresses_components      AS pac1  ON (pa.pid = pac1.pid AND pa.jobid = pac1.jobid AND pa.groupid = pac1.groupid
+                                                                                        AND pa.id = pac1.id AND pa.type = pac1.type AND pace1.id = pac1.component_id)
+                              LEFT JOIN  profile_addresses_components      AS pac2  ON (pa.pid = pac2.pid AND pa.jobid = pac2.jobid AND pa.groupid = pac2.groupid
+                                                                                        AND pa.id = pac2.id AND pa.type = pac2.type AND pace2.id = pac2.component_id)
+                              LEFT JOIN  profile_addresses_components      AS pac3  ON (pa.pid = pac3.pid AND pa.jobid = pac3.jobid AND pa.groupid = pac3.groupid
+                                                                                        AND pa.id = pac3.id AND pa.type = pac3.type AND pace3.id = pac3.component_id)
                                   WHERE  p.xorg_id = {?} AND NOT FIND_IN_SET('job', pa.flags)
                                ORDER BY  NOT FIND_IN_SET('current', pa.flags),
                                          FIND_IN_SET('secondary', pa.flags),
-                                         NOT FIND_IN_SET('mail', pa.flags)",
+                                         NOT FIND_IN_SET('mail', pa.flags)
+                               GROUP BY  pa.pid, pa.jobid, pa.groupid, pa.id, pa.type",
                                 $params[1]);
             // Process the addresses we got.
             if(list($age, $text, $adr['cp'], $adr['ville'],
index ab1ed47..b58cea6 100644 (file)
@@ -67,15 +67,23 @@ class PayPal
         );
 
         if ($user->hasProfile()) {
-            $res = XDB::query("SELECT  pa.text, gl.name AS city, pa.postalCode AS zip, pa.countryId AS country,
+            $res = XDB::query("SELECT  pa.text, pace2.short_name AS city, pace3.short_name AS zip, pace1.short_name AS country,
                                        IF(pp1.display_tel != '', pp1.display_tel, pp2.display_tel) AS night_phone_b
-                                 FROM  profile_addresses AS pa
-                            LEFT JOIN  profile_phones    AS pp1 ON (pp1.pid = pa.pid AND pp1.link_type = 'address'
-                                                                    AND pp1.link_id = pa.id)
-                            LEFT JOIN  profile_phones    AS pp2 ON (pp2.pid = pa.pid AND pp2.link_type = 'user'
-                                                                    AND pp2.link_id = 0)
-                            LEFT JOIN  geoloc_localities AS gl  ON (gl.id = pa.localityId)
+                                 FROM  profile_addresses                 AS pa
+                            LEFT JOIN  profile_phones                    AS pp1   ON (pp1.pid = pa.pid AND pp1.link_type = 'address' AND pp1.link_id = pa.id)
+                            LEFT JOIN  profile_phones                    AS pp2   ON (pp2.pid = pa.pid AND pp2.link_type = 'user' AND pp2.link_id = 0)
+                            LEFT JOIN  profile_addresses_components_enum AS pace1 ON (FIND_IN_SET('country', pace1.types))
+                            LEFT JOIN  profile_addresses_components_enum AS pace2 ON (FIND_IN_SET('locality', pace2.types))
+                            LEFT JOIN  profile_addresses_components_enum AS pace3 ON (FIND_IN_SET('postal_code', pace3.types))
+                            LEFT JOIN  profile_addresses_components      AS pac1  ON (pa.pid = pac1.pid AND pa.jobid = pac1.jobid AND pa.groupid = pac1.groupid
+                                                                                      AND pa.id = pac1.id AND pa.type = pac1.type AND pace1.id = pac1.component_id)
+                            LEFT JOIN  profile_addresses_components      AS pac2  ON (pa.pid = pac2.pid AND pa.jobid = pac2.jobid AND pa.groupid = pac2.groupid
+                                                                                      AND pa.id = pac2.id AND pa.type = pac2.type AND pace2.id = pac2.component_id)
+                            LEFT JOIN  profile_addresses_components      AS pac3  ON (pa.pid = pac3.pid AND pa.jobid = pac3.jobid AND pa.groupid = pac3.groupid
+                                                                                      AND pa.id = pac3.id AND pa.type = pac3.type AND pace3.id = pac3.component_id)
+
                                 WHERE  pa.pid = {?} AND FIND_IN_SET('current', pa.flags)
+                             GROUP BY  pa.pid, pa.jobid, pa.groupid, pa.id, pa.type
                                 LIMIT  1",
                               $user->profile()->id());
             $this->infos['client'] = array_map('replace_accent', array_merge($info_client, $res->fetchOneAssoc()));
index b45d510..4b830fc 100644 (file)
@@ -33,6 +33,7 @@ class ProfileModule extends PLModule
             'profile/ax'                 => $this->make_hook('ax',                         AUTH_COOKIE, 'admin,edit_directory'),
             'profile/edit'               => $this->make_hook('p_edit',                     AUTH_MDP),
             'profile/ajax/address'       => $this->make_hook('ajax_address',               AUTH_COOKIE, 'user', NO_AUTH),
+            'profile/ajax/address/del'   => $this->make_hook('ajax_address_del',           AUTH_MDP),
             'profile/ajax/tel'           => $this->make_hook('ajax_tel',                   AUTH_COOKIE, 'user', NO_AUTH),
             'profile/ajax/edu'           => $this->make_hook('ajax_edu',                   AUTH_COOKIE, 'user', NO_AUTH),
             'profile/ajax/medal'         => $this->make_hook('ajax_medal',                 AUTH_COOKIE, 'user', NO_AUTH),
@@ -359,6 +360,7 @@ class ProfileModule extends PLModule
         }
 
        $page->setTitle('Mon Profil');
+       $page->assign('hrpid', $profile->hrid());
        if (isset($success) && $success) {
            $page->trigSuccess('Ton profil a bien été mis à jour.');
        }
@@ -402,6 +404,20 @@ class ProfileModule extends PLModule
         $page->assign('address', array());
     }
 
+    function handler_ajax_address_del($page, $hrpid)
+    {
+        if ($profile = Profile::get($hrpid)) {
+            if (S::user()->canEdit($profile)) {
+                $address = Post::t('address');
+                if (is_null(AddressReq::get_request($profile->id(), 0, 0, Address::LINK_PROFILE, $address))) {
+                    $req = new AddressReq(S::user(), $profile, $address, $profile->id(), 0, 0, Address::LINK_PROFILE);
+                    $req->submit();
+                }
+            }
+        }
+        exit();
+    }
+
     function handler_ajax_tel($page, $prefid, $prefname, $telid, $subField, $mainField, $mainId)
     {
         pl_content_headers("text/html");
index 2961f1e..62d21ea 100644 (file)
@@ -48,6 +48,7 @@ class ProfileSettingAddresses implements ProfileSetting
         Phone::deletePhones($page->pid(), Phone::LINK_ADDRESS, null, $deletePrivate);
         Address::deleteAddresses($page->pid(), Address::LINK_PROFILE, null, null, $deletePrivate);
         Address::saveFromArray($value, $page->pid(), Address::LINK_PROFILE, null, $deletePrivate);
+        AddressReq::purge_requests($page->pid(), 0, 0, Address::LINK_PROFILE);
         if (S::user()->isMe($page->owner) && count($value) > 1) {
             Platal::page()->trigWarning('Attention, tu as plusieurs adresses sur ton profil. Pense à supprimer celles qui sont obsolètes.');
         }
@@ -68,6 +69,7 @@ class ProfilePageAddresses extends ProfilePage
         parent::__construct($wiz);
         $this->settings['addresses'] = new ProfileSettingAddresses();
         $this->watched['addresses']  = true;
+        Platal::page()->assign('geocoding_removal', true);
     }
 }
 
index b9ae916..9d7301a 100644 (file)
@@ -29,6 +29,7 @@ class SearchModule extends PLModule
             'advanced_search.php'       => $this->make_hook('redir_advanced',     AUTH_PUBLIC),
             'search/autocomplete'       => $this->make_hook('autocomplete',       AUTH_COOKIE, 'directory_ax', NO_AUTH),
             'search/list'               => $this->make_hook('list',               AUTH_COOKIE, 'directory_ax', NO_AUTH),
+            'search/list/count'         => $this->make_hook('list_count',         AUTH_COOKIE, 'directory_ax', NO_AUTH),
             'jobs'                      => $this->make_hook('referent',           AUTH_COOKIE),
             'emploi'                    => $this->make_hook('referent',           AUTH_COOKIE),
             'referent/search'           => $this->make_hook('referent',           AUTH_COOKIE),
@@ -247,7 +248,7 @@ class SearchModule extends PLModule
             'groupexTxt'         => DirEnum::GROUPESX,
             'sectionTxt'         => DirEnum::SECTIONS,
             'networking_typeTxt' => DirEnum::NETWORKS,
-            'city'               => DirEnum::LOCALITIES,
+            'localityTxt'        => DirEnum::LOCALITIES,
             'countryTxt'         => DirEnum::COUNTRIES,
             'entreprise'         => DirEnum::COMPANIES,
             'jobtermTxt'         => DirEnum::JOBTERMS,
@@ -308,7 +309,23 @@ class SearchModule extends PLModule
             break;
           case 'country':
             $ids = DirEnum::getOptionsIter(DirEnum::COUNTRIES);
-            $page->assign('onchange', 'changeCountry(this.value)');
+            $page->assign('onchange', 'changeAddressComponents(\'' . $type . '\', this.value)');
+            break;
+          case 'administrative_area_level_1':
+          case 'administrative_area_level_2':
+          case 'administrative_area_level_3':
+          case 'locality':
+            $page->assign('onchange', 'changeAddressComponents(\'' . $type . '\', this.value)');
+          case 'sublocality':
+            $ids = XDB::iterator("SELECT  pace1.id, pace1.long_name AS field
+                                    FROM  profile_addresses_components_enum AS pace1
+                              INNER JOIN  profile_addresses_components      AS pac1  ON (pac1.component_id = pace1.id)
+                              INNER JOIN  profile_addresses_components      AS pac2  ON (pac1.pid = pac2.pid AND pac1.jobid = pac2.jobid AND pac1.id = pac2.id
+                                                                                         AND pac1.groupid = pac2.groupid AND pac1.type = pac2.type)
+                              INNER JOIN  profile_addresses_components_enum AS pace2 ON (pac2.component_id = pace2.id AND FIND_IN_SET({?}, pace2.types))
+                                   WHERE  pace2.id = {?} AND FIND_IN_SET({?}, pace1.types)
+                                GROUP BY  pace1.long_name",
+                                 Env::v('previous'), Env::v('value'), $type);
             break;
           case 'diploma':
             if (Env::has('school') && Env::i('school') != 0) {
@@ -323,21 +340,6 @@ class SearchModule extends PLModule
           case 'nationalite':
             $ids = DirEnum::getOptionsIter(DirEnum::NATIONALITIES);
             break;
-          case 'administrativearea':
-            if (Env::has('country')) {
-                $ids = DirEnum::getOptionsIter(DirEnum::ADMINAREAS, Env::v('country'));
-            } else {
-                $ids = DirEnum::getOptionsIter(DirEnum::ADMINAREAS);
-            }
-            $page->assign('onchange', 'changeAdministrativeArea(this.value)');
-            break;
-          case 'subadministrativearea':
-            if (Env::has('administrativearea')) {
-                $ids = DirEnum::getOptionsIter(DirEnum::SUBADMINAREAS, Env::v('administrativearea'));
-            } else {
-                $ids = DirEnum::getOptionsIter(DirEnum::SUBADMINAREAS);
-            }
-            break;
           case 'school':
             $ids = DirEnum::getOptionsIter(DirEnum::EDUSCHOOLS);
             $page->assign('onchange', 'changeSchool(this.value)');
index 27c51b4..33c32a5 100644 (file)
 {*                                                                        *}
 {**************************************************************************}
 
-{if t($address.geocodedText)}
-<div class="erreur center {$prefid}_geoloc">
-  Le géocodage n'a pas donné un résultat certain&nbsp;! Tu as le choix entre&nbsp;:
-</div>
-<div class="{$prefid}_geoloc">
-  <ul>
-    <li><a href="javascript:validGeoloc('{$prefid}','{$id}',2)"
-           title="Garder le texte de l'adresse que tu as renseignée tout en utilisant les informations trouvées par le géocodage pour te localiser sur le planisphère et dans lors d'une recherche dans l'annuaire.">
-      le texte de ton adresse localisé à l'endroit que nous te suggérons</a>.</li>
-    <li><a href="javascript:validGeoloc('{$prefid}','{$id}',0)" style="color: red">ton adresse (à gauche)</a>&nbsp;;</li>
-    <li><a href="javascript:validGeoloc('{$prefid}','{$id}',1)" style="color: green">notre suggestion (à droite)</a>&nbsp;;</li>
-  </ul>
-</div>
+{if t($validation)}
+<div style="float: left">
+{else}
+<tr{if t($class)} class="{$class}"{/if}>
+  <td>
 {/if}
-
-<div>
-  <textarea name="{$prefname}[text]" cols="30" rows="4" onkeyup="addressChanged('{$prefid}')"
-            {if t($address.geocodedText)}class="error"{/if}>{$address.text}</textarea>
-{if t($address.geocodedText)}
-  <textarea cols="30" rows="4" class="valid {$prefid}_geoloc">{$address.geocodedText}</textarea>
+    <textarea name="{$prefname}[text]" cols="30" rows="4" onkeyup="addressChanged('{$prefid}')">{$address.text}</textarea>
+    <input type="hidden" name="{$prefname}[postalText]" value="{$address.postalText}" />
+    <input type="hidden" name="{$prefname}[types]" value="{$address.types}" />
+    <input type="hidden" name="{$prefname}[formatted_address]" value="{$address.formatted_address}" />
+    <input type="hidden" name="{$prefname}[latitude]" value="{$address.latitude}" />
+    <input type="hidden" name="{$prefname}[longitude]" value="{$address.longitude}" />
+    <input type="hidden" name="{$prefname}[southwest_latitude]" value="{$address.southwest_latitude}" />
+    <input type="hidden" name="{$prefname}[southwest_longitude]" value="{$address.southwest_longitude}" />
+    <input type="hidden" name="{$prefname}[northeast_latitude]" value="{$address.northeast_latitude}" />
+    <input type="hidden" name="{$prefname}[northeast_longitude]" value="{$address.northeast_longitude}" />
+    <input type="hidden" name="{$prefname}[location_type]" value="{$address.location_type}" />
+    <input type="hidden" name="{$prefname}[partial_match]" value="{$address.partial_match}" />
+    <input type="hidden" name="{$prefname}[componentsIds]" value="{$address.componentsIds}" />
+    <input type="hidden" name="{$prefname}[changed]" value="0" />
+    <input type="hidden" name="{$prefname}[removed]" value="0" />
+    <input type="hidden" name="{$prefname}[geocoding_calls]" value="{$address.geocoding_calls}" />
+    <input type="hidden" name="{$prefname}[geocoding_date]" value="{$address.geocoding_date}" />
+{if t($validation)}
+    <br />
+    <label><input type="checkbox" name="{$prefname}[modified]"{if $valid->modified} checked="checked"{/if} />Utiliser la version modifiée</label>
+</div>
+<div style="float: right">
+{else}
+  </td>
+  <td>
 {/if}
+  {if t($address.latitude)}
+    <img src="https://maps.googleapis.com/maps/api/staticmap?size=300x100&amp;markers=color:{$profile->promoColor()}%7C{$address.latitude},{$address.longitude}&amp;zoom=12&amp;sensor=false"
+         alt="Position de l'adresse" />
+    {if t($geocoding_removal)}
+    <br />
+    <small id="{$prefid}_geocoding_removal">
+    {if !t($address.request)}
+      <a href="javascript:deleteGeocoding('{$prefid}', '{$hrpid}')">{icon name=cross title="Adresse mal localisée"} Signaler que le repère est mal placé</a>
+    {else}
+    Localisation en attente de validation.
+    {/if}
+    </small>
+    {/if}
+  {/if}
+{if t($validation)}
 </div>
-{if !$isMe}<div><small><strong>Adress postale&nbsp;:</strong><br />{$address.postalText|nl2br}</small></div>{/if}
-{if t($address.geocodedText)}
-<input type="hidden" name="{$prefname}[geocodeChosen]" value="1" />
-<input type="hidden" name="{$prefname}[geocodedText]" value="{$address.geocodedText}" />
+<div style="clear: both"></div>
+{else}
+  </td>
+</tr>
 {/if}
-<input type="hidden" name="{$prefname}[accuracy]" value="{$address.accuracy}" />
-<input type="hidden" name="{$prefname}[postalText]" value="{$address.postalText}" />
-<input type="hidden" name="{$prefname}[postalCode]" value="{$address.postalCode}" />
-<input type="hidden" name="{$prefname}[administrativeAreaId]" value="{$address.administrativeAreaId}" />
-<input type="hidden" name="{$prefname}[subAdministrativeAreaId]" value="{$address.subAdministrativeAreaId}" />
-<input type="hidden" name="{$prefname}[localityId]" value="{$address.localityId}" />
-<input type="hidden" name="{$prefname}[countryId]" value="{$address.countryId}" />
-<input type="hidden" name="{$prefname}[latitude]" value="{$address.latitude}" />
-<input type="hidden" name="{$prefname}[longitude]" value="{$address.longitude}" />
-<input type="hidden" name="{$prefname}[north]" value="{$address.north}" />
-<input type="hidden" name="{$prefname}[south]" value="{$address.south}" />
-<input type="hidden" name="{$prefname}[east]" value="{$address.east}" />
-<input type="hidden" name="{$prefname}[west]" value="{$address.west}" />
-<input type="hidden" name="{$prefname}[changed]" value="0" />
-<input type="hidden" name="{$prefname}[removed]" value="0" />
 
 {* vim:set et sw=2 sts=2 sws=2 enc=utf-8: *}
diff --git a/templates/include/form.valid.address.tpl b/templates/include/form.valid.address.tpl
new file mode 100644 (file)
index 0000000..9b6a696
--- /dev/null
@@ -0,0 +1,28 @@
+{**************************************************************************}
+{*                                                                        *}
+{*  Copyright (C) 2003-2011 Polytechnique.org                             *}
+{*  http://opensource.polytechnique.org/                                  *}
+{*                                                                        *}
+{*  This program is free software; you can redistribute it and/or modify  *}
+{*  it under the terms of the GNU General Public License as published by  *}
+{*  the Free Software Foundation; either version 2 of the License, or     *}
+{*  (at your option) any later version.                                   *}
+{*                                                                        *}
+{*  This program is distributed in the hope that it will be useful,       *}
+{*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *}
+{*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *}
+{*  GNU General Public License for more details.                          *}
+{*                                                                        *}
+{*  You should have received a copy of the GNU General Public License     *}
+{*  along with this program; if not, write to the Free Software           *}
+{*  Foundation, Inc.,                                                     *}
+{*  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA               *}
+{*                                                                        *}
+{**************************************************************************}
+
+<tr class="pair">
+  <td class="titre">Adresse fournie</td>
+  <td>{$valid->given_text}</td>
+</tr>
+
+{* vim:set et sw=2 sts=2 sws=2 enc=utf-8: *}
diff --git a/templates/include/form.valid.edit-address.tpl b/templates/include/form.valid.edit-address.tpl
new file mode 100644 (file)
index 0000000..0e6abcc
--- /dev/null
@@ -0,0 +1,25 @@
+{**************************************************************************}
+{*                                                                        *}
+{*  Copyright (C) 2003-2011 Polytechnique.org                             *}
+{*  http://opensource.polytechnique.org/                                  *}
+{*                                                                        *}
+{*  This program is free software; you can redistribute it and/or modify  *}
+{*  it under the terms of the GNU General Public License as published by  *}
+{*  the Free Software Foundation; either version 2 of the License, or     *}
+{*  (at your option) any later version.                                   *}
+{*                                                                        *}
+{*  This program is distributed in the hope that it will be useful,       *}
+{*  but WITHOUT ANY WARRANTY; without even the implied warranty of        *}
+{*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *}
+{*  GNU General Public License for more details.                          *}
+{*                                                                        *}
+{*  You should have received a copy of the GNU General Public License     *}
+{*  along with this program; if not, write to the Free Software           *}
+{*  Foundation, Inc.,                                                     *}
+{*  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA               *}
+{*                                                                        *}
+{**************************************************************************}
+
+{include file="geoloc/form.address.tpl" prefname="valid" prefid=0 address=$valid->address validation=1}
+
+{* vim:set et sw=2 sts=2 sws=2 enc=utf-8: *}
index e39d426..712212c 100644 (file)
@@ -40,7 +40,7 @@
 </table>
 <table class="bicol" style="margin-bottom: 1em" id="{$prefid}">
   <tr>
-    <th>
+    <th colspan="2">
       <div style="float: left">
         <label>
           <input name="{$prefname}[current]" type="radio" {if $address.current}checked="checked"{/if}
     </th>
   </tr>
   <tr {if $hiddenaddr}style="display: none"{/if}>
+    <td colspan="2" class="flags">
+      {include file="include/flags.radio.tpl" name="`$prefname`[pub]" val=$address.pub mainField='addresses' mainId=$i subField='phones' subId=-1}
+    </td>
+  </tr>
+  {include file="geoloc/form.address.tpl" prefname=$prefname prefid=$prefid address=$address id=$i}
+  <tr {if $hiddenaddr}style="display: none"{/if}>
+  {if !$isMe}
     <td>
-      <div style="margin-bottom: 0.2em" class="flags">
-        {include file="include/flags.radio.tpl" name="`$prefname`[pub]" val=$address.pub mainField='addresses' mainId=$i subField='phones' subId=-1}
-      </div>
-      <div style="clear: both"></div>
-      <div style="float: left">
-      {include file="geoloc/form.address.tpl" prefname=$prefname prefid=$prefid address=$address id=$i}
-      </div>
+      <small><strong>Adress postale&nbsp;:</strong><br />{$address.postalText|nl2br}</small>
+    </td>
+    <td>
+  {else}
+    <td colspan="2">
+  {/if}
       <div style="float: left">
         <div>
           <label>
     </td>
   </tr>
   <tr class="pair" {if $hiddenaddr}style="display: none"{/if}>
-    <td>
+    <td colspan="2">
       {foreach from=$address.phones key=t item=tel}
         <div id="{"`$prefid`_phones_`$t`"}" style="clear: both">
           {include file="profile/phone.tpl" prefname="`$prefname`[phones]" prefid="`$prefid`_phones" telid=$t tel=$tel
index 1637b19..5df456d 100644 (file)
 {if $mail_part eq 'head'}
 {from full=#from#}
 {to addr=#to#}
-{subject text="Adresse impossible à geolocaliser"}
+{subject text="Erreur de géolocalisation"}
 {elseif $mail_part eq 'wiki'}
-Un camarade {$smarty.session.hruid} a forcé l'utilisation de l'adresse entrée
-à la main et a refusé la version geolocalisée :
+La requête de géocodage générée par {$smarty.session.hruid} et dont l'url
+est la suivante :\\
+{$url}\\
+a reçu le status d'erreur suivant :\\
+{$status}
 
-'''Version utilisateur (validée) :'''\\
-{$text}
-
-'''Version geoloc (refusée) :'''\\
-{$geoloc}
 {include file="include/signature.mail.tpl"}
 {/if}
 
index 8150c41..68958f0 100644 (file)
       </td>
     </tr>
     <tr id="{$jobid}_w_address" class="pair" {if $hiddenjob || $hiddenaddr}style="display: none"{/if}>
-      <td colspan="2">
-        <div style="float: left">
-          <div class="titre">Adresse</div>
-          <div class="flags">
-            {include file="include/flags.radio.tpl" name="`$jobpref`[w_address][pub]" val=$job.w_address.pub
-                     subField='w_address' mainField='jobs' mainId=$i subId=''}
-          </div>
-          <div style="margin-top: 20px; clear: both">
-            {include file="geoloc/form.address.tpl" prefname="`$jobpref`[w_address]"
-                     prefid=$jobid address=$job.w_address}
-          </div>
-        </div>
+      <td class="titre">Adresse</td>
+      <td class="flags">
+        {include file="include/flags.radio.tpl" name="`$jobpref`[w_address][pub]" val=$job.w_address.pub
+                 subField='w_address' mainField='jobs' mainId=$i subId=''}
       </td>
     </tr>
+    {include file="geoloc/form.address.tpl" prefname="`$jobpref`[w_address]"
+                     prefid=$jobid address=$job.w_address class="pair"}
     {if $hiddenaddr}
     <tr class="pair">
       <td class="titre" colspan="2">Adresse (masquée)</td>
index 4d47703..3806eb7 100644 (file)
       };
   }
 
-  // when changing country, open up administrativearea choice
-  function changeCountry(a2) {
-    $(".autocompleteTarget[name='country']").attr('value',a2);
+  function setAddress(i, j, values)
+  {
+    var types = new Array('country', 'administrative_area_level_1', 'administrative_area_level_2', 'administrative_area_level_3', 'locality', 'sublocality');
+    var prev_type = types[i];
+    var next_type = types[j];
+    var next_list = next_type + '_list';
+
+    if (j == 3) {
+      $('tr#locality_text').hide()
+      $("select[name='localityTxt']").attr('value', '');
+    }
 
-    if (a2) {
-      $(".autocomplete[name='countryTxt']").addClass('hidden_valid');
+    $("[name='" + next_type + "']").parent().load(baseurl + 'list/' + next_type, { previous:prev_type, value:values[i] }, function() {
+      if ($("select[name='" + next_type + "']").children("option").size() > 1) {
+        $("tr#" + next_list).show();
+        $("select[name='" + next_type + "']").attr('value', values[j]);
+        if (j < 6) {
+          setAddress(j, j + 1, values);
+        }
+      } else {
+        $("tr#" + next_list).hide();
+        $("select[name='" + next_type + "']").attr('value', '');
+        if (j < 6) {
+          setAddress(i, j + 1, values);
+        }
+      }
+    });
 
-      $("[name='administrativearea']").parent().load(baseurl + 'list/administrativearea/', { country:a2 }, function() {
-          if ($("select[name='administrativearea']").children("option").size() > 1) {
-            $("select[name='administrativearea']").attr('value', '{/literal}{$smarty.request.administrativearea}{literal}');
+  }
 
-            $("tr#administrativearea_list").show();
-          } else {
-            $("select[name='administrativearea']").attr('value', '');
+  function displayNextAddressComponent(i, j, value)
+  {
+    var types = new Array('country', 'administrative_area_level_1', 'administrative_area_level_2', 'administrative_area_level_3', 'locality', 'sublocality');
+    var prev_type = types[i];
+    var next_type = types[j];
+    var next_list = next_type + '_list';
 
-            $("tr#administrativearea_list").hide();
-          }
-        });
-    } else {
-      $(".autocomplete[name='countryTxt']").removeClass('hidden_valid');
+    if (j == 3) {
+      $('tr#locality_text').hide()
+      $("select[name='localityTxt']").attr('value', '');
+    }
 
-      $("select[name='administrativearea']").attr('value', '');
-      $("select[name='subadministrativearea']").attr('value', '');
+    $("[name='" + next_type + "']").parent().load(baseurl + 'list/' + next_type, { previous:prev_type, value:value }, function() {
+      $("select[name='" + next_type + "']").attr('value', '');
+      if ($("select[name='" + next_type + "']").children("option").size() > 1) {
+        $("tr#" + next_list).show();
+      } else {
+        $("tr#" + next_list).hide();
+        if (j < 6) {
+          displayNextAddressComponent(i, j + 1, value);
+        }
+      }
+    });
+  }
+
+  function changeAddressComponents(type, value)
+  {
+    var types = new Array('country', 'administrative_area_level_1', 'administrative_area_level_2', 'administrative_area_level_3', 'locality', 'sublocality');
+    var i = 0, j = 0;
 
-      $("tr#administrativearea_list").hide();
-      $("tr#subadministrativearea_list").hide();
+    while (types[i] != type && i < 6) {
+      ++i;
     }
-  }
 
-  // when changing administrativearea, open up subadministrativearea choice
-  function changeAdministrativeArea(id) {
-    if (id) {
-      $("[name='subadministrativearea']").parent().load(baseurl + 'list/subadministrativearea/', { administrativearea:id }, function() {
-          if ($("select[name='subadministrativearea']").children("option").size() > 1) {
-            $("select[name='subadministrativearea']").attr('value', '{/literal}{$smarty.request.subadministrativearea}{literal}');
-            $("tr#subadministrativearea_list").show();
-          } else {
-            $("select[name='subadministrativearea']").attr('value', '');
-            $("tr#subadministrativearea_list").hide();
-          }
-        });
-    } else {
-      $("select[name='subadministrativearea']").attr('value', '');
-      $("tr#subadministrativearea_list").hide();
+    j = i + 1;
+    while (j < 6) {
+      $("select[name='" + types[j] + "']").attr('value', '');
+      $("tr#" + types[j] + "_list").hide();
+      ++j;
+    }
+
+    if (value != '' && i < 5) {
+      $("select[name='" + type + "']").attr('value', value);
+      displayNextAddressComponent(i, i + 1, value);
     }
   }
 
       if (nameRealField == name)
         return null;
 
-      // if changing country, might want to open administrativearea choice
-      if (nameRealField == 'country')
+      // When changing country or locality, open next address component.
+      if (nameRealField == 'country' || nameRealField == 'locality') {
         return function(i) {
+            nameRealField = name.replace(/Txt$/, '');
             if (i.extra[0] < 0) {
-              cancel_autocomplete('countryTxt', 'country');
+              cancel_autocomplete(name, nameRealField);
               i.extra[1] = '';
             }
-            changeCountry(i.extra[1]);
+            $("[name='" + nameRealField + "']").parent().load(baseurl + 'list/' + nameRealField, function() {
+              $("select[name='" + nameRealField + "']").attr('value', i.extra[1]);
+            });
+            changeAddressComponents(nameRealField, i.extra[1]);
           }
+      }
 
       if (nameRealField == 'school')
         return function(i) {
 
       $(".autocomplete").change(function() { $(this).removeClass('hidden_valid'); });
 
-      $(".autocomplete[name='countryTxt']").change(function() { changeCountry(''); });
-
-      changeCountry({/literal}'{$smarty.request.country}'{literal});
-      changeAdministrativeArea({/literal}'{$smarty.request.administrativearea}'{literal});
+      if ({/literal}'{$smarty.request.country}'{literal} != '') {
+        $("[name='country']").parent().load(baseurl + 'list/country', function() {
+          $("select[name='country']").attr('value', {/literal}'{$smarty.request.country}'{literal});
+        });
+        setAddress(0, 1, new Array({/literal}'{$smarty.request.country}'{literal},
+                                   {/literal}'{$smarty.request.administrative_area_level_1}'{literal},
+                                   {/literal}'{$smarty.request.administrative_area_level_2}'{literal},
+                                   {/literal}'{$smarty.request.administrative_area_level_3}'{literal},
+                                   {/literal}'{$smarty.request.locality}'{literal},
+                                   {/literal}'{$smarty.request.sublocality}'{literal})
+        );
+      } else {
+        var types = new Array('administrative_area_level_1', 'administrative_area_level_2', 'administrative_area_level_3', 'locality', 'sublocality');
+        for (var i = 0; i < 5; ++i) {
+          $("tr#" + types[i] + '_list').hide();
+        }
+      }
 
       $(".autocomplete[name='schoolTxt']").change(function() { changeSchool(''); });
 
         });
 
       $("#only_referent").change(function() { changeOnlyReferent(); });
-      changeOnlyReferent();
 
     });
 /** Regexps to wipe out from search queries */
@@ -404,10 +451,6 @@ function cleanForm(f) {
       <th colspan="2">Géographie</th>
     </tr>
     <tr>
-      <td>Ville ou code postal</td>
-      <td><input type="text" class="autocomplete" name="city" size="32" value="{$smarty.request.city}" /></td>
-    </tr>
-    <tr>
       <td>Pays</td>
       <td>
         <input name="countryTxt" type="text" class="autocomplete" style="display:none" size="32"
@@ -416,16 +459,38 @@ function cleanForm(f) {
         <a href="country" class="autocompleteToSelect">{icon name="table" title="Tous les pays"}</a>
       </td>
     </tr>
-    <tr id="administrativearea_list">
+    <tr id="administrative_area_level_1_list">
       <td>Région, province, état&hellip;</td>
       <td>
-        <input name="administrativearea" type="hidden" size="32" value="{$smarty.request.administrativearea}" />
+        <input name="administrative_area_level_1" type="hidden" size="32" value="{$smarty.request.administrative_area_level_1}" />
       </td>
     </tr>
-    <tr id="subadministrativearea_list">
+    <tr id="administrative_area_level_2_list">
       <td>Département, comté&hellip;</td>
       <td>
-        <input name="subadministrativearea" type="hidden" size="32" value="{$smarty.request.subadministrativearea}" />
+        <input name="administrative_area_level_2" type="hidden" size="32" value="{$smarty.request.administrative_area_level_2}" />
+      </td>
+    </tr>
+    <tr id="administrative_area_level_3_list">
+      <td>Canton&hellip;</td>
+      <td>
+        <input name="administrative_area_level_3" type="hidden" size="32" value="{$smarty.request.administrative_area_level_3}" />
+      </td>
+    </tr>
+    <tr id="locality_text">
+      <td>Ville</td>
+      <td><input type="text" class="autocomplete" name="localityTxt" size="32" value="{$smarty.request.locality}" /></td>
+    </tr>
+    <tr id="locality_list">
+      <td>Ville</td>
+      <td>
+        <input name="locality" type="hidden" size="32" value="{$smarty.request.locality}" />
+      </td>
+    </tr>
+    <tr id="sublocality_list">
+      <td>Arrondissement, quartier&hellip;</td>
+      <td>
+        <input name="sublocality" type="hidden" size="32" value="{$smarty.request.sublocality}" />
       </td>
     </tr>
     <tr>
diff --git a/upgrade/1.1.2/01_geocoding.sql b/upgrade/1.1.2/01_geocoding.sql
new file mode 100644 (file)
index 0000000..051ff08
--- /dev/null
@@ -0,0 +1,61 @@
+DROP TABLE IF EXISTS profile_addresses_components;
+DROP TABLE IF EXISTS profile_addresses_components_enum;
+
+CREATE TABLE profile_addresses_components_enum (
+  id BIGINT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
+  short_name VARCHAR(255) NOT NULL DEFAULT '',
+  long_name VARCHAR(255) NOT NULL DEFAULT '',
+  types SET('street_address', 'route', 'intersection', 'political', 'country', 'administrative_area_level_1', 'administrative_area_level_2', 'administrative_area_level_3', 'colloquial_area', 'locality', 'sublocality', 'neighborhood', 'premise', 'subpremise', 'postal_code', 'natural_feature', 'airport', 'park', 'point_of_interest', 'post_box', 'street_number', 'floor', 'room') NOT NULL DEFAULT '',
+  PRIMARY KEY (id),
+  UNIQUE KEY (types, long_name),
+  KEY (types, short_name)
+) ENGINE=InnoDB, CHARSET=utf8;
+
+CREATE TABLE profile_addresses_components (
+  pid INT(11) UNSIGNED NOT NULL DEFAULT 0,
+  jobid INT(6) UNSIGNED NOT NULL DEFAULT 0,
+  groupid SMALLINT(5) UNSIGNED NOT NULL DEFAULT 0,
+  type ENUM('home', 'job', 'hq', 'group') NOT NULL DEFAULT 'home',
+  id TINYINT(3) UNSIGNED NOT NULL DEFAULT 0,
+  component_id BIGINT(10) UNSIGNED NOT NULL DEFAULT 0,
+  PRIMARY KEY (pid, jobid, groupid, type, id, component_id),
+  KEY(component_id),
+  FOREIGN KEY (pid, jobid, groupid, type, id) REFERENCES profile_addresses (pid, jobid, groupid, type, id) ON UPDATE CASCADE ON DELETE CASCADE,
+  FOREIGN KEY (component_id) REFERENCES profile_addresses_components_enum (id) ON UPDATE CASCADE ON DELETE CASCADE
+) ENGINE=InnoDB, CHARSET=utf8;
+
+ALTER TABLE profile_addresses DROP FOREIGN KEY profile_addresses_ibfk_1;
+ALTER TABLE profile_addresses DROP FOREIGN KEY profile_addresses_ibfk_2;
+ALTER TABLE profile_addresses DROP FOREIGN KEY profile_addresses_ibfk_3;
+ALTER TABLE profile_addresses DROP FOREIGN KEY profile_addresses_ibfk_4;
+
+ALTER TABLE profile_addresses DROP COLUMN accuracy;
+ALTER TABLE profile_addresses DROP COLUMN postalCode;
+ALTER TABLE profile_addresses DROP COLUMN localityId;
+ALTER TABLE profile_addresses DROP COLUMN subAdministrativeAreaId;
+ALTER TABLE profile_addresses DROP COLUMN administrativeAreaId;
+ALTER TABLE profile_addresses DROP COLUMN countryId;
+ALTER TABLE profile_addresses DROP COLUMN north;
+ALTER TABLE profile_addresses DROP COLUMN south;
+ALTER TABLE profile_addresses DROP COLUMN east;
+ALTER TABLE profile_addresses DROP COLUMN west;
+
+ALTER TABLE profile_addresses ADD COLUMN formatted_address TEXT NOT NULL DEFAULT '' AFTER postalText;
+ALTER TABLE profile_addresses ADD COLUMN types SET('street_address', 'route', 'intersection', 'political', 'country', 'administrative_area_level_1', 'administrative_area_level_2', 'administrative_area_level_3', 'colloquial_area', 'locality', 'sublocality', 'neighborhood', 'premise', 'subpremise', 'postal_code', 'natural_feature', 'airport', 'park', 'point_of_interest', 'post_box', 'street_number', 'floor', 'room') NOT NULL DEFAULT '' AFTER formatted_address;
+ALTER TABLE profile_addresses ADD COLUMN southwest_latitude FLOAT(10, 7) DEFAULT NULL AFTER longitude;
+ALTER TABLE profile_addresses ADD COLUMN southwest_longitude FLOAT(10, 7) DEFAULT NULL AFTER southwest_latitude;
+ALTER TABLE profile_addresses ADD COLUMN northeast_latitude FLOAT(10, 7) DEFAULT NULL AFTER southwest_longitude;
+ALTER TABLE profile_addresses ADD COLUMN northeast_longitude FLOAT(10, 7) DEFAULT NULL AFTER northeast_latitude;
+ALTER TABLE profile_addresses ADD COLUMN location_type ENUM('ROOFTOP', 'RANGE_INTERPOLATED', 'GEOMETRIC_CENTER', 'APPROXIMATE') DEFAULT NULL AFTER northeast_longitude;
+ALTER TABLE profile_addresses ADD COLUMN partial_match BOOLEAN NOT NULL DEFAULT false AFTER location_type;
+ALTER TABLE profile_addresses ADD COLUMN geocoding_date DATE DEFAULT NULL;
+ALTER TABLE profile_addresses ADD COLUMN geocoding_calls TINYINT NOT NULL DEFAULT 0;
+
+UPDATE  profile_addresses
+   SET  latitude = NULL, longitude = NULL;
+
+DROP TABLE IF EXISTS geoloc_administrativeareas;
+DROP TABLE IF EXISTS geoloc_localities;
+DROP TABLE IF EXISTS geoloc_subadministrativeareas;
+
+-- vim:set syntax=mysql:
diff --git a/upgrade/1.1.2/README b/upgrade/1.1.2/README
new file mode 100644 (file)
index 0000000..272bed9
--- /dev/null
@@ -0,0 +1,2 @@
+Once all sql/php scripts have be run, run retrieve_address_tables.sh and finally xx_retrieve_geocoding.sql.
+Then "./formatAddresses.php -g -t g -r e" will format the last ungeocoded addresses.
diff --git a/upgrade/1.1.2/retrieve_address_tables.sh b/upgrade/1.1.2/retrieve_address_tables.sh
new file mode 100755 (executable)
index 0000000..94cdb9a
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+mysqldump -h murphy geocoding profile_addresses | sed -e 's/profile_addresses/tmp_profile_addresses/' | mysql x5dat
+mysqldump -h murphy geocoding profile_addresses_components | mysql x5dat
+mysqldump -h murphy geocoding profile_addresses_components_enum | mysql x5dat
+
diff --git a/upgrade/1.1.2/xx_retrieve_geocoding.sql b/upgrade/1.1.2/xx_retrieve_geocoding.sql
new file mode 100644 (file)
index 0000000..95da5cd
--- /dev/null
@@ -0,0 +1,11 @@
+    UPDATE  profile_addresses     AS pa
+INNER JOIN  tmp_profile_addresses AS ta ON (pa.pid = ta.pid AND pa.jobid = ta.jobid AND pa.groupid = ta.groupid AND pa.type = ta.type AND pa.text = ta.text)
+       SET  pa.id = ta.id, pa.postalText = ta.postalText, pa.formatted_address = ta.formatted_address, pa.types = ta.types,
+            pa.latitude = ta.latitude, pa.longitude = ta.longitude, pa.southwest_latitude = ta.southwest_latitude,
+            pa.southwest_longitude = ta.southwest_longitude, pa.northeast_latitude = ta.northeast_latitude, pa.northeast_longitude = ta.northeast_longitude,
+            pa.location_type = ta.location_type, pa.partial_match = ta.partial_match,
+            pa.geocoding_date = ta.geocoding_date, pa.geocoding_calls = ta.geocoding_calls;
+
+DROP TABLE tmp_profile_addresses;
+
+-- vim:set syntax=mysql: