Merge branch 'xorg/maint' into xorg/master
authorRaphaël Barrois <raphael.barrois@polytechnique.org>
Fri, 9 Sep 2011 22:35:10 +0000 (00:35 +0200)
committerRaphaël Barrois <raphael.barrois@polytechnique.org>
Fri, 9 Sep 2011 22:35:10 +0000 (00:35 +0200)
88 files changed:
ChangeLog
bin/cron/google_apps.php
bin/cron/registrations.php
bin/lists.rpc.py
classes/address.php
classes/direnum.php
classes/phone.php
classes/profile.php
classes/profilevisibility.php [deleted file]
classes/user.php
classes/userfilter.php
classes/userfilter/conditions.inc.php
classes/visibility.php [new file with mode: 0644]
classes/xnet.php
classes/xnetpage.php
classes/xnetsession.php
classes/xorg.php
classes/xorgpage.php
classes/xorgsession.php
configs/mails.conf
configs/platal.ini
core
htdocs/javascript/xorg.js
include/partnersharing.inc.php [new file with mode: 0644]
include/profilefields.inc.php
include/userset.inc.php
include/validations/address.inc.php
include/validations/aliases.inc.php
include/vcard.inc.php
include/xnet.inc.php
modules/api.php
modules/auth.php
modules/axletter.php
modules/deltaten.php
modules/email.php
modules/epletter.php
modules/events.php
modules/geoloc.php
modules/lists.php
modules/marketing.php
modules/newsletter.php
modules/openid.php
modules/payment.php
modules/platal.php
modules/profile.php
modules/profile/general.inc.php
modules/profile/jobs.inc.php
modules/profile/page.inc.php
modules/register.php
modules/reminder.php
modules/search.php
modules/sharingapi.php [new file with mode: 0644]
modules/sharingapi/request.inc.php [new file with mode: 0644]
modules/stats.php
modules/survey.php
modules/survey/survey.inc.php
modules/xnet.php
modules/xnetevents.php
modules/xnetgrp.php
modules/xnetlists.php
modules/xnetnl.php
plugins/function.profile.php
plugins/insert.getUserName.php
templates/core/password_prompt.tpl
templates/include/form.valid.address.tpl
templates/include/form.valid.edit-address.tpl
templates/lists/moderate_mail.tpl
templates/newsletter/admin_all.tpl
templates/newsletter/edit.tpl
templates/newsletter/nl.AX.mail.tpl
templates/platal/password.tpl
templates/platal/password_recovery_xnet.mail.tpl [new file with mode: 0644]
templates/platal/preferences.tpl
templates/profile/general.tpl
templates/profile/profile.tpl
templates/skin/common.menu.tpl
templates/skin/group_login.tpl [new file with mode: 0644]
templates/survey/index.tpl
templates/xnet/login.tpl [deleted file]
templates/xnet/register.success.tpl
templates/xnet/skin.tpl
upgrade/1.1.3/01_marital.sql [new file with mode: 0644]
upgrade/1.1.3/02_names.sql [new file with mode: 0644]
upgrade/1.1.3/03_visibility.sql [new file with mode: 0644]
upgrade/1.1.3/04_pta.sql [new file with mode: 0644]
upgrade/1.1.3/05_accounts.sql [new file with mode: 0644]
upgrade/1.1.3/06_authgroupex.sql [new file with mode: 0644]
upgrade/1.1.3/update.sh [new file with mode: 0755]

index eff74c9..f7b7a03 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,38 @@
 ================================================================================
+VERSION 1.1.3                                                         XX XX XXXX
+
+New:
+
+    * API:
+        - Enable the 'sharing' API (profile exchange with other sites)     -XEL
+
+    * Auth:
+        - Add per-group options to configure the auth-groupe-x behaviour   -XEL
+
+    * Profile:
+        - Significantly improve visibility handling                        -XEL
+
+    * Xnet:
+        - Allow auth-groupe-x for X.net accounts                           -XEL
+
+Bug/Wish:
+
+    * Email:
+        - #1517: Fixes bestalias update when two local_part are identical  -JAC
+        - #1533: Fixes bestalias after updating melix alias                -JAC
+
+    * Profile:
+        - #1532: Allows edition of empty reference birthdates              -JAC
+
+From 1.1.2 branch:
+
+    * Newsletter:
+        - #1524: Fixes NL unsubscription when not loggued on the site      -JAC
+
+    * Search:
+        - #1520: Fixes deltaten                                            -XEL
+
+================================================================================
 VERSION 1.1.2                                                         28 06 2011
 
 New:
index c23f7d1..a7eb8d0 100755 (executable)
@@ -97,12 +97,18 @@ while ($nickname = $res->next()) {
     // we might run in troubler later if we don't keep the two repos. If we need
     // to add a forlife-looking nickname at some point, we'll do it manually.
     if (!preg_match('/^[-a-z]+\.[-a-z]+\.\d{4}$/', $nickname['nickname'])) {
-        XDB::execute(
-            "INSERT  INTO gapps_queue
-                SET  q_recipient_id = {?}, p_entry_date = NOW(), p_notbefore_date = NOW(),
-                     p_priority = 'offline', j_type = 'n_create', j_parameters = {?}",
-            $nickname['uid'],
-            json_encode($nickname));
+        $pending_tasks = XDB::fetchOneCell(
+            "SELECT  COUNT(*)
+               FROM  gapps_queue
+              WHERE  q_recipient_id = {?} AND p_status = 'idle' AND j_type = 'n_create' AND j_parameters = {?}",
+            $nickname['id'], json_encode($nickname));
+        if ($pending_tasks == 0) {
+            XDB::execute(
+                "INSERT  INTO gapps_queue
+                    SET  q_recipient_id = {?}, p_entry_date = NOW(), p_notbefore_date = NOW(),
+                         p_priority = 'offline', j_type = 'n_create', j_parameters = {?}",
+                $nickname['id'], json_encode($nickname));
+        }
     }
 }
 
@@ -114,12 +120,18 @@ $res = XDB::iterator(
   LEFT JOIN  email_source_account AS s ON (s.uid = g.l_userid AND s.type = 'alias' AND s.email = g.g_nickname)
       WHERE  g.l_userid IS NOT NULL AND s.email IS NULL");
 while ($nickname = $res->next()) {
-    XDB::execute(
-        "INSERT  INTO gapps_queue
-            SET  q_recipient_id = {?}, p_entry_date = NOW(), p_notbefore_date = NOW(),
-                 p_priority = 'offline', j_type = 'n_delete', j_parameters = {?}",
-        $nickname['uid'],
-        json_encode($nickname));
+    $pending_tasks = XDB::fetchOneCell(
+        "SELECT  COUNT(*)
+           FROM  gapps_queue
+          WHERE  q_recipient_id = {?} AND p_status = 'idle' AND j_type = 'n_delete' AND j_parameters = {?}",
+        $nickname['id'], json_encode($nickname));
+    if ($pending_tasks == 0) {
+        XDB::execute(
+            "INSERT  INTO gapps_queue
+                SET  q_recipient_id = {?}, p_entry_date = NOW(), p_notbefore_date = NOW(),
+                     p_priority = 'offline', j_type = 'n_delete', j_parameters = {?}",
+            $nickname['id'], json_encode($nickname));
+    }
 }
 
 /* Retrieves successful job queues for post-queue processing. */
index a2c22dc..1866b89 100755 (executable)
@@ -5,7 +5,7 @@ require 'connect.db.inc.php';
 
 $message = '';
 
-$res = XDB::iterRow("SELECT  a.registration_date, a.hruid, s.email, GROUP_CONCAT(r.redirect SEPARATOR ', ')
+$res = XDB::iterRow("SELECT  DATE(a.registration_date), a.hruid, GROUP_CONCAT(DISTINCT r.redirect SEPARATOR ', ')
                        FROM  accounts               AS a
                  INNER JOIN  account_profiles       AS ap ON (ap.uid = a.uid AND FIND_IN_SET('owner', ap.perms))
                  INNER JOIN  profile_display        AS pd ON (ap.pid = pd.pid)
index 843f7c0..61aac8f 100755 (executable)
@@ -69,6 +69,7 @@ def get_config(sec, val, default=None):
 
 MYSQL_USER     = get_config('Core', 'dbuser')
 MYSQL_PASS     = get_config('Core', 'dbpwd')
+MYSQL_HOST     = get_config('Core', 'dbhost')
 MYSQL_DB       = get_config('Core', 'dbdb')
 
 PLATAL_DOMAIN  = get_config('Mail', 'domain')
@@ -167,7 +168,7 @@ def connectDB():
             db=MYSQL_DB,
             user=MYSQL_USER,
             passwd=MYSQL_PASS,
-            unix_socket='/var/run/mysqld/mysqld.sock')
+            host=MYSQL_HOST)
     db.ping()
     db.autocommit(True)
     return db.cursor()
index a532c8d..665d91d 100644 (file)
@@ -575,7 +575,7 @@ class Address
     public function format()
     {
         $this->text = trim($this->text);
-        $this->phones = Phone::formatFormArray($this->phones, $this->error, new ProfileVisibility($this->pub));
+        $this->phones = Phone::formatFormArray($this->phones, $this->error, $this->pub);
         if ($this->removed == 1) {
             if (!S::user()->checkPerms('directory_private') && Phone::hasPrivate($this->phones)) {
                 Platal::page()->trigWarning("L'adresse ne peut être supprimée car elle contient des informations pour lesquelles vous n'avez le droit d'édition.");
@@ -826,7 +826,7 @@ class Address
     // addresses before secondary addresses.
     static private function compare(array $a, array $b)
     {
-        $value = ProfileVisibility::comparePublicity($a, $b);
+        $value = Visibility::comparePublicity($a, $b);
         if ($value == 0) {
             if ($a['secondary'] != $b['secondary']) {
                 $value = $a['secondary'] ? 1 : -1;
@@ -878,9 +878,9 @@ class Address
     }
 
     static public function iterate(array $pids = array(), array $types = array(),
-                                   array $jobids = array(), array $pubs = array())
+                                   array $jobids = array(), Visibility $visibility)
     {
-        return new AddressIterator($pids, $types, $jobids, $pubs);
+        return new AddressIterator($pids, $types, $jobids, $visibility);
     }
 }
 
@@ -895,7 +895,7 @@ class AddressIterator implements PlIterator
 {
     private $dbiter;
 
-    public function __construct(array $pids, array $types, array $jobids, array $pubs)
+    public function __construct(array $pids, array $types, array $jobids, Visibility $visibility)
     {
         $where = array();
         if (count($pids) != 0) {
@@ -907,9 +907,11 @@ class AddressIterator implements PlIterator
         if (count($jobids) != 0) {
             $where[] = XDB::format('(pa.jobid IN {?})', $jobids);
         }
-        if (count($pubs) != 0) {
-            $where[] = XDB::format('(pa.pub IN {?})', $pubs);
+        if ($visibility == null) {
+            $visibility = Visibility::defaultForRead();
         }
+        $where[] = 'pve.best_display_level+0 <= pa.pub+0';
+
         $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,
@@ -924,10 +926,11 @@ class AddressIterator implements PlIterator
              LEFT JOIN  profile_addresses_components_enum AS pace2 ON (FIND_IN_SET(\'locality\', pace2.types) AND pace2.id = pc.component_id)
              LEFT JOIN  profile_addresses_components_enum AS pace3 ON (FIND_IN_SET(\'administrative_area_level_1\', pace3.types) AND pace3.id = pc.component_id)
              LEFT JOIN  profile_addresses_components_enum AS pace4 ON (FIND_IN_SET(\'country\', pace4.types) AND pace4.id = pc.component_id)
-                 ' . ((count($where) > 0) ? 'WHERE  ' . implode(' AND ', $where) : '') . '
+             LEFT JOIN  profile_visibility_enum AS pve ON (pve.access_level = {?})
+                 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);
+        $this->dbiter = XDB::iterator($sql, $visibility->level());
     }
 
     public function next()
index 8a3829d..9d9a163 100644 (file)
@@ -654,6 +654,10 @@ class DE_JobDescription extends DirEnumeration
 // {{{ class DE_JobTerms
 class DE_JobTerms extends DirEnumeration
 {
+    protected $valfield = 'profile_job_term_enum.name';
+    protected $from = 'profile_job_term_enum';
+    protected $idfield = 'profile_job_term_enum.jtid';
+
     // {{{ function getAutoComplete
     public function getAutoComplete($text)
     {
index 29449e7..0cb0703 100644 (file)
@@ -332,16 +332,18 @@ class Phone
             $phone = new Phone($item);
             $success = (!$phone->error && ($phone->format() || $phone->isEmpty()) && $success);
             if (!$phone->isEmpty()) {
-                if (!is_null($maxPublicity) && $maxPublicity->isVisible($phone->pub)) {
-                    $phone->pub = $maxPublicity->level();
+                // Restrict phone visibility to $maxPublicity
+                if (!is_null($maxPublicity) && Visibility::isLessRestrictive($phone->pub, $maxPublicity)) {
+                    $phone->pub = $maxPublicity;
                 }
                 $phones[] = call_user_func(array($phone, $function));
             }
         }
         if (count($phones) == 0 && $requiresEmptyPhone) {
             $phone = new Phone();
-            if (!is_null($maxPublicity) && $maxPublicity->isVisible($phone->pub)) {
-                $phone->pub = $maxPublicity->level();
+            if (!is_null($maxPublicity) && Visibility::isLessRestrictive($phone->pub, $maxPublicity)) {
+                // Restrict phone visibility to $maxPublicity
+                $phone->pub = $maxPublicity;
             }
             $phones[] = call_user_func(array($phone, $function));
         }
@@ -352,7 +354,7 @@ class Phone
     static public function formatFormArray(array $data, &$success = true, $maxPublicity = null)
     {
         $phones = self::formArrayWalk($data, 'toFormArray', $success, true, $maxPublicity);
-        usort($phones, 'ProfileVisibility::comparePublicity');
+        usort($phones, 'Visibility::comparePublicity');
         return $phones;
     }
 
@@ -372,9 +374,9 @@ class Phone
     }
 
     static public function iterate(array $pids = array(), array $link_types = array(),
-                                   array $link_ids = array(), array $pubs = array())
+                                   array $link_ids = array(), Visibility $visibility)
     {
-        return new PhoneIterator($pids, $link_types, $link_ids, $pubs);
+        return new PhoneIterator($pids, $link_types, $link_ids, $visibility);
     }
 }
 
@@ -389,7 +391,7 @@ class PhoneIterator implements PlIterator
 {
     private $dbiter;
 
-    public function __construct(array $pids, array $link_types, array $link_ids, array $pubs)
+    public function __construct(array $pids, array $link_types, array $link_ids, Visibility $visibility)
     {
         $where = array();
         if (count($pids) != 0) {
@@ -401,15 +403,18 @@ class PhoneIterator implements PlIterator
         if (count($link_ids) != 0) {
             $where[] = XDB::format('(link_id IN {?})', $link_ids);
         }
-        if (count($pubs) != 0) {
-            $where[] = XDB::format('(pub IN {?})', $pubs);
+        if ($visibility == null) {
+            $visibility = Visibility::defaultForRead();
         }
+        $where[] = 'pve.best_display_level+0 <= pub+0';
+
         $sql = 'SELECT  search_tel AS search, display_tel AS display, comment, link_id,
                         tel_type AS type, link_type, tel_id AS id, pid, pub
                   FROM  profile_phones
-                 ' . ((count($where) > 0) ? 'WHERE  ' . implode(' AND ', $where) : '') . '
+             LEFT JOIN  profile_visibility_enum AS pve ON (pve.access_level = {?})
+                 WHERE  ' . implode(' AND ', $where) . '
               ORDER BY  pid, link_id, tel_id';
-        $this->dbiter = XDB::iterator($sql);
+        $this->dbiter = XDB::iterator($sql, $visibility->level());
     }
 
     public function next()
index b7a190c..6574d0a 100644 (file)
@@ -110,10 +110,11 @@ class Profile implements PlExportable
     const FETCH_JOB_TERMS      = 0x000200;
     const FETCH_MENTOR_TERMS   = 0x000400;
     const FETCH_DELTATEN       = 0x000800;
+    const FETCH_PARTNER        = 0x001000;
 
     const FETCH_MINIFICHES   = 0x00012D; // FETCH_ADDRESSES | FETCH_EDU | FETCH_JOBS | FETCH_NETWORKING | FETCH_PHONES
 
-    const FETCH_ALL          = 0x000FFF; // OR of FETCH_*
+    const FETCH_ALL          = 0x001FFF; // OR of FETCH_*
 
     static public $descriptions = array(
         'search_names'    => 'Noms',
@@ -160,12 +161,12 @@ class Profile implements PlExportable
     private $visibility = null;
 
 
-    private function __construct(array $data)
+    private function __construct(array $data, Visibility $visibility)
     {
         $this->data = $data;
         $this->pid = $this->data['pid'];
         $this->hrpid = $this->data['hrpid'];
-        $this->visibility = new ProfileVisibility();
+        $this->visibility = $visibility;
     }
 
     public function id()
@@ -384,7 +385,7 @@ class Profile implements PlExportable
     public function displayEmail()
     {
         $o = $this->owner();
-        if ($o != null) {
+        if ($o != null && $this->isVisible(Visibility::EXPORT_PRIVATE)) {
             return $o->bestEmail();
         } else {
             return $this->email_directory;
@@ -458,7 +459,8 @@ class Profile implements PlExportable
      * Clears a profile.
      *  *always deletes in: profile_addresses, profile_binets, profile_deltaten,
      *      profile_job, profile_langskills, profile_mentor, profile_networking,
-     *      profile_phones, profile_skills, watch_profile
+     *      profile_partnersharing_settings, profile_phones, profile_skills,
+     *      watch_profile
      *  *always keeps in: profile_corps, profile_display, profile_education,
      *      profile_medals, profile_*_names, profile_photos, search_name
      *  *modifies: profiles
@@ -469,7 +471,7 @@ class Profile implements PlExportable
             'profile_job', 'profile_langskills', 'profile_mentor',
             'profile_networking', 'profile_skills', 'watch_profile',
             'profile_phones', 'profile_addresses', 'profile_binets',
-            'profile_deltaten');
+            'profile_deltaten', 'profile_partnersharing_settings');
 
         foreach ($tables as $t) {
             XDB::execute('DELETE FROM  ' . $t . '
@@ -485,15 +487,6 @@ class Profile implements PlExportable
                      $this->id());
     }
 
-    /** Sets the level of visibility of the profile
-     * Sets $this->visibility to a list of valid visibilities.
-     * @param one of the self::VIS_* values
-     */
-    public function setVisibilityLevel($visibility)
-    {
-        $this->visibility->setLevel($visibility);
-    }
-
     /** Determine whether an item with visibility $visibility can be displayed
      * with the current level of visibility of the profile
      * @param $visibility The level of visibility to be checked
@@ -883,7 +876,7 @@ class Profile implements PlExportable
      */
     public function getBinets()
     {
-        if ($this->visibility->isVisible(ProfileVisibility::VIS_PRIVATE)) {
+        if ($this->visibility->isVisible(Visibility::EXPORT_PRIVATE)) {
             return XDB::fetchColumn('SELECT  binet_id
                                        FROM  profile_binets
                                       WHERE  pid = {?}', $this->id());
@@ -893,7 +886,7 @@ class Profile implements PlExportable
     }
     public function getBinetsNames()
     {
-        if ($this->visibility->isVisible(ProfileVisibility::VIS_PRIVATE)) {
+        if ($this->visibility->isVisible(Visibility::EXPORT_PRIVATE)) {
             return XDB::fetchColumn('SELECT  text
                                        FROM  profile_binets AS pb
                                   LEFT JOIN  profile_binet_enum AS pbe ON (pbe.id = pb.binet_id)
@@ -922,6 +915,25 @@ class Profile implements PlExportable
         return $this->medals->medals;
     }
 
+    /** Sharing data with partner websites
+     */
+    private $partners_settings = null;
+    public function setPartnersSettings(ProfilePartnerSharing $partners_settings)
+    {
+        $this->partners_settings = $partners_settings;
+    }
+
+    public function getPartnerSettings($partner_id)
+    {
+        if ($this->partners_settings === null && !$this->fetched(self::FETCH_PARTNER)) {
+            $this->setPartnersSettings($this->getProfileField(self::FETCH_PARTNER));
+        }
+        if ($this->partners_settings === null) {
+            return PartnerSettings::getEmpty($partner_id);
+        }
+        return $this->partners_settings->get($partner_id);
+    }
+
     public function compareNames($firstname, $lastname)
     {
         $_lastname  = mb_strtoupper($this->lastName());
@@ -971,13 +983,15 @@ class Profile implements PlExportable
             $order = '';
         }
 
-        $visibility = new ProfileVisibility($visibility);
+        if ($visibility === null) {
+            $visibility = Visibility::defaultForRead();
+        }
 
         $it = XDB::Iterator('SELECT  p.pid, p.hrpid, p.xorg_id, p.ax_id, p.birthdate, p.birthdate_ref,
                                      p.next_birthday, p.deathdate, p.deathdate_rec, p.sex = \'female\' AS sex,
                                      IF ({?}, p.cv, NULL) AS cv, p.medals_pub, p.alias_pub, p.email_directory,
                                      p.last_change, p.nationality1, p.nationality2, p.nationality3,
-                                     IF (p.freetext_pub IN {?}, p.freetext, NULL) AS freetext,
+                                     IF (p.freetext_pub >= {?}, p.freetext, NULL) AS freetext,
                                      pe.entry_year, pe.grad_year, pe.promo_year, pe.program, pe.fieldid,
                                      IF ({?}, pse.text, NULL) AS section,
                                      ppn.firstname_main AS firstname, ppn.lastname_main AS lastname, IF ({?}, pn.name, NULL) AS nickname,
@@ -985,8 +999,8 @@ class Profile implements PlExportable
                                      IF (ppn.lastname_ordinary = \'\', ppn.firstname_main, ppn.lastname_ordinary) AS lastname_ordinary,
                                      pd.yourself, pd.promo, pd.short_name, pd.public_name AS full_name,
                                      pd.directory_name, pd.public_name, pd.private_name,
-                                     IF (pp.pub IN {?}, pp.display_tel, NULL) AS mobile,
-                                     (ph.pub IN {?} AND ph.attach IS NOT NULL) AS has_photo,
+                                     IF (pp.pub >= {?}, pp.display_tel, NULL) AS mobile,
+                                     (ph.pub >= {?} AND ph.attach IS NOT NULL) AS has_photo, ph.pub as photo_pub,
                                      ph.x AS photo_width, ph.y AS photo_height,
                                      p.last_change < DATE_SUB(NOW(), INTERVAL 365 DAY) AS is_old,
                                      pm.expertise AS mentor_expertise,
@@ -1006,13 +1020,13 @@ class Profile implements PlExportable
                               WHERE  p.pid IN {?}
                            GROUP BY  p.pid
                                      ' . $order,
-                           $visibility->isVisible(ProfileVisibility::VIS_PRIVATE), // CV
-                           $visibility->levels(), // freetext
-                           $visibility->isVisible(ProfileVisibility::VIS_PRIVATE), // section
-                           $visibility->isVisible(ProfileVisibility::VIS_PRIVATE), // nickname
-                           $visibility->levels(), // mobile
-                           $visibility->levels(), // photo
-                           $visibility->isVisible(ProfileVisibility::VIS_PRIVATE), // deltaten_message
+                           $visibility->isVisible(Visibility::EXPORT_PRIVATE), // CV
+                           $visibility->level(), // freetext
+                           $visibility->isVisible(Visibility::EXPORT_PRIVATE), // section
+                           $visibility->isVisible(Visibility::EXPORT_PRIVATE), // nickname
+                           $visibility->level(), // mobile
+                           $visibility->level(), // photo
+                           $visibility->isVisible(Visibility::EXPORT_PRIVATE), // deltaten_message
                            $pids
                        );
         return new ProfileIterator($it, $pids, $fields, $visibility);
@@ -1054,9 +1068,12 @@ class Profile implements PlExportable
      */
     public static function get($login, $fields = 0x0000, $visibility = null)
     {
+        if ($visibility === null) {
+            $visibility = Visibility::defaultForRead();
+        }
+
         if (is_array($login)) {
-            $pf = new Profile($login);
-            $pf->setVisibilityLevel($visibility);
+            $pf = new Profile($login, $visibility);
             return $pf;
         }
         $pid = self::getPID($login);
@@ -1069,7 +1086,7 @@ class Profile implements PlExportable
             if (!($login instanceof PlUser)) {
                 $user = User::getSilent($login);
                 if ($user && $user->hasProfile()) {
-                    return $user->profile();
+                    return $user->profile(false, $fields, $visibility);
                 }
             }
             return null;
@@ -1271,12 +1288,12 @@ class ProfileIterator implements PlIterator
 
     const FETCH_ALL    = 0x000033F; // FETCH_ADDRESSES | FETCH_CORPS | FETCH_EDU | FETCH_JOBS | FETCH_MEDALS | FETCH_NETWORKING | FETCH_PHONES | FETCH_JOB_TERMS
 
-    public function __construct(PlIterator $it, array $pids, $fields = 0x0000, ProfileVisibility $visibility = null)
+    public function __construct(PlIterator $it, array $pids, $fields = 0x0000, $visibility = null)
     {
         require_once 'profilefields.inc.php';
 
-        if ($visibility == null) {
-            $visibility = new ProfileVisibility();
+        if ($visibility === null) {
+            $visibility = Visibility::defaultForRead();
         }
 
         $this->fields = $fields;
@@ -1307,8 +1324,7 @@ class ProfileIterator implements PlIterator
 
     private function fillProfile(array $vals)
     {
-        $pf = Profile::get($vals[0]);
-        $pf->setVisibilityLevel($this->visibility->level());
+        $pf = Profile::get($vals[0], 0x0, $this->visibility);
         $pf->setFetchedFields($this->fields);
 
         if ($this->hasData(Profile::FETCH_PHONES, $vals)) {
diff --git a/classes/profilevisibility.php b/classes/profilevisibility.php
deleted file mode 100644 (file)
index cf5687d..0000000
+++ /dev/null
@@ -1,108 +0,0 @@
-<?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 ProfileVisibility
-{
-    static private $v_values = array(self::VIS_PUBLIC  => array(self::VIS_PUBLIC),
-                                     self::VIS_AX      => array(self::VIS_AX, self::VIS_PUBLIC),
-                                     self::VIS_PRIVATE => array(self::VIS_PRIVATE, self::VIS_AX, self::VIS_PUBLIC));
-
-    const VIS_PUBLIC  = 'public';
-    const VIS_AX      = 'ax';
-    const VIS_PRIVATE = 'private';
-
-    private $level;
-
-    public function __construct($level = null, $force = false)
-    {
-        if ($force) {
-            $this->forceLevel($level);
-        } else {
-            $this->setLevel($level);
-        }
-    }
-
-    public function setLevel($level = self::VIS_PUBLIC)
-    {
-        if ($level != null && $level != self::VIS_PRIVATE && $level != self::VIS_AX && $level != self::VIS_PUBLIC) {
-            Platal::page()->kill("Invalid visibility: " . $level);
-        }
-
-        // Unlogged or not allowed to view directory_ax or requesting public
-        // => public view
-        if (!S::logged() || !S::user()->checkPerms('directory_ax') || $level == self::VIS_PUBLIC) {
-            $level = self::VIS_PUBLIC;
-        // Not allowed to view directory_private or requesting ax
-        } else if (!S::user()->checkPerms('directory_private') || $level == self::VIS_AX) {
-            $level = self::VIS_AX;
-        } else {
-            $level = self::VIS_PRIVATE;
-        }
-
-        if ($this->level == null || $this->level == self::VIS_PRIVATE) {
-            $this->level = $level;
-        } else if ($this->level == self::VIS_AX && $level == self::VIS_PRIVATE) {
-            return;
-        } else {
-            $this->level = self::VIS_PUBLIC;
-        }
-    }
-
-    public function forceLevel($level)
-    {
-        if ($level != self::VIS_PRIVATE && $level != self::VIS_AX && $level != self::VIS_PUBLIC) {
-            Platal::page()->kill('Invalid visibility: ' . $level);
-        }
-
-        $this->level = $level;
-    }
-
-    public function level()
-    {
-        if ($this->level == null) {
-            return self::VIS_PUBLIC;
-        } else {
-            return $this->level;
-        }
-    }
-
-    public function levels()
-    {
-        return self::$v_values[$this->level()];
-    }
-
-    public function isVisible($visibility)
-    {
-        return in_array($visibility, $this->levels());
-    }
-
-    static public function comparePublicity($a, $b)
-    {
-        $a_pub = new ProfileVisibility($a['pub'], true);
-        $b_pub = new ProfileVisibility($b['pub'], true);
-
-        return !$a_pub->isVisible($b_pub->level());
-    }
-}
-
-
-// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
-?>
index f24a8dc..eb3c855 100644 (file)
@@ -225,6 +225,49 @@ class User extends PlUser
         $this->perm_flags = null;
     }
 
+    /** Retrieve the 'general' read visibility.
+     * This is the maximum level of fields that may be viewed by the current user on other profiles.
+     *
+     * Rules are:
+     *  - Everyone can view 'public'
+     *  - directory_ax gives access to 'AX' level
+     *  - directory_private gives access to 'private' level
+     *  - admin gives access to 'hidden' level
+     */
+    public function readVisibility()
+    {
+        $level = Visibility::VIEW_NONE;
+        if ($this->is_admin) {
+            $level = Visibility::VIEW_ADMIN;
+        } elseif ($this->checkPerms('directory_private')) {
+            $level = Visibility::VIEW_PRIVATE;
+        } elseif ($this->checkPerms('directory_ax')) {
+            $level = Visibility::VIEW_AX;
+        } else {
+            $level = Visibility::VIEW_PUBLIC;
+        }
+        return Visibility::get($level);
+    }
+
+    /** Retrieve the 'general' edit visibility.
+     * This is the maximum level of fields that may be edited by the current user on other profiles.
+     *
+     * Rules are:
+     *  - Only admins can edit the 'hidden' fields
+     *  - If someone has 'directory_edit' (which is actually directory_ax_edit): AX level
+     *  - Otherwise, nothing.
+     */
+    public function editVisibility()
+    {
+        $level = Visibility::VIEW_NONE;
+        if ($this->is_admin) {
+            $level = Visibility::VIEW_ADMIN;
+        } elseif ($this->checkPerms('directory_edit')) {
+            $level = Visibility::VIEW_AX;
+        }
+        return Visibility::get($level);
+    }
+
     // We do not want to store the password in the object.
     // So, fetch it 'on demand'
     public function password()
@@ -320,6 +363,8 @@ class User extends PlUser
         if (!$this->_profile_fetched || $forceFetch) {
             $this->_profile_fetched = true;
             $this->_profile = Profile::get($this, $fields, $visibility);
+        } else if ($this->_profile !== null && !$this->_profile->visibility->equals($visibility)) {
+            return Profile::get($this, $fields, $visibility);
         }
         return $this->_profile;
     }
@@ -841,7 +886,6 @@ class User extends PlUser
     public static function makePerms($perms, $is_admin)
     {
         $flags = new PlFlagSet($perms);
-        $flags->addFlag(PERMS_USER);
         if ($is_admin) {
             $flags->addFlag(PERMS_ADMIN);
         }
index 21fc1bf..983b2ee 100644 (file)
@@ -85,7 +85,9 @@ class UserFilter extends PlFilter
     private $orderby = null;
 
     // Store the current 'search' visibility.
-    private $profile_visibility = null;
+    private $visibility = null;
+    // If the 'search' visibility should be based on a DB field instead.
+    private $visibility_field = null;
 
     private $lastusercount = null;
     private $lastprofilecount = null;
@@ -117,27 +119,50 @@ class UserFilter extends PlFilter
         }
 
         // This will set the visibility to the default correct level.
-        $this->profile_visibility = new ProfileVisibility();
+        $this->visibility = Visibility::defaultForRead();
     }
 
-    public function getVisibilityLevels()
-    {
-        return $this->profile_visibility->levels();
-    }
-
-    public function getVisibilityLevel()
-    {
-        return $this->profile_visibility->level();
-    }
-
-    public function restrictVisibilityTo($level)
-    {
-        $this->profile_visibility->setLevel($level);
+    /** Get the SQL condition to filter by visibility level for a field.
+     * This will return a SQL condition which evaluates to True if the given
+     * field display level is available from the current access level.
+     * @param $field Name of the field holding a display level
+     * @return string SQL condition, properly escaped, for that field.
+     */
+    public function getVisibilityConditionForField($field)
+    {
+        if ($this->visibility_field != null) {
+            // Use enum 'bit' arithmetic.
+            // Display levels are ordered as 'hidden, private, ax, public'
+            // Thus ax > private.
+            // The $sub.display_level cell will contain the 'most private' display
+            // level available based on $field. If it is 'ax' and $field is
+            // 'private','ax' <= 'private' is false.
+            $sub = $this->addVisibilityFieldFilter($this->visibility_field);
+            return $sub . '.best_display_level + 0 <= 0 + ' . $field;
+        } else {
+            $sub = $this->addVisibilityAbsoluteFilter($this->visibility->level());
+            return $sub . '.best_display_level + 0 <= 0 + ' . $field;
+        }
     }
 
-    public function getVisibilityCondition($field)
+    /** Get the SQL condition to filter by a given visibility level.
+     * @param $level One of Visibility::EXPORT_*
+     * @return string A SQL condition, properly escaped, which evaluates to 'true' if the $level can be viewed with the current access level.
+     */
+    public function getVisibilityConditionAbsolute($level)
     {
-        return $field . ' IN ' . XDB::formatArray($this->getVisibilityLevels());
+        if ($this->visibility_field != null) {
+            // The $sub.display_levels cell will contain allowed display levels
+            // for an access level of $this->visibility_field.
+            $sub = $this->addVisibilityFieldFilter($this->visibility_field);
+            return XDB::format('FIND_IN_SET({?}, ' . $sub . '.display_levels)', $level);
+        } else {
+            if ($this->visibility->isVisible($level)) {
+                return 'TRUE';
+            } else {
+                return 'FALSE';
+            }
+        }
     }
 
     private function buildQuery()
@@ -1352,6 +1377,77 @@ class UserFilter extends PlFilter
             return array();
         }
     }
+
+
+    /** PARTNER SHARING
+     */
+
+    // Lists partner shortnames in use, as a $partner_shortname => true map.
+    private $ppss = array();
+
+    /** Add a filter on user having settings for a given partner.
+     * @param $partner_id the ID of the partner
+     * @return the name of the table to use in joins (e.g ppss_$partner_id).
+     */
+    public function addPartnerSharingFilter($partner_id)
+    {
+        $this->requireProfiles();
+        $sub = "ppss_" . $partner_id;
+        $this->ppss[$sub] = $partner_id;
+        return $sub;
+    }
+
+    protected function partnerSharingJoins()
+    {
+        $joins = array();
+        foreach ($this->ppss as $sub => $partner_id) {
+            $joins[$sub] = PlSqlJoin::left('profile_partnersharing_settings', '$ME.pid = $PID AND $ME.partner_id = {?} AND $ME.sharing_level != \'none\'', $partner_id);
+        }
+        return $joins;
+    }
+
+    public function restrictVisibilityForPartner($partner_id)
+    {
+        $sub = $this->addPartnerSharingFilter($partner_id);
+        $this->visibility_field = $sub . '.sharing_level';
+    }
+
+    /** VISIBILITY
+     */
+    private $vlevels = array();
+    private $vfields = array();
+    public function addVisibilityAbsoluteFilter($level)
+    {
+        $sub = 'pvel_' . $level;
+        $this->vlevels[$level] = $sub;
+        return $sub;
+    }
+
+    public function addVisibilityFieldFilter($field)
+    {
+        $sub = 'pvef_' . self::getDBSuffix($field);
+        $this->vfields[$field] = $sub;
+        return $sub;
+    }
+
+    /** Since this method might perform inner joins on tables which have been
+     * joined previously (e.g when using addVisibilityFieldFilter), it has to
+     * come after the Joins() methods for those tables.
+     * This is due to the implementation logic for discovering joins and the
+     * ordering used by PHP introspection.
+     */
+    protected function visibilityJoins()
+    {
+        $joins = array();
+        foreach ($this->vlevels as $level => $sub) {
+            $joins[$sub] = PlSqlJoin::inner('profile_visibility_enum', '$ME.access_level = {?}', $level);
+        }
+        foreach ($this->vfields as $field => $sub) {
+            $joins[$sub] = PlSqlJoin::inner('profile_visibility_enum', '$ME.access_level = ' . $field);
+        }
+        return $joins;
+    }
+
 }
 // }}}
 // {{{ class ProfileFilter
index 4070c3f..cb8f5de 100644 (file)
@@ -410,7 +410,7 @@ class UFC_Comment extends UserFilterCondition
     public function buildCondition(PlFilter $uf)
     {
         $uf->requireProfiles();
-        return $uf->getVisibilityCondition('p.freetext_pub') . ' AND p.freetext ' . XDB::formatWildcards(XDB::WILDCARD_CONTAINS, $this->text);
+        return $uf->getVisibilityConditionForField('p.freetext_pub') . ' AND p.freetext ' . XDB::formatWildcards(XDB::WILDCARD_CONTAINS, $this->text);
     }
 
     public function export()
@@ -668,6 +668,7 @@ class UFC_NameTokens extends UserFilterCondition
             if ($this->general_type) {
                 $c .= XDB::format(' AND ' . $sub . '.general_type = {?}', $this->general_type);
             }
+            $c .= ' AND (' . $uf->getVisibilityConditionAbsolute(Visibility::EXPORT_PRIVATE) . ' OR ' . $sub . '.general_type != \'nickname\')';
             $conds[] = $c;
         }
 
@@ -919,12 +920,9 @@ class UFC_Binet extends UserFilterCondition
 
     public function buildCondition(PlFilter $uf)
     {
-        // Binets are private.
-        if ($uf->getVisibilityLevel() != ProfileVisibility::VIS_PRIVATE) {
-            return self::COND_TRUE;
-        }
         $sub = $uf->addBinetsFilter();
-        return XDB::format($sub . '.binet_id IN {?}', $this->val);
+        // Binets are private.
+        return XDB::format($uf->getVisibilityConditionAbsolute(Visibility::EXPORT_PRIVATE) . ' AND ' . $sub . '.binet_id IN {?}', $this->val);
     }
 }
 // }}}
@@ -944,11 +942,8 @@ class UFC_Section extends UserFilterCondition
     public function buildCondition(PlFilter $uf)
     {
         // Sections are private.
-        if ($uf->getVisibilityLevel() != ProfileVisibility::VIS_PRIVATE) {
-            return self::COND_TRUE;
-        }
         $uf->requireProfiles();
-        return XDB::format('p.section IN {?}', $this->section);
+        return XDB::format($uf->getVisibilityConditionAbsolute(Visibility::EXPORT_PRIVATE) . ' AND p.section IN {?}', $this->section);
     }
 }
 // }}}
@@ -1106,7 +1101,7 @@ class UFC_AddressComponent extends UFC_Address
     public function buildCondition(PlFilter $uf)
     {
         $sub = $uf->addAddressFilter($this->fieldtype);
-        $conds = $this->initConds($sub, $uf->getVisibilityCondition('pa' . $sub . '.pub'));
+        $conds = $this->initConds($sub, $uf->getVisibilityConditionForField('pa' . $sub . '.pub'));
         $conds[] = XDB::format('pace' . $sub . '.id IN {?}', $this->val);
 
         return implode(' AND ', $conds);
@@ -1149,7 +1144,7 @@ class UFC_Corps extends UserFilterCondition
         }
         // XXX(x2006barrois): find a way to get rid of that hardcoded
         // reference to 'pc'.
-        $cond .= ' AND ' . $uf->getVisibilityCondition('pc.corps_pub');
+        $cond .= ' AND ' . $uf->getVisibilityConditionForField('pc.corps_pub');
         return $cond;
     }
 }
@@ -1183,7 +1178,7 @@ class UFC_Corps_Rank extends UserFilterCondition
         }
         // XXX(x2006barrois): find a way to get rid of that hardcoded
         // reference to 'pc'.
-        $cond .= ' AND ' . $uf->getVisibilityCondition('pc.corps_pub');
+        $cond .= ' AND ' . $uf->getVisibilityConditionForField('pc.corps_pub');
         return $cond;
     }
 }
@@ -1221,7 +1216,7 @@ class UFC_Job_Company extends UserFilterCondition
         $sub = $uf->addJobCompanyFilter();
         $cond  = $sub . '.' . $this->type . XDB::formatWildcards(XDB::WILDCARD_CONTAINS, $this->value);
         $jsub = $uf->addJobFilter();
-        $cond .= ' AND ' . $uf->getVisibilityCondition($jsub . '.pub');
+        $cond .= ' AND ' . $uf->getVisibilityConditionForField($jsub . '.pub');
         return $cond;
     }
 }
@@ -1251,7 +1246,7 @@ class UFC_Job_Terms extends UserFilterCondition
             $conditions[] = $sub[$i] . '.jtid_1 = ' . XDB::escape($jtid);
         }
         $jsub = $uf->addJobFilter();
-        $conditions[] = $uf->getVisibilityCondition($jsub . '.pub');
+        $conditions[] = $uf->getVisibilityConditionForField($jsub . '.pub');
         return implode(' AND ', $conditions);
     }
 }
@@ -1278,24 +1273,18 @@ class UFC_Job_Description extends UserFilterCondition
         $conds = array();
 
         $jsub = $uf->addJobFilter();
-        // CV is private => if only CV requested, and not private,
-        // don't do anything. Otherwise restrict to standard job visibility.
-        if ($this->fields == UserFilter::JOB_CV) {
-           if ($uf->getVisibilityLevel() != ProfileVisibility::VIS_PRIVATE) {
-               return self::COND_TRUE;
-           }
-        }
         if ($this->fields & UserFilter::JOB_USERDEFINED) {
             $conds[] = $jsub . '.description ' . XDB::formatWildcards(XDB::WILDCARD_CONTAINS, $this->description);
         }
-        if ($this->fields & UserFilter::JOB_CV && $uf->getVisibilityLevel() == ProfileVisibility::VIS_PRIVATE) {
+        if ($this->fields & UserFilter::JOB_CV) {
             $uf->requireProfiles();
-            $conds[] = 'p.cv ' . XDB::formatWildcards(XDB::WILDCARD_CONTAINS, $this->description);
+            // CV is private
+            $conds[] = '( ' . $uf->getVisibilityConditionAbsolute(Visibility::EXPORT_PRIVATE) . ' AND p.cv ' . XDB::formatWildcards(XDB::WILDCARD_CONTAINS, $this->description) . ')';
         }
         if (count($conds) == 0) {
             return self::COND_TRUE;
         }
-        return $uf->getVisibilityCondition($jsub . '.pub') . ' AND ( ' . implode(' OR ', $conds) . ' )';
+        return $uf->getVisibilityConditionForField($jsub . '.pub') . ' AND ( ' . implode(' OR ', $conds) . ' )';
     }
 }
 // }}}
@@ -1319,7 +1308,7 @@ class UFC_Networking extends UserFilterCondition
     {
         $sub = $uf->addNetworkingFilter();
         $conds = array();
-        $conds[] = $uf->getVisibilityCondition($sub . '.pub');
+        $conds[] = $uf->getVisibilityConditionForField($sub . '.pub');
         $conds[] = $sub . '.address ' . XDB::formatWildcards(XDB::WILDCARD_CONTAINS, $this->value);
         if ($this->type != -1) {
             $conds[] = $sub . '.nwid = ' . XDB::format('{?}', $this->type);
@@ -1364,7 +1353,7 @@ class UFC_Phone extends UserFilterCondition
         $sub = $uf->addPhoneFilter();
         $conds = array();
 
-        $conds[] = $uf->getVisibilityCondition($sub . '.pub');
+        $conds[] = $uf->getVisibilityConditionForField($sub . '.pub');
 
         $conds[] = $sub . '.search_tel = ' . XDB::format('{?}', $this->number);
         if ($this->num_type != self::NUM_ANY) {
@@ -1400,7 +1389,7 @@ class UFC_Medal extends UserFilterCondition
         // This will require profiles => table 'p' will be available.
         $sub = $uf->addMedalFilter();
 
-        $conds[] = $uf->getVisibilityCondition('p.medals_pub');
+        $conds[] = $uf->getVisibilityConditionForField('p.medals_pub');
 
         $conds[] = $sub . '.mid = ' . XDB::format('{?}', $this->medal);
         if ($this->grade != null) {
@@ -1418,7 +1407,7 @@ class UFC_Photo extends UserFilterCondition
     public function buildCondition(PlFilter $uf)
     {
         $sub = $uf->addPhotoFilter();
-        return $sub . '.attach IS NOT NULL AND ' . $uf->getVisibilityCondition($sub . '.pub');
+        return $sub . '.attach IS NOT NULL AND ' . $uf->getVisibilityConditionForField($sub . '.pub');
     }
 }
 // }}}
@@ -1627,6 +1616,72 @@ class UFC_MarketingHash extends UserFilterCondition
     }
 }
 // }}}
+// {{{ class UFC_PartnerSharing
+/** Filters users, keeping only those sharing data with a given partner.
+ */
+class UFC_PartnerSharing extends UserFilterCondition
+{
+    const PTA = 'pta';
+
+    private $partner_id;
+
+    public function __construct($partner_id)
+    {
+        $this->partner_id = $partner_id;
+    }
+
+    public function buildCondition(PlFilter $uf)
+    {
+        $sub = $uf->addPartnerSharingFilter($this->partner_id);
+        return XDB::format("$sub.exposed_uid IS NOT NULL");
+    }
+}
+// }}}
+// {{{ class UFC_PartnerSharingEmail
+/** Filters users, keeping only those allowing emails to be sent by
+ * a given partner.
+ */
+class UFC_PartnerSharingEmail extends UserFilterCondition
+{
+    private $partner_id;
+
+    public function __construct($partner_id)
+    {
+        $this->partner_id = $partner_id;
+    }
+
+    public function buildCondition(PlFilter $uf)
+    {
+        $sub = $uf->addPartnerSharingFilter($this->partner_id);
+        return XDB::format("$sub.allow_email IN ('digest', 'direct')");
+    }
+}
+// }}}
+// {{{ class UFC_PartnerSharingID
+/** Filters users according to a list of partner-known IDs
+ */
+class UFC_PartnerSharingID extends UserFilterCondition
+{
+    private $partner_id;
+    private $ids;
+
+    public function __construct($partner_id)
+    {
+        $this->partner_id = $partner_id;
+        $ids = func_get_args();
+        array_shift($ids);
+        $this->ids   = pl_flatten($ids);
+    }
+
+    public function buildCondition(PlFilter $uf)
+    {
+        $uf->requireProfiles();
+        $ids = $this->ids;
+        $sub = $uf->addPartnerSharingFilter($this->partner_id);
+        return XDB::format("$sub.exposed_uid IN {?}", $ids);
+    }
+}
+// }}}
 
 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
 ?>
diff --git a/classes/visibility.php b/classes/visibility.php
new file mode 100644 (file)
index 0000000..dba6578
--- /dev/null
@@ -0,0 +1,163 @@
+<?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 Visibility
+{
+    /** Visibility levels.
+     * The VIEW_* constants describe the access level
+     * The EXPORT_* constants describe the degree of confidentiality of the data.
+     */
+    const VIEW_NONE = 'none';
+    const VIEW_PUBLIC = 'public';
+    const VIEW_AX = 'ax';
+    const VIEW_PRIVATE = 'private';
+    const VIEW_ADMIN = 'admin';
+
+    const EXPORT_PUBLIC = 'public';
+    const EXPORT_AX = 'ax';
+    const EXPORT_PRIVATE = 'private';
+    const EXPORT_HIDDEN = 'hidden';
+
+    /** Map each VIEW_ level to the list of EXPORT_ levels it can view.
+     */
+    static private $view_levels = array(
+        self::VIEW_NONE    => array(),
+        self::VIEW_PUBLIC  => array(self::EXPORT_PUBLIC),
+        self::VIEW_AX      => array(self::EXPORT_AX, self::EXPORT_PUBLIC),
+        self::VIEW_PRIVATE => array(self::EXPORT_PRIVATE, self::EXPORT_AX, self::EXPORT_PUBLIC),
+        self::VIEW_ADMIN   => array(self::EXPORT_HIDDEN, self::EXPORT_PRIVATE, self::EXPORT_AX, self::EXPORT_PUBLIC),
+    );
+
+    static private $display_levels = array(
+        self::EXPORT_PUBLIC => 0,
+        self::EXPORT_AX => 1,
+        self::EXPORT_PRIVATE => 2,
+        self::EXPORT_HIDDEN => 3,
+    );
+
+    private $level;
+
+    private function __construct($level)
+    {
+        $this->level = $level;
+    }
+
+    static private $vis_list = array();
+    public static function get($level)
+    {
+        Platal::assert(array_key_exists($level, self::$view_levels), "Invalid visibility access level $level.");
+        if (!array_key_exists($level, self::$vis_list)) {
+            self::$vis_list[$level] = new Visibility($level);
+        }
+        return self::$vis_list[$level];
+    }
+
+    public function level()
+    {
+        return $this->level;
+    }
+
+    public static function defaultForRead($max_level = null)
+    {
+        if (!S::logged()) {
+            $vis = self::get(self::VIEW_PUBLIC);
+        } else {
+            $vis = S::user()->readVisibility();
+        }
+        if ($max_level != null) {
+            return $vis->restrict($max_level);
+        } else {
+            return $vis;
+        }
+    }
+
+    public static function defaultForEdit($max_level = null)
+    {
+        if (!S::logged()) {
+            $vis = self::get(self::VIEW_NONE);
+        } else {
+            $vis = S::user()->editVisibility();
+        }
+        if ($max_level != null) {
+            return $vis->restrict($max_level);
+        } else {
+            return $vis;
+        }
+    }
+
+    /** Retrieve a 'restricted' version of the current Visibility.
+     *
+     * @param $level The visibility level to restrict to
+     * @return A new Visibility instance, whose level is min($this->level, $level)
+     */
+    public function restrict($level = null)
+    {
+        if ($level != null && !$this->isVisible($level)) {
+            $level = $this->level();
+        }
+
+        return self::get($level);
+    }
+
+    public function isVisible($visibility)
+    {
+        return in_array($visibility, self::$view_levels[$this->level()]);
+    }
+
+    public function equals($visibility)
+    {
+        return $visibility !== null && $this->level() == $visibility->level();
+    }
+
+    static public function isLessRestrictive($level_a, $level_b)
+    {
+        // self::$display_levels is order from least restrictive
+        // to most restrictive.
+        return self::$display_levels[$a] >= self::$display_levels[$b];
+    }
+
+    /** Compare the visibility of two fields.
+     * Returns:
+     *   >0 if $a is less restrictive than $b,
+     *   <0 if $a is more restrictive than $b,
+     *   0  if $a is equal to $b.
+     */
+    static public function cmpLessRestrictive($a, $b)
+    {
+        $a_pub = self::$display_levels[$a];
+        $b_pub = self::$display_levels[$b];
+        /* self::$display_levels is ordered from least restrictive to
+         * most restrictive.
+         * This will be 0 if both levels are equal, < 0 if $b_pub is less
+         * than $a_pub, thus less restrictive, which means that $a comes
+         * before $b in descending restrictiveness order.
+         */
+        return $b_pub - $a_pub;
+    }
+
+    static public function comparePublicity($a, $b)
+    {
+        return self::cmpLessRestrictive($a['pub'], $b['pub']);
+    }
+}
+
+// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
+?>
index 6dc2e23..9e24b3a 100644 (file)
@@ -23,8 +23,17 @@ class Xnet extends Platal
 {
     public function __construct()
     {
-        parent::__construct('xnet', 'xnetgrp', 'xnetlists', 'xnetevents',
-                            'payment', 'bandeau', 'xnetnl', 'geoloc');
+        parent::__construct(
+            'xnet',
+
+            'bandeau',
+            'geoloc',
+            'payment',
+            'xnetevents',
+            'xnetgrp',
+            'xnetlists',
+            'xnetnl'
+        );
     }
 
     public function hook_map($name)
index d32374d..69c121b 100644 (file)
@@ -86,7 +86,7 @@ class XnetPage extends PlPage
         $sub['documentation']     = 'Xnet';
         if (S::user()->type == 'xnet') {
             $sub['mon compte'] = 'edit';
-            $sub['mon mot de passe'] = 'password';
+            $sub['mes préférences'] = $globals->xnet->xorg_baseurl . 'prefs';
         }
         $sub['signaler un bug']   = array('href' => 'send_bug/'.$_SERVER['REQUEST_URI'], 'class' => 'popup_840x600');
         $menu["no_title"]   = $sub;
index 27ed4d8..b6fe1f7 100644 (file)
@@ -34,26 +34,6 @@ class XnetSession extends XorgSession
             }
         }
 
-        if (!S::logged() && Post::has('auth_type') && Post::v('auth_type') == 'xnet' && !Post::has('wait')) {
-            $email = Post::v('username');
-            $type = XDB::fetchOneCell('SELECT  type
-                                         FROM  accounts
-                                        WHERE  email = {?}',
-                                      $email);
-            if ((!is_null($type) && $type != 'xnet') || !User::isForeignEmailAddress($email)) {
-                Platal::page()->trigErrorRedirect('Ce formulaire d\'authentification est réservé aux extérieurs à la communauté polytechnicienne.', '');
-            }
-
-            $user = parent::doAuth(AUTH_MDP);
-            if (is_null($user)) {
-                return false;
-            }
-            if (!parent::checkAuth(AUTH_MDP) || !parent::startSessionAs($user, AUTH_MDP)) {
-                $this->destroy();
-                return false;
-            }
-        }
-
         global $globals;
         if (!S::logged() && $globals->xnet->auth_baseurl) {
             // prevent connection to be linked to disconnection
@@ -97,7 +77,7 @@ class XnetSession extends XorgSession
 
     protected function doAuth($level)
     {
-        if (S::identified()) { // ok, c'est bon, on n'a rien à faire
+        if (S::identified()) { // Nothing to do there
             return User::getSilentWithValues(null, array('uid' => S::i('uid')));
         }
         if (!Get::has('auth')) {
@@ -118,6 +98,7 @@ class XnetSession extends XorgSession
         if (!$user->checkPerms('groups')) {
             return false;
         }
+
         if ($level == AUTH_SUID) {
             S::set('auth', AUTH_MDP);
         }
@@ -134,8 +115,8 @@ class XnetSession extends XorgSession
         S::set('perms', $user->perms);
         S::set('is_admin', $user->is_admin);
 
-
-        $this->makePerms($user->perms, $user->is_admin);
+        // Add the 'user' perms to the user.
+        $this->makePerms($user->perms . ',' . PERMS_USER, $user->is_admin);
         S::kill('challenge');
         S::kill('loginX');
         S::kill('may_update');
@@ -156,7 +137,7 @@ class XnetSession extends XorgSession
         if (!$this->startSUID($user)) {
             return false;
         }
-        S::set('perms', User::makePerms(PERMS_USER));
+        S::set('perms', User::makePerms(PERMS_USER . ",groups"));
         return true;
     }
 
index ec7aee9..c53f31a 100644 (file)
@@ -23,12 +23,39 @@ class Xorg extends Platal
 {
     public function __construct()
     {
-        parent::__construct('auth', 'carnet', 'email', 'events', 'forums',
-                            'lists', 'marketing', 'payment', 'platal',
-                            'profile', 'register', 'search', 'stats', 'admin',
-                            'newsletter', 'axletter', 'epletter', 'bandeau', 'survey',
-                            'fusionax', 'gadgets', 'googleapps', 'poison',
-                            'openid', 'reminder', 'api', 'urlshortener', 'deltaten', 'geoloc');
+        parent::__construct(
+            'auth',
+
+            'admin',
+            'api',
+            'axletter',
+            'bandeau',
+            'carnet',
+            'deltaten',
+            'email',
+            'epletter',
+            'events',
+            'forums',
+            'fusionax',
+            'gadgets',
+            'geoloc',
+            'googleapps',
+            'lists',
+            'marketing',
+            'newsletter',
+            'openid',
+            'payment',
+            'platal',
+            'poison',
+            'profile',
+            'register',
+            'reminder',
+            'search',
+            'sharingapi',
+            'stats',
+            'survey',
+            'urlshortener'
+        );
     }
 
     public function find_hook()
index 852b842..2bf2062 100644 (file)
@@ -21,6 +21,9 @@
 
 class XorgPage extends PlPage
 {
+    protected $forced_skin = null;
+    protected $default_skin = null;
+
     public function __construct()
     {
         parent::__construct();
@@ -52,13 +55,36 @@ class XorgPage extends PlPage
         }
     }
 
+    /** Force the skin to use, bypassing user choice.
+     * Typically used for the 'register' page.
+     * @param $skin The skin to use.
+     */
+    public function forceSkin($skin)
+    {
+        $this->forced_skin = $skin;
+    }
+
+    /** Choose another 'default' skin.
+     * Typically used for the 'Auth Groupe X' login page.
+     * @param $skin The default skin to use.
+     */
+    public function setDefaultSkin($skin)
+    {
+        $this->default_skin = $skin;
+    }
+
     public function run()
     {
         global $globals, $platal;
-        if (isset($platal) && $platal->path == 'register') {
-            $skin = $globals->register_skin . ".tpl";
+        if ($this->forced_skin !== null) {
+            $skin = $this->forced_skin . '.tpl';
         } else {
-            $skin = S::v('skin', $globals->skin . ".tpl");
+            if ($this->default_skin === null) {
+                $default_skin = $globals->skin;
+            } else {
+                $default_skin = $this->default_skin;
+            }
+            $skin = S::v('skin', $default_skin . '.tpl');
         }
         $this->_run('skin/' . $skin);
     }
index 815ad81..84128af 100644 (file)
@@ -77,36 +77,29 @@ class XorgSession extends PlSession
         return self::INVALID_USER;
     }
 
-    private function checkPassword($uname, $login, $response, $login_type)
+    const TEXT_INVALID_LOGIN = "Mot de passe ou nom d'utilisateur invalide";
+    const TEXT_INVALID_PASS = "Mot de passe invalide";
+
+    private function checkPassword($login, User $user, $response)
     {
-        if ($login_type == 'alias') {
-            $res = XDB::query('SELECT  a.uid, a.password
-                                 FROM  accounts             AS a
-                           INNER JOIN  email_source_account AS e ON (e.uid = a.uid)
-                                WHERE  e.email = {?}',
-                              $login);
+        if ($user === null) {
+            Platal::page()->trigError(self::TEXT_INVALID_LOGIN);
+            return false;
         } else {
-            $res = XDB::query('SELECT  uid, password
-                                 FROM  accounts
-                                WHERE  ' . $login_type . ' = {?}',
-                              $login);
-        }
-        if (list($uid, $password) = $res->fetchOneRow()) {
-            $expected_response = sha1("$uname:$password:" . S::v('challenge'));
+            $password = $user->password();
+            $expected_response = sha1("$login:$password:" . S::v('challenge'));
             /* Deprecates len(password) > 10 conversion. */
             if ($response != $expected_response) {
                 if (!S::logged()) {
-                    Platal::page()->trigError('Mot de passe ou nom d\'utilisateur invalide');
+                    Platal::page()->trigError(self::TEXT_INVALID_LOGIN);
                 } else {
-                    Platal::page()->trigError('Mot de passe invalide');
+                    Platal::page()->trigError(self::TEXT_INVALID_PASS);
                 }
                 S::logger($uid)->log('auth_fail', 'bad password');
-                return null;
+                return false;
             }
-            return $uid;
+            return true;
         }
-        Platal::page()->trigError('Mot de passe ou nom d\'utilisateur invalide');
-        return null;
     }
 
 
@@ -138,60 +131,27 @@ class XorgSession extends PlSession
         /** We come from an authentication form.
          */
         if (S::suid()) {
-            $login = $uname = S::suid('uid');
-            $loginType = 'uid';
+            $login = S::suid('uid');
         } else {
-            $uname = Post::v('username');
-            if (Post::s('domain') == "alias") {
-                $login = XDB::fetchOneCell('SELECT  uid
-                                              FROM  email_source_account
-                                             WHERE  email = {?} AND type = \'alias_aux\'',
-                                           $uname);
-                $loginType = 'uid';
-            } else if (Post::s('domain') == 'hruid') {
-                $login = $uname;
-                $loginType = 'hruid';
-            } else if ((Post::s('domain') == 'email')) {
-                $login = XDB::fetchOneCell('SELECT  SQL_CALC_FOUND_ROWS uid
-                                              FROM  accounts
-                                             WHERE  email = {?}',
-                                           $uname);
-                if (!(XDB::fetchOneCell('SELECT FOUND_ROWS()') == 1)) {
-                    $login =null;
-                }
-                $loginType = 'uid';
-            } else {
-                $login = $uname;
-                $loginType = is_numeric($uname) ? 'uid' : 'alias';
-            }
+            $login = Post::v('username');
         }
 
-        $uid = $this->checkPassword($uname, $login, Post::v('response'), $loginType);
-        if (!is_null($uid) && S::suid()) {
-            if (S::suid('uid') == $uid) {
-                $uid = S::i('uid');
-            } else {
-                $uid = null;
-            }
+        $user = User::getSilent($login);
+
+        $success = $this->checkPassword($login, $user, Post::v('response'));
+
+        if (!is_null($user) && S::suid()) {
+            $success = (S::suid('uid') == $user->id());
+        } else {
+            $success = $this->checkPassword($login, $user, Post::v('response'));
         }
-        if (!is_null($uid)) {
+
+        if ($success) {
             S::set('auth', AUTH_MDP);
-            if (!S::suid()) {
-                if (Post::has('domain')) {
-                    $domain = Post::v('domain', 'login');
-                    if ($domain == 'alias') {
-                        Cookie::set('domain', 'alias', 300);
-                    } else if ($domain == 'ax') {
-                        Cookie::set('domain', 'ax', 300);
-                    } else {
-                        Cookie::kill('domain');
-                    }
-                }
-            }
             S::kill('challenge');
-            S::logger($uid)->log('auth_ok');
+            S::logger($user->id())->log('auth_ok');
         }
-        return User::getSilentWithUID($uid);
+        return $user;
     }
 
     protected function startSessionAs($user, $level)
@@ -228,7 +188,10 @@ class XorgSession extends PlSession
         $this->securityChecks();
         $this->setSkin();
         $this->updateNbNotifs();
-        check_redirect();
+        // Only check email redirection for 'internal' users.
+        if ($user->checkPerms(PERMS_USER)) {
+            check_redirect();
+        }
 
         // We should not have to use this private data anymore
         S::kill('auth_by_cookie');
index c8f6888..fc294d4 100644 (file)
@@ -79,6 +79,9 @@ replyto=registration+watch@staff.m4x.org
 from="Polytechnique.org" <validation_modification@polytechnique.org>
 cc="Polytechnique.org" <validation_modification@polytechnique.org>
 
+[password_recovery_xnet]
+from="Gestion des mots de passe" <support+password@polytechnique.org>
+
 [xnet_notification]
 from="Polytechnique.org" <contact@polytechnique.org>
 
index 658f728..6c4eb46 100644 (file)
@@ -149,10 +149,6 @@ memcache = ""
 ; select a skin.
 skin = "default"
 
-; $globals->register_skin
-; Skin to use during registration process
-register_skin = "register"
-
 ; $globals->core->econfiance
 ; Key used to perform transactions with the 'econfiance' webservice in order
 ; to synchronize the X-Informatique directory with polytechnique.net
@@ -402,6 +398,14 @@ private_max = 800
 per_page    =  20
 
 
+; The SharingAPI section contains parameters about the 'sharing' webservice
+[SharingAPI]
+
+; $globals->sharingapi->max_result_per_query
+; Maximum number of results returned in an API response.
+max_result_per_query = 10;
+
+
 ; The register section contains parameters for the registration process
 [Register]
 
@@ -438,9 +442,13 @@ private_key_ax = ""
 secret       = ""
 
 ; $globals->xnet->auth_baseurl
-; Baseur of the site used to perform authentication
+; Baseurl of the site used to perform authentication
 auth_baseurl = ""
 
+; $globals->xnet->xorg_baseurl
+; Baseurl of the 'X.org' website
+xorg_baseurl = "https://www.polytechnique.org/"
+
 ; $globals->xnet->evts_domain
 ; Domain address where the aliases containing the list of (non-)subscribers
 ; of an event are created.
diff --git a/core b/core
index 68785a0..a552b53 160000 (submodule)
--- a/core
+++ b/core
@@ -1 +1 @@
-Subproject commit 68785a05436244b5032f4399bb47f21b161075c9
+Subproject commit a552b53ac5b4e512720d77b457bfda823ed3e10b
index c05ed85..f069982 100644 (file)
@@ -662,7 +662,6 @@ function doChallengeResponse() {
     }
     document.forms.loginsub.username.value = document.forms.login.username.value;
     document.forms.loginsub.remember.value = document.forms.login.remember.checked;
-    document.forms.loginsub.domain.value = document.forms.login.domain.value;
     document.forms.login.password.value = "";
     document.forms.loginsub.submit();
 }
diff --git a/include/partnersharing.inc.php b/include/partnersharing.inc.php
new file mode 100644 (file)
index 0000000..7a7b6c7
--- /dev/null
@@ -0,0 +1,108 @@
+<?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 PartnerSharing
+// Holds data about a "directory partner".
+class PartnerSharing
+{
+    public $id;
+    public $shortname;
+    public $name;
+    public $url;
+    public $has_directory = false;
+    public $has_bulkmail = false;
+    public $default_sharing_level = Visibility::VIEW_NONE;
+    protected $api_uid = null;
+
+    protected function __construct(array $data)
+    {
+        foreach ($data as $key => $val) {
+            $this->$key = $val;
+        }
+    }
+
+    public function apiUser()
+    {
+        return User::getSilentWithUID($this->api_uid);
+    }
+
+    public static function fetchByAPIUser(User $user)
+    {
+        $res = XDB::fetchOneAssoc('SELECT  id, shortname, name, url,
+                                           has_directory, has_bulkmail,
+                                           default_sharing_level, api_uid
+                                     FROM  profile_partnersharing_enum
+                                    WHERE  api_uid = {?}', $user->uid);
+        if ($res == null) {
+            return null;
+        } else {
+            return new PartnerSharing($res);
+        }
+    }
+
+    public static function fetchById($id)
+    {
+        $res = XDB::fetchOneAssoc('SELECT  id, shortname, name, url,
+                                           has_directory, has_bulkmail,
+                                           default_sharing_level, api_uid
+                                     FROM  profile_partnersharing_enum
+                                    WHERE  id = {?}', $id);
+        if ($res == null) {
+            return null;
+        } else {
+            return new PartnerSharing($res);
+        }
+    }
+}
+// }}}
+// {{{ class PartnerSettings
+class PartnerSettings
+{
+    public $exposed_uid;
+    public $sharing_level;
+    public $allow_email = false;
+    protected $partner_id;
+    public $partner = null;
+
+    public function __construct(array $data)
+    {
+        foreach ($data as $key => $val) {
+            $this->$key = $val;
+        }
+        $this->partner = PartnerSharing::fetchById($this->partner_id);
+        $this->sharing_visibility = Visibility::get($this->sharing_level);
+    }
+
+    public static function getEmpty($partner_id)
+    {
+        $data = array(
+            'partner_id' => $partner_id,
+            'exposed_uid' => 0,
+            'sharing_level' => Visibility::VIEW_NONE,
+            'allow_email' => false,
+        );
+        return new PartnerSettings($data);
+    }
+}
+// }}}
+
+// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
+?>
index 8d4ef1b..c1dd074 100644 (file)
@@ -36,6 +36,7 @@ abstract class ProfileField
         Profile::FETCH_MENTOR_COUNTRY => 'ProfileMentoringCountries',
         Profile::FETCH_JOB_TERMS      => 'ProfileJobTerms',
         Profile::FETCH_MENTOR_TERMS   => 'ProfileMentoringTerms',
+        Profile::FETCH_PARTNER        => 'ProfilePartnerSharing',
     );
 
     /** The profile to which this field belongs
@@ -50,12 +51,12 @@ abstract class ProfileField
      *
      * MUST be reimplemented for each kind of ProfileField.
      */
-    public static function fetchData(array $pids, ProfileVisibility $visibility)
+    public static function fetchData(array $pids, Visibility $visibility)
     {
         return PlIteratorUtils::emptyIterator();
     }
 
-    public static function buildForPID($cls, $pid, ProfileVisibility $visibility)
+    public static function buildForPID($cls, $pid, Visibility $visibility)
     {
         $res = self::buildFromPIDs($cls, array($pid), $visibility);
         return array_pop($res);
@@ -67,7 +68,7 @@ abstract class ProfileField
      * @param $visibility An array of allowed visibility contexts
      * @return An array of $pid => ProfileField
      */
-    public static function buildFromPIDs($cls, array $pids, ProfileVisibility $visibility)
+    public static function buildFromPIDs($cls, array $pids, Visibility $visibility)
     {
         $it = new ProfileFieldIterator($cls, $pids, $visibility);
         $res = array();
@@ -77,7 +78,7 @@ abstract class ProfileField
         return $res;
     }
 
-    public static function getForPID($cls, $pid, ProfileVisibility $visibility)
+    public static function getForPID($cls, $pid, Visibility $visibility)
     {
         $it = new ProfileFieldIterator($cls, array($pid), $visibility);
         return $it->next();
@@ -91,7 +92,7 @@ class ProfileFieldIterator implements PlIterator
     private $data;
     private $cls;
 
-    public function __construct($cls, array $pids, ProfileVisibility $visibility)
+    public function __construct($cls, array $pids, Visibility $visibility)
     {
         if (is_numeric($cls) && isset(ProfileField::$fields[$cls])) {
             $cls = ProfileField::$fields[$cls];
@@ -192,6 +193,9 @@ class Job
     public $user_site;
     public $user_email;
 
+    public $pub;
+    public $email_pub;
+
     /** Fields are:
      * pid, id, company_id, description, url, email
      */
@@ -333,7 +337,7 @@ class ProfileEducation extends ProfileField
         return $educations;
     }
 
-    public static function fetchData(array $pids, ProfileVisibility $visibility)
+    public static function fetchData(array $pids, Visibility $visibility)
     {
         $data = XDB::iterator('SELECT  pe.id, pe.pid,
                                        pe.entry_year, pe.grad_year, pe.program, pe.flags,
@@ -368,16 +372,17 @@ class ProfileMedals extends ProfileField
         }
     }
 
-    public static function fetchData(array $pids, ProfileVisibility $visibility)
+    public static function fetchData(array $pids, Visibility $visibility)
     {
         $data = XDB::iterator('SELECT  pm.pid, pm.mid, pm.gid, pme.text, pme.img, pmge.text AS grade
                                  FROM  profile_medals AS pm
                             LEFT JOIN  profiles AS p ON (pm.pid = p.pid)
                             LEFT JOIN  profile_medal_enum AS pme ON (pme.id = pm.mid)
                             LEFT JOIN  profile_medal_grade_enum AS pmge ON (pmge.mid = pm.mid AND pmge.gid = pm.gid)
-                                WHERE  pm.pid IN {?} AND p.medals_pub IN {?}
+                            LEFT JOIN  profile_visibility_enum AS pve ON (pve.access_level = {?})
+                                WHERE  pm.pid IN {?} AND pve.best_display_level + 0 <= p.medals_pub + 0
                              ORDER BY  ' . XDB::formatCustomOrder('pm.pid', $pids),
-                                $pids, $visibility->levels());
+                                $visibility->level(), $pids);
 
         return PlIteratorUtils::subIterator($data, PlIteratorUtils::arrayValueCallback('pid'));
     }
@@ -397,15 +402,16 @@ class ProfileNetworking extends ProfileField
         }
     }
 
-    public static function fetchData(array $pids, ProfileVisibility $visibility)
+    public static function fetchData(array $pids, Visibility $visibility)
     {
         $data = XDB::iterator('SELECT  pid, id, address, pne.nwid, pne.network_type, pne.link, pne.name
                                  FROM  profile_networking AS pn
                             LEFT JOIN  profile_networking_enum AS pne USING(nwid)
-                                WHERE  pid IN {?} AND pub IN {?}
+                            LEFT JOIN  profile_visibility_enum AS pve ON (pve.access_level = {?})
+                                WHERE  pn.pid IN {?} AND pve.best_display_level + 0 <= pn.pub + 0
                              ORDER BY  ' . XDB::formatCustomOrder('pid', $pids) . ',
                                        pn.nwid, id',
-                               $pids, $visibility->levels());
+                               $visibility->level(), $pids);
 
         return PlIteratorUtils::subIterator($data, PlIteratorUtils::arrayValueCallback('pid'));
     }
@@ -453,7 +459,7 @@ class ProfileCorps extends ProfileField
         }
     }
 
-    public static function fetchData(array $pids, ProfileVisibility $visibility)
+    public static function fetchData(array $pids, Visibility $visibility)
     {
         $data = XDB::iterator('SELECT  pc.pid, pc.original_corpsid AS original, pc.current_corpsid AS current,
                                        pceo.name AS original_name, pceo.abbreviation AS original_abbrev,
@@ -466,9 +472,10 @@ class ProfileCorps extends ProfileField
                             LEFT JOIN  profile_corps_enum AS pceo ON (pceo.id = pc.original_corpsid)
                             LEFT JOIN  profile_corps_enum AS pcec ON (pcec.id = pc.current_corpsid)
                             LEFT JOIN  profile_corps_rank_enum AS pcrec ON (pcrec.id = pc.rankid)
-                                WHERE  pc.pid IN {?} AND pc.corps_pub IN {?} AND pceo.id != 1
+                            LEFT JOIN  profile_visibility_enum AS pve ON (pve.access_level = {?})
+                                WHERE  pc.pid IN {?} AND pve.best_display_level + 0 <= pc.corps_pub + 0 AND pceo.id != 1
                              ORDER BY  ' . XDB::formatCustomOrder('pid', $pids),
-                                $pids, $visibility->levels());
+                                $visibility->level(), $pids);
 
         return $data;
     }
@@ -487,7 +494,7 @@ class ProfileMentoringCountries extends ProfileField
         }
     }
 
-    public static function fetchData(array $pids, ProfileVisibility $visibility)
+    public static function fetchData(array $pids, Visibility $visibility)
     {
         $data = XDB::iterator('SELECT  pmc.pid, pmc.country AS id, gc.country AS name
                                  FROM  profile_mentor_country AS pmc
@@ -533,9 +540,9 @@ class ProfileAddresses extends ProfileField
         return $addresses;
     }
 
-    public static function fetchData(array $pids, ProfileVisibility $visibility)
+    public static function fetchData(array $pids, Visibility $visibility)
     {
-        $it = Address::iterate($pids, array(), array(), $visibility->levels());
+        $it = Address::iterate($pids, array(), array(), $visibility);
         return PlIteratorUtils::subIterator($it->value(), PlIteratorUtils::arrayValueCallback('pid'));
     }
 
@@ -584,9 +591,9 @@ class ProfilePhones extends ProfileField
         return $phones;
     }
 
-    public static function fetchData(array $pids, ProfileVisibility $visibility)
+    public static function fetchData(array $pids, Visibility $visibility)
     {
-        $it = Phone::iterate($pids, array(), array(), $visibility->levels());
+        $it = Phone::iterate($pids, array(), array(), $visibility);
         return PlIteratorUtils::subIterator($it->value(), PlIteratorUtils::arrayValueCallback('pid'));
     }
 }
@@ -604,15 +611,15 @@ class ProfileJobs extends ProfileField
         }
     }
 
-    public static function fetchData(array $pids, ProfileVisibility $visibility)
+    public static function fetchData(array $pids, Visibility $visibility)
     {
         CompanyList::preload($pids);
-        $data = XDB::iterator('SELECT  id, pid, description, url as user_site, jobid,
-                                       IF(email_pub IN {?}, email, NULL) AS user_email
+        $data = XDB::iterator('SELECT  id, pid, description, url as user_site, jobid, pub,                                       IF(pve.best_display_level + 0 <= email_pub + 0, email, NULL) AS user_email, email_pub
                                  FROM  profile_job
-                                WHERE  pid IN {?} AND pub IN {?}
+                            LEFT JOIN  profile_visibility_enum AS pve ON (pve.access_level = {?})
+                                WHERE  pid IN {?} AND pve.best_display_level + 0 <= pub + 0
                              ORDER BY  ' . XDB::formatCustomOrder('pid', $pids) . ', id',
-                                 $visibility->levels(), $pids, $visibility->levels());
+                                 $visibility->level(), $pids);
         return PlIteratorUtils::subIterator($data, PlIteratorUtils::arrayValueCallback('pid'));
     }
 
@@ -686,15 +693,16 @@ class ProfileJobTerms extends ProfileField
         return $this->jobterms;
     }
 
-    public static function fetchData(array $pids, ProfileVisibility $visibility)
+    public static function fetchData(array $pids, Visibility $visibility)
     {
         $data = XDB::iterator('SELECT  jt.jtid, jte.full_name, jt.pid, jt.jid
                                  FROM  profile_job_term AS jt
                            INNER JOIN  profile_job AS j ON (jt.pid = j.pid AND jt.jid = j.id)
                             LEFT JOIN  profile_job_term_enum AS jte USING(jtid)
-                                WHERE  jt.pid IN {?} AND j.pub IN {?}
+                            LEFT JOIN  profile_visibility_enum AS pve ON (pve.access_level = {?})
+                                WHERE  jt.pid IN {?} AND pve.best_display_level + 0 <= j.pub + 0
                              ORDER BY  ' . XDB::formatCustomOrder('jt.pid', $pids),
-                                 $pids, $visibility->levels());
+                                 $visibility->level(), $pids);
         return PlIteratorUtils::subIterator($data, PlIteratorUtils::arrayValueCallback('pid'));
     }
 }
@@ -702,7 +710,7 @@ class ProfileJobTerms extends ProfileField
 // {{{ class ProfileMentoringTerms                    [ Field ]
 class ProfileMentoringTerms extends ProfileJobTerms
 {
-    public static function fetchData(array $pids, ProfileVisibility $visibility)
+    public static function fetchData(array $pids, Visibility $visibility)
     {
         $data = XDB::iterator('SELECT  mt.jtid, jte.full_name, mt.pid
                                  FROM  profile_mentor_term AS mt
@@ -714,6 +722,45 @@ class ProfileMentoringTerms extends ProfileJobTerms
     }
 }
 // }}}
+// {{{ class ProfilePartnerSharing                    [ Field ]
+class ProfilePartnerSharing extends ProfileField
+{
+    public function __construct(PlInnerSubIterator $it)
+    {
+        require_once 'partnersharing.inc.php';
+
+        $this->pid = $it->value();
+        while ($partner_settings = $it->next()) {
+            $settings = new PartnerSettings($partner_settings);
+            $this->partners_settings[$settings->partner->id] = $settings;
+        }
+    }
+
+    public static function fetchData(array $pids, Visibility $visibility)
+    {
+        $data = XDB::iterator('SELECT  ppss.pid, ppss.exposed_uid, ppss.sharing_level,
+                                       ppss.allow_email, ppss.partner_id,
+                                       ppse.shortname AS partner_shortname,
+                                       ppse.name AS partner_name,
+                                       ppse.url AS partner_url
+                                 FROM  profile_partnersharing_settings AS ppss
+                            LEFT JOIN  profile_partnersharing_enum AS ppse ON (ppss.partner_id = ppse.id)
+                                WHERE  ppss.pid IN {?}
+                             ORDER BY  ' . XDB::formatCustomOrder('ppss.pid', $pids),
+                                 $pids);
+        return PlIteratorUtils::subIterator($data, PlIteratorUtils::arrayValueCallback('pid'));
+    }
+
+    public function get($partner_id)
+    {
+        if (isset($this->partners_settings[$partner_id])) {
+            return $this->partners_settings[$partner_id];
+        } else {
+            return PartnerSettings::getEmpty($partner_id);
+        }
+    }
+}
+// }}}
 // {{{ class CompanyList
 class CompanyList
 {
@@ -753,7 +800,7 @@ class CompanyList
 
         // Add phones to hq
         if (count($newcompanies)) {
-            $it = Phone::iterate(array(), array(Phone::LINK_COMPANY), $newcompanies);
+            $it = Phone::iterate(array(), array(Phone::LINK_COMPANY), $newcompanies, Visibility::defaultForRead());
             while ($phone = $it->next()) {
                 self::$companies[$phone->link_id]->setPhone($phone);
             }
@@ -775,6 +822,7 @@ class CompanyList
         return null;
     }
 }
+// }}}
 
 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
 ?>
index 1a7e7d7..62b5a02 100644 (file)
@@ -424,7 +424,7 @@ class AddressesView implements PlView
     public function apply(PlPage $page)
     {
         $pids = $this->set->getIds(new PlLimit());
-        $visibility = new ProfileVisibility(ProfileVisibility::VIS_AX);
+        $visibility = Visibility::defaultForRead(Visibility::VIEW_AX);
         pl_cached_content_headers('text/x-csv', 1);
 
         $csv = fopen('php://output', 'w');
index b31bc9b..91c4148 100644 (file)
@@ -104,7 +104,7 @@ class AddressReq extends ProfileValidate
     protected function _mail_body($isok)
     {
         if ($isok) {
-            return "  Nous avons réussit à mieux localiser l'adresse.";
+            return "  Nous avons réussi à mieux localiser l'adresse suivante :\n{$this->given_text}.";
         } 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.";
         }
index 4dabe8d..5109fa9 100644 (file)
@@ -99,6 +99,12 @@ class AliasReq extends Validate
                                      $this->alias, $this->user->id(), Platal::globals()->mail->alias_dom);
         }
 
+        if ($success) {
+            // Update the local User object, to pick up the new bestalias.
+            fix_bestalias($this->user);
+            $this->user = User::getSilentWithUID($this->user->id());
+        }
+
         return $success;
     }
 }
index de0b49d..b544ef1 100644 (file)
@@ -30,7 +30,7 @@ class VCard extends PlVCard
     public function __construct($photos = true, $freetext = null)
     {
         PlVCard::$folding = false;
-        $this->visibility = new ProfileVisibility(ProfileVisibility::VIS_PRIVATE);
+        $this->visibility = Visibility::defaultForRead(Visibility::VIEW_PRIVATE);
         $this->freetext = $freetext;
         $this->photos   = $photos;
     }
index a2d1c22..a57e421 100644 (file)
@@ -26,7 +26,6 @@ define('PL_SESSION_CLASS', 'XnetSession');
 define('PL_PAGE_CLASS', 'XnetPage');
 
 require_once dirname(dirname(__FILE__)) . '/core/include/platal.inc.php';
-require_once 'security.inc.php';
 require_once 'common.inc.php';
 
 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
index edbc2cd..11630cc 100644 (file)
@@ -27,7 +27,7 @@ class ApiModule extends PlModule
             // TODO(vzanotti): Extend the plat/al engine to support placeholders
             // in handler urls, for instance "api/1/user/%forlife/isRegistered".
             'api/1/user'   => $this->make_api_hook('user',   AUTH_COOKIE, 'api_user_readonly'),
-            'api/1/search' => $this->make_api_hook('search', AUTH_COOKIE),
+            'api/1/search' => $this->make_api_hook('search', AUTH_COOKIE, 'user'),
         );
     }
 
index f302f1b..21f85bf 100644 (file)
@@ -29,9 +29,9 @@ class AuthModule extends PLModule
 
             'webservices/manageurs.php'     => $this->make_hook('manageurs',          AUTH_PUBLIC, 'user', NO_HTTPS),
 
-            'auth-redirect.php'             => $this->make_hook('redirect',           AUTH_COOKIE),
-            'auth-groupex.php'              => $this->make_hook('groupex_old',        AUTH_COOKIE),
-            'auth-groupex'                  => $this->make_hook('groupex',            AUTH_COOKIE),
+            'auth-redirect.php'             => $this->make_hook('redirect',           AUTH_COOKIE, 'user'),
+            'auth-groupex.php'              => $this->make_hook('groupex_old',        AUTH_COOKIE, 'user'),
+            'auth-groupex'                  => $this->make_hook('groupex',            AUTH_PUBLIC),
             'admin/auth-groupes-x'          => $this->make_hook('admin_authgroupesx', AUTH_MDP,    'admin'),
         );
     }
@@ -109,24 +109,48 @@ class AuthModule extends PLModule
         return $this->handler_groupex($page, 'iso-8859-1');
     }
 
+    /** Handles the 'auth-groupe-x' authentication.
+     * Expects the following GET parameters:
+     * - pass: the 'password' for the authentication
+     * - challenge: the authentication challenge
+     * - url: the return URL
+     * - session: the remote PHP session ID
+     */
     function handler_groupex($page, $charset = 'utf8')
     {
+        if (!S::logged()) {
+            $page->assign('referer', true);
+            $page->setTitle('Authentification');
+            $page->setDefaultSkin('group_login');
+
+            if (Get::has('group')) {
+                $res = XDB::query('SELECT  nom
+                                     FROM  groups
+                                    WHERE  diminutif = {?}', Get::s('group'));
+                $page->assign('group', $res->fetchOneCell());
+            } else {
+                $page->assign('group', null);
+            }
+            return PL_DO_AUTH;
+        }
+
         $this->load('auth.inc.php');
-        $page->assign('referer', true);
 
-        $gpex_pass = $_GET["pass"];
-        $gpex_url  = urldecode($_GET["url"]);
-        if (strpos($gpex_url, '?') === false) {
-            $gpex_url .= "?PHPSESSID=" . $_GET["session"];
-        } else {
-            $gpex_url .= "&PHPSESSID=" . $_GET["session"];
+        $gpex_pass = Get::s('pass');
+        $gpex_url  = urldecode(Get::s('url'));
+        if (Get::has('session')) {
+            if (strpos($gpex_url, '?') === false) {
+                $gpex_url .= "?PHPSESSID=" . Get::s('session');
+            } else {
+                $gpex_url .= "&PHPSESSID=" . Get::s('session');
+            }
         }
 
         // Normalize the return URL.
         if (!preg_match("/^(http|https):\/\/.*/",$gpex_url)) {
             $gpex_url = "http://$gpex_url";
         }
-        $gpex_challenge = $_GET["challenge"];
+        $gpex_challenge = Get::s('challenge');
 
         // Update the last login information (unless the user is in SUID).
         $uid = S::i('uid');
@@ -135,9 +159,23 @@ class AuthModule extends PLModule
             S::logger($uid)->log('connexion_auth_ext', $platal->path.' '.urldecode($_GET['url']));
         }
 
+        if (Get::has('group')) {
+            $req_group_id = XDB::fetchOneCell('SELECT  asso_id
+                                                 FROM  groups
+                                                WHERE  diminutif = {?}',
+                                              Get::s('group'));
+        } else {
+            $req_group_id = null;
+        }
+
         // Iterate over the auth token to find which one did sign the request.
-        $res = XDB::iterRow('SELECT privkey, name, datafields, returnurls FROM group_auth');
-        while (list($privkey,$name,$datafields,$returnurls) = $res->next()) {
+        $res = XDB::iterRow(
+            'SELECT  ga.privkey, ga.name, ga.datafields, ga.returnurls,
+                     ga.group_id, ga.flags, g.nom
+               FROM  group_auth AS ga
+          LEFT JOIN  groups AS g ON (g.id = ga.group_id)');
+
+        while (list($privkey, $name, $datafields, $returnurls, $group_id, $group_flags, $group_name) = $res->next()) {
             if (md5($gpex_challenge.$privkey) == $gpex_pass) {
                 $returnurls = trim($returnurls);
                 // We check that the return url matches a per-key regexp to prevent
@@ -149,6 +187,38 @@ class AuthModule extends PLModule
                                      SET  last_used = DATE(NOW())
                                    WHERE  name = {?}',
                                  $name);
+
+                    // Two conditions control access to the return URL:
+                    // - If that URL is attached to a group:
+                    //   - If the user belongs to the group, OK
+                    //   - If the user is 'xnet' and doesn't belong, NOK
+                    //   - If the user is not 'xnet' and the group is not 'strict', OK
+                    //   - If the user is not 'xnet' and the group is 'strict', NOK
+                    // - Otherwise, all but 'xnet' accounts may access the URL.
+                    $user_is_xnet = S::user()->type == 'xnet';
+                    $group_flags = new PlFlagSet($group_flags);
+
+                    // If this key is not attached to a group, but a group was
+                    // requested (e.g query from wiki / blogs / ...), use the
+                    // requested group_id.
+                    if (!$group_id && $req_group_id) {
+                        $group_id = $req_group_id;
+                    }
+
+                    if ($group_id) {
+                        // Check group permissions
+                        $is_member = XDB::fetchOneCell('SELECT  COUNT(*)
+                                                          FROM  group_members
+                                                         WHERE  uid = {?} AND asso_id = {?}',
+                                                         S::user()->id(), $group_id);
+                        if (!$is_member && ($user_is_xnet || $group_flags->hasFlag('group_only'))) {
+                            $page->kill("Le site demandé est réservé aux membres du groupe $group_name.");
+                        }
+
+                    } else if ($user_is_xnet && !$group_flags->hasFlag('allow_xnet')) {
+                        $page->kill("Le site demandé est réservé aux polytechniciens.");
+                    }
+
                     http_redirect($returl);
                 } else if (S::admin()) {
                     $page->kill("La requête d'authentification a échouée (url de retour invalide).");
index a9f4596..d6a63be 100644 (file)
@@ -26,16 +26,16 @@ class AXLetterModule extends NewsletterModule
     function handlers()
     {
         return array(
-            'ax'                   => $this->make_hook('nl',              AUTH_COOKIE),
+            'ax'                   => $this->make_hook('nl',              AUTH_COOKIE, 'user'),
             'ax/out'               => $this->make_hook('out',             AUTH_PUBLIC),
-            'ax/show'              => $this->make_hook('nl_show',         AUTH_COOKIE),
-            'ax/search'            => $this->make_hook('nl_search',       AUTH_COOKIE),
-            'ax/admin'             => $this->make_hook('admin_nl',        AUTH_MDP),
-            'ax/admin/edit'        => $this->make_hook('admin_nl_edit',   AUTH_MDP),
-            'ax/admin/edit/valid'  => $this->make_hook('admin_nl_valid',  AUTH_MDP),
-            'ax/admin/edit/cancel' => $this->make_hook('admin_nl_cancel', AUTH_MDP),
-            'ax/admin/edit/delete' => $this->make_hook('admin_nl_delete', AUTH_MDP),
-            'ax/admin/categories'  => $this->make_hook('admin_nl_cat',    AUTH_MDP),
+            'ax/show'              => $this->make_hook('nl_show',         AUTH_COOKIE, 'user'),
+            'ax/search'            => $this->make_hook('nl_search',       AUTH_COOKIE, 'user'),
+            'ax/admin'             => $this->make_hook('admin_nl',        AUTH_MDP,    'user'),
+            'ax/admin/edit'        => $this->make_hook('admin_nl_edit',   AUTH_MDP,    'user'),
+            'ax/admin/edit/valid'  => $this->make_hook('admin_nl_valid',  AUTH_MDP,    'user'),
+            'ax/admin/edit/cancel' => $this->make_hook('admin_nl_cancel', AUTH_MDP,    'user'),
+            'ax/admin/edit/delete' => $this->make_hook('admin_nl_delete', AUTH_MDP,    'user'),
+            'ax/admin/categories'  => $this->make_hook('admin_nl_cat',    AUTH_MDP,    'user'),
         );
     }
 
@@ -45,14 +45,14 @@ class AXLetterModule extends NewsletterModule
         return NewsLetter::forGroup(NewsLetter::GROUP_AX);
     }
 
-    function handler_out($page, $hash = null)
+    function handler_out($page, $hash = null, $issue_id = null)
     {
         if (!$hash) {
             if (!S::logged()) {
                 return PL_DO_AUTH;
             }
         }
-        return $this->handler_nl($page, 'out', $hash);
+        return $this->handler_nl($page, 'out', $hash, $issue_id);
     }
 }
 
index 006aa83..2c6a129 100644 (file)
@@ -26,8 +26,8 @@ class DeltaTenModule extends PLModule
     function handlers()
     {
         return array(
-            'deltaten/search'   => $this->make_hook('index', AUTH_COOKIE),
-            'deltaten'          => $this->make_hook('index', AUTH_COOKIE),
+            'deltaten/search'   => $this->make_hook('index', AUTH_COOKIE, 'user'),
+            'deltaten'          => $this->make_hook('index', AUTH_COOKIE, 'user'),
         );
     }
 
index 7648515..e5049c5 100644 (file)
@@ -27,10 +27,10 @@ class EmailModule extends PLModule
             'emails'                  => $this->make_hook('emails',      AUTH_COOKIE, 'mail'),
             'emails/alias'            => $this->make_hook('alias',       AUTH_MDP,    'mail'),
             'emails/antispam'         => $this->make_hook('antispam',    AUTH_MDP,    'mail'),
-            'emails/broken'           => $this->make_hook('broken',      AUTH_COOKIE),
+            'emails/broken'           => $this->make_hook('broken',      AUTH_COOKIE, 'user'),
             'emails/redirect'         => $this->make_hook('redirect',    AUTH_MDP,    'mail'),
             'emails/send'             => $this->make_hook('send',        AUTH_MDP,    'mail'),
-            'emails/antispam/submit'  => $this->make_hook('submit',      AUTH_COOKIE),
+            'emails/antispam/submit'  => $this->make_hook('submit',      AUTH_COOKIE, 'user'),
             'emails/test'             => $this->make_hook('test',        AUTH_COOKIE, 'mail', NO_AUTH),
 
             'emails/rewrite/in'       => $this->make_hook('rewrite_in',  AUTH_PUBLIC),
@@ -67,9 +67,12 @@ class EmailModule extends PLModule
                            WHERE  uid = {?}", $user->id());
             // Then gives the bestalias flag to the given email.
             list($email, $domain) = explode('@', $email);
-            XDB::execute("UPDATE  email_source_account
+            XDB::execute("UPDATE  email_source_account  AS s
+                      INNER JOIN  email_virtual_domains AS m ON (m.id = s.domain)
+                      INNER JOIN  email_virtual_domains AS d ON (d.aliasing = m.id)
                              SET  flags = CONCAT_WS(',', IF(flags = '', NULL, flags), 'bestalias')
-                           WHERE  uid = {?} AND email = {?}", $user->id(), $email);
+                           WHERE  s.uid = {?} AND s.email = {?} AND d.name = {?}",
+                         $user->id(), $email, $domain);
             XDB::execute('UPDATE  accounts              AS a
                       INNER JOIN  email_virtual_domains AS d ON (d.name = {?})
                       INNER JOIN  email_virtual_domains AS m ON (d.aliasing = m.id)
index 0cb1c0f..b914893 100644 (file)
@@ -26,16 +26,16 @@ class EPLetterModule extends NewsletterModule
     function handlers()
     {
         return array(
-            'epletter'                   => $this->make_hook('nl',              AUTH_COOKIE),
+            'epletter'                   => $this->make_hook('nl',              AUTH_COOKIE, 'user'),
             'epletter/out'               => $this->make_hook('out',             AUTH_PUBLIC),
-            'epletter/show'              => $this->make_hook('nl_show',         AUTH_COOKIE),
-            'epletter/search'            => $this->make_hook('nl_search',       AUTH_COOKIE),
-            'epletter/admin'             => $this->make_hook('admin_nl',        AUTH_MDP),
-            'epletter/admin/edit'        => $this->make_hook('admin_nl_edit',   AUTH_MDP),
-            'epletter/admin/edit/valid'  => $this->make_hook('admin_nl_valid',  AUTH_MDP),
-            'epletter/admin/edit/cancel' => $this->make_hook('admin_nl_cancel', AUTH_MDP),
-            'epletter/admin/edit/delete' => $this->make_hook('admin_nl_delete', AUTH_MDP),
-            'epletter/admin/categories'  => $this->make_hook('admin_nl_cat',    AUTH_MDP),
+            'epletter/show'              => $this->make_hook('nl_show',         AUTH_COOKIE, 'user'),
+            'epletter/search'            => $this->make_hook('nl_search',       AUTH_COOKIE, 'user'),
+            'epletter/admin'             => $this->make_hook('admin_nl',        AUTH_MDP,    'user'),
+            'epletter/admin/edit'        => $this->make_hook('admin_nl_edit',   AUTH_MDP,    'user'),
+            'epletter/admin/edit/valid'  => $this->make_hook('admin_nl_valid',  AUTH_MDP,    'user'),
+            'epletter/admin/edit/cancel' => $this->make_hook('admin_nl_cancel', AUTH_MDP,    'user'),
+            'epletter/admin/edit/delete' => $this->make_hook('admin_nl_delete', AUTH_MDP,    'user'),
+            'epletter/admin/categories'  => $this->make_hook('admin_nl_cat',    AUTH_MDP,    'user'),
         );
     }
 
@@ -45,14 +45,14 @@ class EPLetterModule extends NewsletterModule
         return NewsLetter::forGroup(NewsLetter::GROUP_EP);
     }
 
-    function handler_out($page, $hash = null)
+    function handler_out($page, $hash = null, $issue_id = null)
     {
         if (!$hash) {
             if (!S::logged()) {
                 return PL_DO_AUTH;
             }
         }
-        return $this->handler_nl($page, 'out', $hash);
+        return $this->handler_nl($page, 'out', $hash, $issue_id);
     }
 }
 
index 3c4df28..b1f6482 100644 (file)
@@ -24,12 +24,12 @@ class EventsModule extends PLModule
     function handlers()
     {
         return array(
-            'events'         => $this->make_hook('ev',           AUTH_COOKIE),
+            'events'         => $this->make_hook('ev',           AUTH_COOKIE, 'user'),
             'events/preview' => $this->make_hook('preview',      AUTH_PUBLIC, 'user', NO_AUTH),
             'events/photo'   => $this->make_hook('photo',        AUTH_PUBLIC),
-            'events/submit'  => $this->make_hook('ev_submit',    AUTH_MDP),
+            'events/submit'  => $this->make_hook('ev_submit',    AUTH_MDP,    'user'),
             'admin/events'   => $this->make_hook('admin_events', AUTH_MDP,    'admin'),
-            'rss'            => $this->make_token_hook('rss',    AUTH_COOKIE),
+            'rss'            => $this->make_token_hook('rss',    AUTH_COOKIE, 'user'),
 
             'ajax/tips'      => $this->make_hook('tips',         AUTH_COOKIE, 'user', NO_AUTH),
             'admin/tips'     => $this->make_hook('admin_tips',   AUTH_MDP,    'admin'),
index 340dfd4..fafbe49 100644 (file)
@@ -24,8 +24,8 @@ class GeolocModule extends PLModule
     function handlers()
     {
         return array(
-            'map'     => $this->make_hook('map', AUTH_COOKIE),
-            'map_url' => $this->make_hook('map_url', AUTH_COOKIE)
+            'map'     => $this->make_hook('map',     AUTH_COOKIE, 'user'),
+            'map_url' => $this->make_hook('map_url', AUTH_COOKIE, 'user')
         );
     }
 
index 59f0bd0..688b0b0 100644 (file)
@@ -26,23 +26,23 @@ class ListsModule extends PLModule
     function handlers()
     {
         return array(
-            'lists'              => $this->make_hook('lists',     AUTH_MDP),
+            'lists'              => $this->make_hook('lists',     AUTH_MDP,    'user'),
             'lists/ajax'         => $this->make_hook('ajax',      AUTH_MDP,    'user', NO_AUTH),
             'lists/create'       => $this->make_hook('create',    AUTH_MDP,    'lists'),
 
-            'lists/members'      => $this->make_hook('members',   AUTH_COOKIE),
-            'lists/csv'          => $this->make_hook('csv',       AUTH_COOKIE),
-            'lists/annu'         => $this->make_hook('annu',      AUTH_COOKIE),
-            'lists/archives'     => $this->make_hook('archives',  AUTH_COOKIE),
+            'lists/members'      => $this->make_hook('members',   AUTH_COOKIE, 'user'),
+            'lists/csv'          => $this->make_hook('csv',       AUTH_COOKIE, 'user'),
+            'lists/annu'         => $this->make_hook('annu',      AUTH_COOKIE, 'user'),
+            'lists/archives'     => $this->make_hook('archives',  AUTH_COOKIE, 'user'),
             'lists/archives/rss' => $this->make_hook('rss',       AUTH_PUBLIC, 'user', NO_HTTPS),
 
-            'lists/moderate'     => $this->make_hook('moderate',  AUTH_MDP),
-            'lists/admin'        => $this->make_hook('admin',     AUTH_MDP),
-            'lists/options'      => $this->make_hook('options',   AUTH_MDP),
-            'lists/delete'       => $this->make_hook('delete',    AUTH_MDP),
+            'lists/moderate'     => $this->make_hook('moderate',  AUTH_MDP,    'user'),
+            'lists/admin'        => $this->make_hook('admin',     AUTH_MDP,    'user'),
+            'lists/options'      => $this->make_hook('options',   AUTH_MDP,    'user'),
+            'lists/delete'       => $this->make_hook('delete',    AUTH_MDP,    'user'),
 
-            'lists/soptions'     => $this->make_hook('soptions',  AUTH_MDP),
-            'lists/check'        => $this->make_hook('check',     AUTH_MDP),
+            'lists/soptions'     => $this->make_hook('soptions',  AUTH_MDP,    'user'),
+            'lists/check'        => $this->make_hook('check',     AUTH_MDP,    'user'),
             'admin/lists'        => $this->make_hook('admin_all', AUTH_MDP,    'admin'),
             'admin/aliases'      => $this->make_hook('aaliases',  AUTH_MDP,    'admin')
         );
index 33f131a..5086e96 100644 (file)
@@ -24,15 +24,15 @@ class MarketingModule extends PLModule
     function handlers()
     {
         return array(
-            'marketing'            => $this->make_hook('marketing',  AUTH_MDP, 'admin'),
-            'marketing/promo'      => $this->make_hook('promo',      AUTH_MDP, 'admin'),
-            'marketing/relance'    => $this->make_hook('relance',    AUTH_MDP, 'admin'),
-            'marketing/this_week'  => $this->make_hook('week',       AUTH_MDP, 'admin'),
-            'marketing/volontaire' => $this->make_hook('volontaire', AUTH_MDP, 'admin'),
-
-            'marketing/private'    => $this->make_hook('private',    AUTH_MDP, 'admin'),
-            'marketing/public'     => $this->make_hook('public',     AUTH_COOKIE),
-            'marketing/broken'     => $this->make_hook('broken',     AUTH_COOKIE),
+            'marketing'            => $this->make_hook('marketing',  AUTH_MDP,    'admin'),
+            'marketing/promo'      => $this->make_hook('promo',      AUTH_MDP,    'admin'),
+            'marketing/relance'    => $this->make_hook('relance',    AUTH_MDP,    'admin'),
+            'marketing/this_week'  => $this->make_hook('week',       AUTH_MDP,    'admin'),
+            'marketing/volontaire' => $this->make_hook('volontaire', AUTH_MDP,    'admin'),
+
+            'marketing/private'    => $this->make_hook('private',    AUTH_MDP,    'admin'),
+            'marketing/public'     => $this->make_hook('public',     AUTH_COOKIE, 'user'),
+            'marketing/broken'     => $this->make_hook('broken',     AUTH_COOKIE, 'user'),
         );
     }
 
index 149e27b..6603bc8 100644 (file)
@@ -24,16 +24,16 @@ class NewsletterModule extends PLModule
     function handlers()
     {
         return array(
-            'nl'                           => $this->make_hook('nl',              AUTH_COOKIE),
-            'nl/show'                      => $this->make_hook('nl_show',         AUTH_COOKIE),
-            'nl/search'                    => $this->make_hook('nl_search',       AUTH_COOKIE),
-            'nl/submit'                    => $this->make_hook('nl_submit',       AUTH_MDP),
-            'nl/remaining'                 => $this->make_hook('nl_remaining',    AUTH_MDP),
-            'admin/nls'                    => $this->make_hook('admin_nl_groups', AUTH_MDP, 'admin'),
-            'admin/newsletter'             => $this->make_hook('admin_nl',        AUTH_MDP, 'admin'),
-            'admin/newsletter/categories'  => $this->make_hook('admin_nl_cat',    AUTH_MDP, 'admin'),
-            'admin/newsletter/edit'        => $this->make_hook('admin_nl_edit',   AUTH_MDP, 'admin'),
-            'admin/newsletter/edit/delete' => $this->make_hook('admin_nl_delete', AUTH_MDP, 'admin'),
+            'nl'                           => $this->make_hook('nl',              AUTH_COOKIE, 'user'),
+            'nl/show'                      => $this->make_hook('nl_show',         AUTH_COOKIE, 'user'),
+            'nl/search'                    => $this->make_hook('nl_search',       AUTH_COOKIE, 'user'),
+            'nl/submit'                    => $this->make_hook('nl_submit',       AUTH_MDP,    'user'),
+            'nl/remaining'                 => $this->make_hook('nl_remaining',    AUTH_MDP,    'user'),
+            'admin/nls'                    => $this->make_hook('admin_nl_groups', AUTH_MDP,    'admin'),
+            'admin/newsletter'             => $this->make_hook('admin_nl',        AUTH_MDP,    'admin'),
+            'admin/newsletter/categories'  => $this->make_hook('admin_nl_cat',    AUTH_MDP,    'admin'),
+            'admin/newsletter/edit'        => $this->make_hook('admin_nl_edit',   AUTH_MDP,    'admin'),
+            'admin/newsletter/edit/delete' => $this->make_hook('admin_nl_delete', AUTH_MDP,    'admin'),
             // Automatic mailing is disabled for X.org NL
 //            'admin/newsletter/edit/cancel' => $this->make_hook('cancel', AUTH_MDP, 'admin'),
 //            'admin/newsletter/edit/valid'  => $this->make_hook('valid',  AUTH_MDP, 'admin'),
@@ -55,16 +55,26 @@ class NewsletterModule extends PLModule
             return PL_NOT_FOUND;
         }
 
-        $page->changeTpl('newsletter/index.tpl');
-        $page->setTitle('Lettres mensuelles');
-
         $hash = ($hash == 'nohash') ? null : $hash;
         switch ($action) {
-          case 'out': $nl->unsubscribe($issue_id, $hash, $hash != null); break;
+          case 'out':
+            $success = $nl->unsubscribe($issue_id, $hash, $hash != null);
+            if (!is_null($hash)) {
+                if ($success) {
+                    $page->trigSuccess('La désinscription a été effectuée avec succès.');
+                } else {
+                    $page->trigError("La désinscription n'a été pas pu être effectuée.");
+                }
+                return;
+            }
+            break;
           case 'in':  $nl->subscribe(); break;
           default: ;
         }
 
+        $page->changeTpl('newsletter/index.tpl');
+        $page->setTitle('Lettres mensuelles');
+
         $page->assign_by_ref('nl', $nl);
         $page->assign('nls', $nl->subscriptionState());
         $page->assign('nl_list', $nl->listSentIssues(true));
index 49b08c1..fa4c1d5 100644 (file)
@@ -64,8 +64,8 @@ class OpenidModule extends PLModule
             'openid'               => $this->make_hook('openid',        AUTH_PUBLIC),
             'openid/melix'         => $this->make_hook('melix',         AUTH_PUBLIC),
             'openid/xrds'          => $this->make_hook('xrds',          AUTH_PUBLIC),
-            'openid/trust'         => $this->make_hook('trust',         AUTH_MDP),
-            'openid/trusted'       => $this->make_hook('trusted',       AUTH_MDP),
+            'openid/trust'         => $this->make_hook('trust',         AUTH_MDP, 'user'),
+            'openid/trusted'       => $this->make_hook('trusted',       AUTH_MDP, 'user'),
             'admin/openid/trusted' => $this->make_hook('admin_trusted', AUTH_MDP, 'admin'),
         );
     }
index 06efdb8..91771e6 100644 (file)
@@ -108,8 +108,8 @@ class PaymentModule extends PLModule
             'payment'                      => $this->make_hook('payment',          AUTH_MDP,    'payment'),
             'payment/cyber2_return'        => $this->make_hook('cyber2_return',    AUTH_PUBLIC, 'user', NO_HTTPS),
             'payment/paypal_return'        => $this->make_hook('paypal_return',    AUTH_PUBLIC, 'user', NO_HTTPS),
-            '%grp/paiement'                => $this->make_hook('xnet_payment',     AUTH_MDP),
-            '%grp/payment'                 => $this->make_hook('xnet_payment',     AUTH_MDP),
+            '%grp/paiement'                => $this->make_hook('xnet_payment',     AUTH_MDP,    'user'),
+            '%grp/payment'                 => $this->make_hook('xnet_payment',     AUTH_MDP,    'user'),
             '%grp/payment/csv'             => $this->make_hook('payment_csv',      AUTH_MDP,    'groupadmin'),
             '%grp/payment/cyber2_return'   => $this->make_hook('cyber2_return',    AUTH_PUBLIC, 'user', NO_HTTPS),
             '%grp/payment/paypal_return'   => $this->make_hook('paypal_return',    AUTH_PUBLIC, 'user', NO_HTTPS),
index 715ba78..c9fd3d9 100644 (file)
@@ -43,16 +43,18 @@ class PlatalModule extends PLModule
             'changelog'         => $this->make_hook('changelog', AUTH_PUBLIC),
 
             // Preferences thingies
-            'prefs'             => $this->make_hook('prefs',     AUTH_COOKIE),
-            'prefs/rss'         => $this->make_hook('prefs_rss', AUTH_COOKIE),
-            'prefs/webredirect' => $this->make_hook('webredir',  AUTH_MDP, 'mail'),
-            'prefs/skin'        => $this->make_hook('skin',      AUTH_COOKIE),
+            'prefs'             => $this->make_hook('prefs',     AUTH_COOKIE, 'user,groups'),
+            'prefs/rss'         => $this->make_hook('prefs_rss', AUTH_COOKIE, 'user'),
+            'prefs/webredirect' => $this->make_hook('webredir',  AUTH_MDP,    'mail'),
+            'prefs/skin'        => $this->make_hook('skin',      AUTH_COOKIE, 'user'),
 
             // password related thingies
-            'password'          => $this->make_hook('password',  AUTH_MDP),
+            'password'          => $this->make_hook('password',  AUTH_MDP,    'user,groups'),
             'tmpPWD'            => $this->make_hook('tmpPWD',    AUTH_PUBLIC),
-            'password/smtp'     => $this->make_hook('smtppass',  AUTH_MDP, 'mail'),
+            'password/smtp'     => $this->make_hook('smtppass',  AUTH_MDP,    'mail'),
             'recovery'          => $this->make_hook('recovery',  AUTH_PUBLIC),
+            'recovery/ext'      => $this->make_hook('recovery_ext', AUTH_PUBLIC),
+            'register/ext'      => $this->make_hook('register_ext', AUTH_PUBLIC),
             'exit'              => $this->make_hook('exit',      AUTH_PUBLIC),
             'review'            => $this->make_hook('review',    AUTH_PUBLIC),
             'deconnexion.php'   => $this->make_hook('exit',      AUTH_PUBLIC),
@@ -347,10 +349,43 @@ Adresse de secours : ' . $to));
         S::logger($user->id())->log('recovery', is_null($to) ? $inactives_to . ', ' . $user->bestEmail() : $to);
     }
 
+    function handler_recovery_ext($page)
+    {
+        $page->changeTpl('xnet/recovery.tpl');
+
+        if (!Post::has('login')) {
+            return;
+        }
+
+        $user = User::getSilent(Post::t('login'));
+        if (is_null($user)) {
+            $page->trigError('Le compte n\'existe pas.');
+            return;
+        }
+        if ($user->state != 'active') {
+            $page->trigError('Ton compte n\'est pas activé.');
+            return;
+        }
+
+        $page->assign('ok', true);
+
+        $hash = rand_url_id();
+        XDB::execute('INSERT INTO  account_lost_passwords (uid, created, certificat)
+                           VALUES  ({?}, NOW(), {?})',
+                     $user->id(), $hash);
+
+        $mymail = new PlMailer('platal/password_recovery_xnet.mail.tpl');
+        $mymail->addTo($user);
+        $mymail->assign('hash', $hash);
+        $mymail->assign('email', Post::t('login'));
+        $mymail->send();
+
+        S::logger($user->id())->log('recovery', $user->bestEmail());
+    }
+
     function handler_tmpPWD($page, $certif = null)
     {
         global $globals;
-        // XXX: recovery requires data from the profile
         XDB::execute('DELETE FROM  account_lost_passwords
                             WHERE  DATE_SUB(NOW(), INTERVAL 380 MINUTE) > created');
 
@@ -400,6 +435,47 @@ Adresse de secours : ' . $to));
         }
     }
 
+    function handler_register_ext($page, $hash = null)
+    {
+        XDB::execute('DELETE FROM  register_pending_xnet
+                            WHERE  DATE_SUB(NOW(), INTERVAL 1 MONTH) > date');
+        $res = XDB::fetchOneAssoc('SELECT  uid, hruid, email
+                                     FROM  register_pending_xnet
+                                    WHERE  hash = {?}',
+                                  $hash);
+
+        if (is_null($hash) || is_null($res)) {
+            $page->trigErrorRedirect('Cette adresse n\'existe pas ou n\'existe plus sur le serveur.', '');
+        }
+
+        if (Post::has('pwhash') && Post::t('pwhash')) {
+            XDB::startTransaction();
+            XDB::query('UPDATE  accounts
+                           SET  password = {?}, state = \'active\', registration_date = NOW()
+                         WHERE  uid = {?} AND state = \'pending\' AND type = \'xnet\'',
+                       Post::t('pwhash'), $res['uid']);
+            XDB::query('DELETE FROM  register_pending_xnet
+                              WHERE  uid = {?}',
+                              $res['uid']);
+            XDB::commit();
+
+            S::logger($res['uid'])->log('passwd', '');
+
+            // Try to start a session (so the user don't have to log in); we will use
+            // the password available in Post:: to authenticate the user.
+            Post::kill('wait');
+            Platal::session()->startAvailableAuth();
+
+            $page->changeTpl('xnet/register.success.tpl');
+            $page->assign('email', $res['email']);
+        } else {
+            $page->changeTpl('platal/password.tpl');
+            $page->assign('xnet', true);
+            $page->assign('hruid', $res['hruid']);
+            $page->assign('do_auth', 1);
+        }
+    }
+
     function handler_skin($page)
     {
         global $globals;
index 954753e..c780786 100644 (file)
@@ -25,15 +25,15 @@ class ProfileModule extends PLModule
     {
         return array(
             'photo'                      => $this->make_hook('photo',                      AUTH_PUBLIC),
-            'photo/change'               => $this->make_hook('photo_change',               AUTH_MDP),
+            'photo/change'               => $this->make_hook('photo_change',               AUTH_MDP,    'user'),
 
             'fiche.php'                  => $this->make_hook('fiche',                      AUTH_PUBLIC),
             'profile'                    => $this->make_hook('profile',                    AUTH_PUBLIC),
-            'profile/private'            => $this->make_hook('profile',                    AUTH_COOKIE),
+            'profile/private'            => $this->make_hook('profile',                    AUTH_COOKIE, 'user'),
             'profile/ax'                 => $this->make_hook('ax',                         AUTH_COOKIE, 'admin,edit_directory'),
-            'profile/edit'               => $this->make_hook('p_edit',                     AUTH_MDP),
+            'profile/edit'               => $this->make_hook('p_edit',                     AUTH_MDP,    'user'),
             '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/address/del'   => $this->make_hook('ajax_address_del',           AUTH_MDP,    'user'),
             '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),
@@ -45,15 +45,15 @@ class ProfileModule extends PLModule
             'profile/ajax/buildnames'    => $this->make_hook('ajax_buildnames',            AUTH_COOKIE, 'user', NO_AUTH),
             'profile/ajax/tree/jobterms' => $this->make_hook('ajax_tree_job_terms',        AUTH_COOKIE, 'user', NO_AUTH),
             'profile/jobterms'           => $this->make_hook('jobterms',                   AUTH_COOKIE, 'user', NO_AUTH),
-            'javascript/education.js'    => $this->make_hook('education_js',               AUTH_COOKIE),
-            'javascript/grades.js'       => $this->make_hook('grades_js',                  AUTH_COOKIE),
+            'javascript/education.js'    => $this->make_hook('education_js',               AUTH_COOKIE, 'user'),
+            'javascript/grades.js'       => $this->make_hook('grades_js',                  AUTH_COOKIE, 'user'),
             'profile/medal'              => $this->make_hook('medal',                      AUTH_PUBLIC),
 
-            'referent'                   => $this->make_hook('referent',                   AUTH_COOKIE),
+            'referent'                   => $this->make_hook('referent',                   AUTH_COOKIE, 'user'),
             'referent/country'           => $this->make_hook('ref_country',                AUTH_COOKIE, 'user', NO_AUTH),
             'referent/autocomplete'      => $this->make_hook('ref_autocomplete',           AUTH_COOKIE, 'user', NO_AUTH),
 
-            'groupes-x'                  => $this->make_hook('xnet',                       AUTH_COOKIE),
+            'groupes-x'                  => $this->make_hook('xnet',                       AUTH_COOKIE, 'groups'),
             'groupes-x/logo'             => $this->make_hook('xnetlogo',                   AUTH_PUBLIC),
 
             'vcard'                      => $this->make_hook('vcard',                      AUTH_COOKIE, 'user', NO_HTTPS),
@@ -224,13 +224,14 @@ class ProfileModule extends PLModule
         }
 
         // Determines the access level at which the profile will be displayed.
-        if (!S::logged() || !S::user()->checkPerms('directory_ax') || Env::v('view') == 'public') {
-            $view = 'public';
-        } else if (!S::user()->checkPerms('directory_private') || Env::v('view') == 'ax') {
-            $view = 'ax';
+        if (Env::v('view') == 'public') {
+            $view = Visibility::VIEW_PUBLIC;
+        } else if (Env::v('view') == 'ax') {
+            $view = Visibility::VIEW_AX;
         } else {
-            $view = 'private';
+            $view = Visibility::VIEW_PRIVATE;
         }
+        $visibility = Visibility::defaultForRead($view);
 
         // Display pending picture
         if (S::logged() && Env::v('modif') == 'new') {
@@ -241,7 +242,7 @@ class ProfileModule extends PLModule
         if (is_null($pid)) {
             $owner = User::getSilent($id);
             if ($owner) {
-                $profile = $owner->profile(true, Profile::FETCH_ALL, $view);
+                $profile = $owner->profile(true, Profile::FETCH_ALL, $visibility);
                 if ($profile) {
                     $pid = $profile->id();
                 }
@@ -249,7 +250,7 @@ class ProfileModule extends PLModule
         } else {
             // Fetches profile's and profile's owner information and redirects to
             // marketing if the owner has not subscribed and the requirer has logged in.
-            $profile = Profile::get($pid, Profile::FETCH_ALL, $view);
+            $profile = Profile::get($pid, Profile::FETCH_ALL, $visibility);
             $owner = $profile->owner();
         }
         if (is_null($pid)) {
@@ -258,6 +259,7 @@ class ProfileModule extends PLModule
             }
             return PL_NOT_FOUND;
         }
+
         // Now that we know this is an existing profile, we can switch to the
         // appropriate template.
         $page->changeTpl('profile/profile.tpl', SIMPLE);
@@ -271,13 +273,13 @@ class ProfileModule extends PLModule
         $page->setTitle($profile->fullName());
 
         // Determines and displays the virtual alias.
-        if (!is_null($owner) && $profile->alias_pub == 'public') {
+        if (!is_null($owner) && $profile->isVisible($profile->alias_pub)) {
             $page->assign('virtualalias', $owner->emailAlias());
         }
 
         $page->assign_by_ref('profile', $profile);
         $page->assign_by_ref('owner', $owner);
-        $page->assign('view', $view);
+        $page->assign('view', $visibility);
         $page->assign('logged', S::logged());
 
         header('Last-Modified: ' . date('r', strtotime($profile->last_change)));
index 12731d2..b235080 100644 (file)
@@ -571,6 +571,7 @@ class ProfilePageGeneral extends ProfilePage
         if (!S::user()->isMe($this->owner)) {
             $this->settings['deathdate'] = new ProfileSettingDate(true);
             $this->settings['birthdate'] = new ProfileSettingDate(true);
+            $this->settings['birthdate_ref'] = new ProfileSettingDate(true);
         } else {
             $this->settings['yourself'] = null;
             $this->settings['birthdate'] = new ProfileSettingDate();
@@ -592,7 +593,7 @@ class ProfilePageGeneral extends ProfilePage
         $res = XDB::query("SELECT  p.nationality1, p.nationality2, p.nationality3, IF(p.birthdate = 0, '', p.birthdate) AS birthdate,
                                    p.email_directory as email_directory, pd.promo AS promo_display,
                                    p.freetext, p.freetext_pub, p.ax_id AS matricule_ax, pd.yourself,
-                                   p.deathdate
+                                   p.deathdate, IF(p.birthdate_ref = 0, '', p.birthdate_ref) AS birthdate_ref
                              FROM  profiles              AS p
                        INNER JOIN  profile_display       AS pd ON (pd.pid = p.pid)
                             WHERE  p.pid = {?}", $this->pid());
@@ -697,6 +698,12 @@ class ProfilePageGeneral extends ProfilePage
                 }
             }
         }
+        if ($this->orig['birthdate_ref'] == 0 && !S::user()->isMe($this->owner) && $this->changed['birthdate_ref']) {
+            XDB::execute('UPDATE  profiles
+                             SET  birthdate_ref = {?}
+                           WHERE  pid = {?}',
+                         ProfileSettingDate::toSQLDate($this->values['birthdate_ref']), $this->pid());
+        }
         if (!S::user()->isMe($this->owner) && $this->changed['deathdate']) {
             XDB::execute('UPDATE  profiles
                              SET  deathdate = {?}, deathdate_rec = NOW()
index b31f327..d29a5ef 100644 (file)
@@ -131,7 +131,7 @@ class ProfileSettingJob implements ProfileSetting
         return $jobs;
     }
 
-    private function cleanJob(ProfilePage $page, $jobid, array &$job, &$success, $maxPublicity)
+    private function cleanJob(ProfilePage $page, $jobid, array &$job, &$success, $job_level)
     {
         if ($job['w_email'] == "new@example.org") {
             $job['w_email'] = $job['w_email_new'];
@@ -178,10 +178,10 @@ class ProfileSettingJob implements ProfileSetting
             }
         }
 
-        if ($maxPublicity->isVisible($job['w_email_pub'])) {
-            $job['w_email_pub'] = $maxPublicity->level();
+        if (Visibility::isLessRestrictive($job['w_email_pub'], $job_level)) {
+            $job['w_email_pub'] = $job_level;
         }
-        $job['w_phone'] = Phone::formatFormArray($job['w_phone'], $s, $maxPublicity);
+        $job['w_phone'] = Phone::formatFormArray($job['w_phone'], $s, $job_level);
 
         if ($job['w_entry_year'] && strlen($job['w_entry_year']) != 4) {
             $job['w_entry_year_error'] = true;
@@ -244,17 +244,20 @@ class ProfileSettingJob implements ProfileSetting
         foreach ($value as $key => &$job) {
             $address = new Address($job['w_address']);
             $s = $address->format();
-            $maxPublicity = new ProfileVisibility($job['pub']);
-            if ($maxPublicity->isVisible($address->pub)) {
-                $address->pub = $maxPublicity->level();
+
+            // Force the address publicity to be at least as restricted as
+            // the job publicity.
+            $job_level = $job['pub'];
+            if (Visibility::isLessRestrictive($address->pub, $job_level)) {
+                $address->pub = $job_level;
             }
             $job['w_address'] = $address->toFormArray();
-            $this->cleanJob($page, $key, $job, $s, $maxPublicity);
+            $this->cleanJob($page, $key, $job, $s, $job_level);
             if (!$init) {
                 $success = ($success && $s);
             }
         }
-        usort($value, 'ProfileVisibility::comparePublicity');
+        usort($value, 'Visibility::comparePublicity');
         return $value;
     }
 
index 242733f..c6eae36 100644 (file)
@@ -108,7 +108,7 @@ class ProfileSettingPhones implements ProfileSetting
         $phones = array();
 
         if (is_null($value)) {
-            $it = Phone::iterate(array($page->pid()), array(Phone::LINK_PROFILE), array(0));
+            $it = Phone::iterate(array($page->pid()), array(Phone::LINK_PROFILE), array(0), Visibility::defaultForEdit());
             while ($phone = $it->next()) {
                 $success = ($phone->format() && $success);
                 $phones[] = $phone->toFormArray();
index 265f2c9..83169e8 100644 (file)
@@ -31,6 +31,8 @@ class RegisterModule extends PLModule
 
     function handler_register($page, $hash = null)
     {
+        $page->forceSkin('register');
+
         $alert = array();
         $alert_details = '';
         $subState = new PlDict(S::v('subState', array()));
@@ -178,7 +180,7 @@ class RegisterModule extends PLModule
                         if (abs($ref_year - $year) > 2) {
                             $error[] = "La 'Date de naissance' n'est pas correcte.";
                             $alert[] = "Date de naissance incorrecte à l'inscription";
-                            $alert_details .= "\n   * Date de naissance renseignée : " . $birth . ' (date connue : ' . $subState->v('birthdateRef') . ')';
+                            $alert_details .= "\n   * Date de naissance renseignée : " . Post::t('birthdate') . ' (date connue : ' . $subState->v('birthdateRef') . ')';
                             $subState->set('wrong_birthdate', $birth);
                         }
                     }
@@ -234,7 +236,7 @@ class RegisterModule extends PLModule
                         if ($subState->s('birthdateRef') != '0000-00-00'
                             && $subState->s('birthdateRef') != $subState->s('birthdate')) {
                             $alert[] = "Date de naissance incorrecte à l'inscription";
-                            $alert_details .= "\n   * Date de naissance renseignée : " . $subState->s('birthdateRef') . ' (date connue : ' . $subState->s('birthdateRef') . ')';
+                            $alert_details .= "\n   * Date de naissance renseignée : " . $subState->s('birthdate') . ' (date connue : ' . $subState->s('birthdateRef') . ')';
                         }
                         if ($bannedIp) {
                             $alert[] = "Tentative d'inscription depuis une IP surveillée";
index d1e6fd4..a395c4d 100644 (file)
@@ -24,7 +24,7 @@ class ReminderModule extends PLModule
     function handlers()
     {
         return array(
-            'ajax/reminder' => $this->make_hook('reminder', AUTH_COOKIE),
+            'ajax/reminder' => $this->make_hook('reminder', AUTH_COOKIE, 'user'),
         );
     }
 
index 237c739..0504a77 100644 (file)
@@ -30,10 +30,10 @@ class SearchModule extends PLModule
             '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),
-            'search/referent/countries' => $this->make_hook('referent_countries', AUTH_COOKIE),
+            'jobs'                      => $this->make_hook('referent',           AUTH_COOKIE, 'user'),
+            'emploi'                    => $this->make_hook('referent',           AUTH_COOKIE, 'user'),
+            'referent/search'           => $this->make_hook('referent',           AUTH_COOKIE, 'user'),
+            'search/referent/countries' => $this->make_hook('referent_countries', AUTH_COOKIE, 'user'),
         );
     }
 
diff --git a/modules/sharingapi.php b/modules/sharingapi.php
new file mode 100644 (file)
index 0000000..e914ca6
--- /dev/null
@@ -0,0 +1,115 @@
+<?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 SharingAPIModule extends PlModule
+{
+    function handlers()
+    {
+        return array(
+            'api/1/sharing/search'    => $this->make_api_hook('search',    AUTH_COOKIE, 'api_user_readonly'),
+            'api/1/sharing/bulkmail'  => $this->make_api_hook('bulkmail',  AUTH_COOKIE, 'api_user_readonly'),
+            'api/1/sharing/picture'   => $this->make_hook('picture_token', AUTH_PUBLIC),
+        );
+    }
+
+    function handler_search(PlPage $page, PlUser $authUser, $payload)
+    {
+        require_once 'partnersharing.inc.php';
+        $partner = PartnerSharing::fetchByAPIUser($authUser);
+        if ($partner == null || !$partner->has_directory) {
+            return PL_FORBIDDEN;
+        }
+
+        $this->load('request.inc.php');
+
+        $payload = new PlDict($payload);
+
+        $errors = WSDirectoryRequest::validatePayload($payload);
+
+        if (count($errors)) {
+            foreach ($errors as $error_code) {
+                $page->trigError(WSDirectoryRequest::$ERROR_MESSAGES[$error_code]);
+            }
+            return PL_BAD_REQUEST;
+        }
+
+        // Processing
+        $request = new WSDirectoryRequest($partner, $payload);
+        $request->assignToPage($page);
+        return PL_JSON;
+    }
+
+    function handler_bulkmail(PlPage $page, PlUser $authUser, $payload)
+    {
+        require_once 'partnersharing.inc.php';
+        $partner = PartnerSharing::fetchByAPIUser($authUser);
+        if ($partner == null || !$partner->has_bulkmail) {
+            return PL_FORBIDDEN;
+        }
+
+        if (!isset($payload['uids'])) {
+            $page->trigError('Malformed query.');
+            return PL_BAD_REQUEST;
+        }
+
+        $uids = $payload['uids'];
+
+        $pf = new UserFilter(
+            new PFC_And(
+                new UFC_PartnerSharingID($partner->id, $uids),
+                new UFC_HasValidEmail(),
+                new UFC_PartnerSharingEmail($partner->id)
+            ));
+
+        $contexts = array();
+        foreach ($pf->iterUsers() as $user) {
+            $contexts[] = array(
+                'name' => $user->fullName(),
+                'email' => $user->bestEmail(),
+                'gender' => $user->isFemale() ? 'woman' : 'man',
+            );
+        }
+        $page->jsonAssign('contexts', $contexts);
+        return PL_JSON;
+    }
+
+    function handler_picture_token(PlPage $page, $size, $token)
+    {
+        XDB::rawExecute('DELETE FROM  profile_photo_tokens
+                               WHERE  expires <= NOW()');
+        $pid = XDB::fetchOneCell('SELECT  pid
+                                    FROM  profile_photo_tokens
+                                   WHERE  token = {?}', $token);
+        if ($pid != null) {
+            $res = XDB::fetchOneAssoc('SELECT  attach, attachmime, x, y, last_update
+                                         FROM  profile_photos
+                                        WHERE  pid = {?}', $pid);
+            $photo = PlImage::fromData($res['attach'], 'image/' . $res['attachmime'], $res['x'], $res['y'], $res['last_update']);
+            $photo->send();
+        } else {
+            return PL_NOT_FOUND;
+        }
+    }
+}
+
+// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
+?>
diff --git a/modules/sharingapi/request.inc.php b/modules/sharingapi/request.inc.php
new file mode 100644 (file)
index 0000000..0dd7910
--- /dev/null
@@ -0,0 +1,763 @@
+<?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 WSDirectoryRequest
+{
+    // Default number of returned results.
+    const DEFAULT_AMOUNT = 20;
+
+    public $fields;
+    public $criteria;
+    public $order = array();
+    public $amount = 0;
+    protected $partner = null;
+
+    const ORDER_RAND = 'rand';
+    const ORDER_NAME = 'name';
+    const ORDER_PROMOTION = 'promotion';
+
+    public static $order_choices = array(
+        self::ORDER_RAND,
+        self::ORDER_NAME,
+        self::ORDER_PROMOTION,
+    );
+
+    public function __construct($partner, PlDict $payload)
+    {
+        $this->partner = $partner;
+        global $globals;
+
+        $this->fields = array_intersect($payload->v('fields'), WSRequestFields::$choices);
+        $this->order = array_intersect($payload->v('order', array()), self::$order_choices);
+
+        $this->criteria = array();
+        $criteria = new PlDict($payload->v('criteria'));
+        foreach (WSRequestCriteria::$choices_simple as $criterion) {
+            if ($criteria->has($criterion)) {
+                $this->criteria[$criterion] = $criteria->s($criterion);
+            }
+        }
+        foreach (WSRequestCriteria::$choices_enum as $criterion) {
+            if ($criteria->has($criterion)) {
+                $this->criteria[$criterion] = $criteria->s($criterion);
+            }
+        }
+        foreach (WSRequestCriteria::$choices_list as $criterion) {
+            if ($criteria->has($criterion)) {
+                $this->criteria[$criterion] = $criteria->v($criterion);
+            }
+        }
+
+        // Amount may not exceed $globals->sharingapi->max_result_per_query.
+        $amount = $payload->i('amount', self::DEFAULT_AMOUNT);
+        $this->amount = min($amount, $globals->sharingapi->max_result_per_query);
+    }
+
+    public function get()
+    {
+        $cond = $this->getCond();
+        $cond->addChild(new UFC_PartnerSharing($this->partner->id));
+        $pf = new ProfileFilter($cond, $this->getOrders());
+        $pf->restrictVisibilityForPartner($this->partner->id);
+        $response = array();
+        $matches = $pf->getTotalProfileCount();
+        $response['matches'] = $matches;
+
+        $profiles = array();
+        if ($matches) {
+            // TODO : improve fetching by passing an adequate FETCH field
+            $iter = $pf->iterProfiles(new PlLimit($this->amount), 0x0000, Visibility::get(Visibility::VIEW_PRIVATE));
+            while ($profile = $iter->next()) {
+                if ($profile->getPartnerSettings($this->partner->id)->exposed_uid !== 0) {
+                    $profile_data = new WSRequestEntry($this->partner, $profile);
+                    $profiles[] = $profile_data->getFields($this->fields);
+                }
+            }
+        }
+        $response['profiles'] = $profiles;
+        return $response;
+    }
+
+    public function assignToPage(PlPage $page)
+    {
+        $response = $this->get();
+        $page->jsonAssign('matches', $response['matches']);
+        $page->jsonAssign('profiles', $response['profiles']);
+    }
+
+    /** Compute the orders to use for the current request.
+     * @return array of PlFilterOrder
+     */
+    protected function getOrders()
+    {
+        $orders = array();
+        foreach ($this->order as $order)
+        {
+            switch ($order) {
+            case self::ORDER_RAND:
+                $orders[] = new PFO_Random();
+                break;
+            case self::ORDER_NAME:
+                $orders[] = new UFO_Name(Profile::DN_SORT);
+                break;
+            case self::ORDER_PROMOTION:
+                $orders[] = new UFO_Promo();
+                break;
+            default:
+                break;
+            }
+        }
+        return $orders;
+    }
+
+    /** Compute the conditions to use for the current request.
+     * @return A PlFilterCondition instance (actually a PFC_And)
+     */
+    protected function getCond()
+    {
+        $cond = new PFC_And();
+        foreach ($this->criteria as $criterion => $value) {
+            switch ($criterion) {
+
+            // ENUM fields
+            case WSRequestCriteria::SCHOOL:
+                // Useless criterion: we don't need to check on origin school
+                if (WSRequestCriteria::$choices_enum[$criterion][$value]) {
+                    $cond->addChild(new PFC_True());
+                } else {
+                    $cond->addChild(new PFC_False());
+                };
+                break;
+            case WSRequestCriteria::DIPLOMA:
+                $diploma = WSRequestCriteria::$choices_enum[$criterion][$value];
+                $id_X = XDB::fetchOneCell('SELECT  id
+                                             FROM  profile_education_enum
+                                            WHERE  abbreviation = {?}', 'X');
+                $cond->addChildren(array(
+                    new UFC_EducationSchool($id_X),
+                    new UFC_EducationDegree($diploma),
+                ));
+                break;
+
+            // TEXT fields
+            case WSRequestCriteria::FIRSTNAME:
+            case WSRequestCriteria::LASTNAME:
+                $cond->addChild(new UFC_NameTokens($value, UFC_NameTokens::FLAG_PUBLIC, false, false, $criterion));
+                break;
+            case WSRequestCriteria::PROMOTION:
+                $cond->addChild(new PFC_Or(
+                    new UFC_Promo(UserFilter::OP_EQUALS,
+                                  UserFilter::GRADE_ING,
+                                  $value),
+                    new UFC_Promo(UserFilter::OP_EQUALS,
+                                  UserFilter::GRADE_MST,
+                                  $value),
+                    new UFC_Promo(UserFilter::OP_EQUALS,
+                                  UserFilter::GRADE_PHD,
+                                  $value)
+                ));
+                break;
+            case WSRequestCriteria::ALT_DIPLOMA:
+                $cond->addChild(
+                    new UFC_EducationDegree(
+                        DirEnum::getIds(DirEnum::EDUDEGREES, $value)));
+                break;
+            case WSRequestCriteria::DIPLOMA_FIELD:
+                $cond->addChild(
+                    new UFC_EducationField(
+                        DirEnum::getIds(DirEnum::EDUFIELDS, $value)));
+                break;
+            case WSRequestCriteria::CITY:
+                $cond->addChild(
+                    new UFC_AddressField($value,
+                                         UFC_AddressField::FIELD_LOCALITY,
+                                         UFC_Address::TYPE_HOME,
+                                         UFC_Address::FLAG_CURRENT));
+                break;
+            case WSRequestCriteria::COUNTRY:
+                $cond->addChild(
+                    new UFC_AddressField($value,
+                                         UFC_AddressField::FIELD_COUNTRY,
+                                         UFC_Address::TYPE_HOME,
+                                         UFC_Address::FLAG_CURRENT));
+                break;
+            case WSRequestCriteria::ZIPCODE:
+                $cond->addChild(
+                    new UFC_AddressField($value,
+                                         UFC_AddressField::FIELD_ZIPCODE,
+                                         UFC_Address::TYPE_HOME,
+                                         UFC_Address::FLAG_CURRENT));
+                break;
+            case WSRequestCriteria::JOB_ANY_COUNTRY:
+                $cond->addChild(
+                    new UFC_AddressField($value,
+                                         UFC_AddressField::FIELD_COUNTRY,
+                                         UFC_Address::TYPE_PRO,
+                                         UFC_Address::FLAG_ANY));
+                break;
+            case WSRequestCriteria::JOB_CURRENT_CITY:
+                $cond->addChild(
+                    new UFC_AddressField($value,
+                                         UFC_AddressField::FIELD_LOCALITY,
+                                         UFC_Address::TYPE_PRO,
+                                         UFC_Address::FLAG_ANY));
+                break;
+            case WSRequestCriteria::JOB_ANY_COMPANY:
+            case WSRequestCriteria::JOB_CURRENT_COMPANY:
+                $cond->addChild(
+                    new UFC_Job_Company(UFC_Job_Company::JOBNAME,
+                                        $value));
+                break;
+            case WSRequestCriteria::JOB_ANY_SECTOR:
+            case WSRequestCriteria::JOB_CURRENT_SECTOR:
+            case WSRequestCriteria::JOB_CURRENT_TITLE:
+                $cond->addChild(
+                    new UFC_Job_Terms(DirEnum::getIds(DirEnum::JOBTERMS, $value)));
+                break;
+
+            // LIST fields
+            case WSRequestCriteria::HOBBIES:
+                $subcond = new PFC_Or();
+                foreach ($value as $val) {
+                    $subcond->addChild(new UFC_Comment($value));
+                }
+                $cond->addChild($subcond);
+                break;
+            case WSRequestCriteria::JOB_COMPETENCIES:
+            case WSRequestCriteria::JOB_RESUME:
+            case WSRequestCriteria::PROFESSIONAL_PROJECT:
+                $subcond = new PFC_Or();
+                foreach ($value as $val) {
+                    $subcond->addChild(
+                        new UFC_Job_Description($value, UserFilter::JOB_USERDEFINED));
+                }
+                $cond->addChild($subcond);
+                break;
+            case WSRequestCriteria::NOT_UID:
+                $cond->addChild(
+                    new PFC_Not(
+                        new UFC_PartnerSharingID($this->partner->id, $value)));
+                break;
+            default:
+                break;
+            }
+        }
+
+        return $cond;
+    }
+
+    /** Input validation
+     */
+    const ERROR_MISSING_FIELDS = 'missing_fields';
+    const ERROR_MISSING_CRITERIA = 'missing_criteria';
+    const ERROR_MALFORMED_AMOUNT = 'malformed_amount';
+    const ERROR_MALFORMED_ORDER = 'malformed_order';
+
+    public static $ERROR_MESSAGES = array(
+        self::ERROR_MISSING_FIELDS => "The 'fields' field is mandatory.",
+        self::ERROR_MISSING_CRITERIA => "The 'criteria' field is mandatory.",
+        self::ERROR_MALFORMED_AMOUNT => "The 'amount' value is invalid (expected an int)",
+        self::ERROR_MALFORMED_ORDER => "The 'order' value is invalid (expected an array)",
+    );
+
+    /** Static method performing all input validation on the payload.
+     * @param PlDict $payload The payload to validate
+     * @return array Errors discovered when validating input
+     */
+    public static function validatePayload(PlDict $payload)
+    {
+        $errors = array();
+        if (!$payload->has('fields')) {
+            $errors[] = self::ERROR_MISSING_FIELDS;
+        }
+        if (!$payload->has('criteria')) {
+            $errors[] = self::ERROR_MISSING_CRITERIA;
+        }
+
+        if ($payload->has('amount') && $payload->i('amount', -1) < 0) {
+            $errors[] = self::ERROR_MALFORMED_AMOUNT;
+        }
+
+        if (!is_array($payload->v('order', array()))) {
+            $errors[] = self::ERROR_MALFORMED_ORDER;
+        }
+
+        return $errors;
+    }
+}
+
+// {{{ WSRequestEntry
+/** Performs field retrieval for a profile.
+ */
+class WSRequestEntry
+{
+    private $profile = null;
+    private $partner = null;
+    private $settings = null;
+
+    public function __construct($partner, $profile)
+    {
+        $this->partner = $partner;
+        $this->profile = $profile;
+        $this->settings = $this->profile->getPartnerSettings($this->partner->id);
+    }
+
+    public function isVisible($level)
+    {
+        return $this->settings->sharing_visibility->isVisible($level);
+    }
+
+    public function getFields($fields)
+    {
+        $data = array();
+        foreach ($fields as $field)
+        {
+            $val = $this->getFieldValue($field);
+            if ($val !== null) {
+                $data[$field] = $val;
+            }
+        }
+        $data['uid'] = $this->settings->exposed_uid;
+        return $data;
+    }
+
+    protected function getFieldValue($field)
+    {
+        // Shortcut
+        $p = $this->profile;
+
+        switch ($field) {
+        case WSRequestFields::UID:
+            // UID is always included
+            return;
+        case WSRequestFields::BIRTHDATE:
+        case WSRequestFields::FAMILY_POSITION:
+        case WSRequestFields::HONORARY_TITLES:
+        case WSRequestFields::LANGS:
+        case WSRequestFields::JOB_COMPETENCIES:
+        case WSRequestFields::RESUME:
+        case WSRequestFields::PROFESSIONAL_PROJECT:
+        case WSRequestFields::HOBBIES:
+            // Ignored fields
+            return;
+
+        // Public fields
+        case WSRequestFields::FIRSTNAME:
+            return $p->firstName();
+        case WSRequestFields::LASTNAME:
+            return $p->lastName();
+        case WSRequestFields::GENDER:
+            if ($p->isFemale()) {
+                return WSRequestFields::GENDER_WOMAN;
+            } else {
+                return WSRequestFields::GENDER_MAN;
+            }
+        case WSRequestFields::SCHOOL:
+            return WSRequestCriteria::SCHOOL_X;
+        case WSRequestFields::DIPLOMA:
+            $edu = $p->getEducations(Profile::EDUCATION_MAIN);
+            if (count($edu)) {
+                return WSRequestFields::profileDegreeToWSDiploma(
+                    array_pop($edu)->degree);
+            } else {
+                return null;
+            }
+        case WSRequestFields::DIPLOMA_FIELD:
+            $edu = $p->getEducations(Profile::EDUCATION_MAIN);
+            if (count($edu)) {
+                return array_pop($edu)->field;
+            } else {
+                return null;
+            }
+        case WSRequestFields::PROMOTION:
+            return $p->yearpromo();
+        case WSRequestFields::ALT_DIPLOMAS:
+            $diplomas = array();
+            foreach ($p->getExtraEducations() as $edu) {
+                $diplomas[] = WSRequestFields::profileDegreeToWSDiploma(
+                    $edu->degree);
+            }
+            return $diplomas;
+
+        // Other generic profile fields
+        case WSRequestFields::EMAIL:
+            if ($this->settings->sharing_visibility->isVisible(Visibility::EXPORT_PRIVATE)) {
+                // If sharing "all" data, share best email.
+                return $p->displayEmail();
+            } elseif ($this->settings->sharing_visibility->isVisible(Visibility::EXPORT_AX)) {
+                // If sharing "AX" level, share "AX" email.
+                return $p->email_directory;
+            } else {
+                // Otherwise, don't share.
+                return null;
+            }
+        case WSRequestFields::MOBILE_PHONE:
+            $phones = $p->getPhones(Profile::PHONE_TYPE_MOBILE | Profile::PHONE_LINK_PROFILE);
+            if (count($phones)) {
+                $phone = array_pop($phones);
+                if ($this->isVisible($phone->pub)) {
+                    return $phone->display;
+                }
+            }
+            return null;
+        case WSRequestFields::PIC_SMALL:
+        case WSRequestFields::PIC_MEDIUM:
+        case WSRequestFields::PIC_LARGE:
+            if ($this->isVisible($p->photo_pub)) {
+                $token = sha1(uniqid(rand(), true));
+                XDB::execute('DELETE FROM  profile_photo_tokens
+                                    WHERE  pid = {?}', $p->pid);
+                XDB::execute('INSERT INTO  profile_photo_tokens
+                                      SET  pid = {?}, token = {?},
+                                           expires = ADDTIME(NOW(), \'0:05:00\')',
+                                           $p->pid, $token);
+                $size_mappings = array(
+                    WSRequestFields::PIC_SMALL => 'small',
+                    WSRequestFields::PIC_MEDIUM => 'medium',
+                    WSRequestFields::PIC_LARGE => 'large',
+                );
+                $size = $size_mappings[$field];
+                return pl_url("api/1/sharing/picture/$size/$token");
+            } else {
+                return null;
+            }
+
+        // Address related
+        case WSRequestFields::CURRENT_CITY:
+            $address = $p->getMainAddress();
+            if ($address != null && $this->isVisible($address->pub)) {
+                return $address->locality;
+            } else {
+                return null;
+            }
+        case WSRequestFields::CURRENT_COUNTRY:
+            $address = $p->getMainAddress();
+            if ($address != null && $this->isVisible($address->pub)) {
+                return $address->country;
+            } else {
+                return null;
+            }
+        case WSRequestFields::ADDRESS:
+            $address = $p->getMainAddress();
+            if ($address != null && $this->isVisible($address->pub)) {
+                return $this->addressToResponse($address);
+            } else {
+                return null;
+            }
+
+        // Job related
+        case WSRequestFields::CURRENT_COMPANY:
+            $job = $p->getMainJob();
+            if ($job != null && $this->isVisible($job->pub)) {
+                return $job->company->name;
+            } else {
+                return null;
+            }
+        case WSRequestFields::JOB:
+            $jobs = $p->getJobs(Profile::JOBS_ALL);
+            $res = array();
+            foreach ($jobs as $job) {
+                if ($this->isVisible($job->pub)) {
+                    $res[] = $this->jobToResponse($job);
+                }
+            }
+            return $res;
+        case WSRequestFields::MINI_RESUME:
+            if ($this->isVisible(Visibility::EXPORT_PRIVATE)) {
+                return $p->cv;
+            } else {
+                return null;
+            }
+
+        // Community
+        case WSRequestFields::GROUPS:
+            $groups = array();
+            if ($this->isVisible(Visibility::EXPORT_PRIVATE)) {
+                foreach ($p->owner()->groups(true, true) as $group) {
+                    $groups[] = array('name' => $group['nom']);
+                }
+            }
+            return $groups;
+        case WSRequestFields::FRIENDS:
+            $friends = array();
+            if ($this->isVisible(Visibility::EXPORT_PRIVATE)) {
+                $contacts = $p->owner()->iterContacts();
+                if ($contacts == null) {
+                    return $friends;
+                }
+
+                while ($contact = $contacts->next()) {
+                    $cps = $contact->getPartnerSettings($this->partner->id);
+                    if ($cps->sharing_visibility->isVisible(Visibility::EXPORT_PRIVATE)) {
+                        $friends[] = $cps->exposed_uid;
+                    }
+                }
+            }
+            return $friends;
+        case WSRequestFields::NETWORKING:
+            $networks = array();
+            if ($this->isVisible(Visibility::EXPORT_PRIVATE)) {
+                foreach ($p->getNetworking(Profile::NETWORKING_ALL) as $nw) {
+                    $networks[] = array(
+                        'network' => $nw['name'],
+                        'login' => $nw['address'],
+                    );
+                }
+            }
+            return $networks;
+
+        default:
+            return null;
+        }
+    }
+
+    protected function jobToResponse($job)
+    {
+        $data = array(
+            'company' => $job->company->name,
+            'title' => $job->description,
+            'sector' => array_pop($job->terms),
+            'entry' => null,
+            'left' => null,
+        );
+        foreach($job->phones() as $phone) {
+            if ($this->isVisible($phone->pub)) {
+                $data['phone'] = $phone->display;
+                break;
+            }
+        }
+        if ($job->address && $this->isVisible($job->address->pub)) {
+            $data['address'] = $this->addressToResponse($job->address);
+        }
+        return $data;
+    }
+
+    protected function addressToResponse($address)
+    {
+        $data = array(
+            'street' => $address->postalText,
+            'zipcode' => $address->postalCode,
+            'city' => $address->locality,
+            'country' => $address->country,
+            'latitude' => $address->latitude,
+            'longitude' => $address->longitude,
+        );
+        return $data;
+    }
+}
+// }}}
+// {{{ WSRequestCriteria
+/** Holds all enums and related mappings for criterias.
+ */
+class WSRequestCriteria
+{
+    const FIRSTNAME = 'firstname';
+    const LASTNAME = 'lastname';
+    const SCHOOL = 'school';
+    const DIPLOMA = 'diploma';
+    const DIPLOMA_FIELD = 'diploma_field';
+    const PROMOTION = 'promotion';
+    const HOBBIES = 'hobbies';
+    const ZIPCODE = 'zipcode';
+    const CITY = 'city';
+    const COUNTRY = 'country';
+    const JOB_CURRENT_SECTOR = 'job_current_sector';
+    const JOB_CURRENT_TITLE = 'job_current_title';
+    const JOB_CURRENT_COMPANY = 'job_current_company';
+    const JOB_CURRENT_CITY = 'job_current_city';
+    const JOB_CURRENT_COUNTRY = 'job_current_country';
+    const JOB_ANY_SECTOR = 'job_any_sector';
+    const JOB_ANY_COMPANY = 'job_any_company';
+    const JOB_ANY_COUNTRY = 'job_any_country';
+    const JOB_RESUME = 'job_resume';
+    const JOB_COMPETENCIES = 'job_competencies';
+    const PROFESSIONAL_PROJECT = 'professional_project';
+    const ALT_DIPLOMA = 'alt_diploma';
+    const NOT_UID = 'not_uid';
+
+    public static $choices_simple = array(
+        self::FIRSTNAME,
+        self::LASTNAME,
+        self::PROMOTION,
+        self::ALT_DIPLOMA,
+        self::DIPLOMA_FIELD,
+        self::CITY,
+        self::ZIPCODE,
+        self::COUNTRY,
+        self::JOB_ANY_COUNTRY,
+        self::JOB_CURRENT_CITY,
+        self::JOB_CURRENT_COUNTRY,
+        self::JOB_ANY_COMPANY,
+        self::JOB_ANY_SECTOR,
+        self::JOB_CURRENT_COMPANY,
+        self::JOB_CURRENT_SECTOR,
+        self::JOB_CURRENT_TITLE,
+    );
+
+    const SCHOOL_AGRO = 'agro';
+    const SCHOOL_ENSAE = 'ensae';
+    const SCHOOL_ENSCP = 'enscp';
+    const SCHOOL_ENST = 'enst';
+    const SCHOOL_ENSTA = 'ensta';
+    const SCHOOL_ESPCI = 'espci';
+    const SCHOOL_GADZ = 'gadz';
+    const SCHOOL_HEC = 'hec';
+    const SCHOOL_MINES = 'ensmp';
+    const SCHOOL_PONTS = 'enpc';
+    const SCHOOL_SUPELEC = 'supelec';
+    const SCHOOL_SUPOP = 'supop';
+    const SCHOOL_X = 'X';
+
+    const DIPLOMA_ING = 'ING';
+    const DIPLOMA_MASTER = 'MASTER';
+    const DIPLOMA_PHD = 'PHD';
+
+    public static $choices_enum = array(
+        self::SCHOOL => array(
+            self::SCHOOL_AGRO => false,
+            self::SCHOOL_ENSAE => false,
+            self::SCHOOL_ENSCP => false,
+            self::SCHOOL_ENST => false,
+            self::SCHOOL_ENSTA => false,
+            self::SCHOOL_ESPCI => false,
+            self::SCHOOL_GADZ => false,
+            self::SCHOOL_HEC => false,
+            self::SCHOOL_MINES => false,
+            self::SCHOOL_PONTS => false,
+            self::SCHOOL_SUPELEC => false,
+            self::SCHOOL_SUPOP => false,
+            self::SCHOOL_X => true,
+        ),
+        self::DIPLOMA => array(
+            self::DIPLOMA_ING => UserFilter::GRADE_ING,
+            self::DIPLOMA_MASTER => UserFilter::GRADE_MST,
+            self::DIPLOMA_PHD => UserFilter::GRADE_PHD,
+        ),
+    );
+
+    public static $choices_list = array(
+        self::HOBBIES,
+        self::JOB_COMPETENCIES,
+        self::JOB_RESUME,
+        self::NOT_UID,
+        self::PROFESSIONAL_PROJECT,
+    );
+}
+
+// }}}
+// {{{ WSRequestFields
+/** Holds all enums for fields.
+ */
+class WSRequestFields
+{
+    const UID = 'uid';
+    const FIRSTNAME = 'firstname';
+    const LASTNAME = 'lastname';
+    const BIRTHDATE = 'birthdate';
+    const GENDER = 'gender';
+    const FAMILY_POSITION = 'family_position';
+    const SCHOOL = 'school';
+    const DIPLOMA = 'diploma';
+    const DIPLOMA_FIELD = 'diploma_field';
+    const PROMOTION = 'promotion';
+    const ALT_DIPLOMAS = 'alt_diplomas';
+    const CURRENT_COMPANY = 'current_company';
+    const CURRENT_CITY = 'current_city';
+    const CURRENT_COUNTRY = 'current_country';
+    const MOBILE_PHONE = 'mobile_phone';
+    const HONORARY_TITLES = 'honorary_titles';
+    const EMAIL = 'email';
+    const PIC_SMALL = 'pic_small';
+    const PIC_MEDIUM = 'pic_medium';
+    const PIC_LARGE = 'pic_large';
+    const ADDRESS = 'address';
+    const JOB = 'job';
+    const GROUPS = 'groups';
+    const LANGS = 'langs';
+    const JOB_COMPETENCIES = 'job_competencies';
+    const MINI_RESUME = 'mini_resume';
+    const RESUME = 'resume';
+    const PROFESSIONAL_PROJECT = 'professional_project';
+    const HOBBIES = 'hobbies';
+    const FRIENDS = 'friends';
+    const NETWORKING = 'networking';
+
+    const GENDER_MAN = 'man';
+    const GENDER_WOMAN = 'woman';
+
+    const DIPLOMA_ING = 'engineer';
+    const DIPLOMA_MASTER = 'master';
+    const DIPLOMA_PHD = 'phd';
+
+    public static $choices = array(
+        self::UID,
+        self::FIRSTNAME,
+        self::LASTNAME,
+        self::BIRTHDATE,
+        self::GENDER,
+        self::FAMILY_POSITION,
+        self::SCHOOL,
+        self::DIPLOMA,
+        self::DIPLOMA_FIELD,
+        self::PROMOTION,
+        self::ALT_DIPLOMAS,
+        self::CURRENT_COMPANY,
+        self::CURRENT_CITY,
+        self::CURRENT_COUNTRY,
+        self::MOBILE_PHONE,
+        self::HONORARY_TITLES,
+        self::EMAIL,
+        self::PIC_SMALL,
+        self::PIC_MEDIUM,
+        self::PIC_LARGE,
+        self::ADDRESS,
+        self::JOB,
+        self::GROUPS,
+        self::LANGS,
+        self::JOB_COMPETENCIES,
+        self::MINI_RESUME,
+        self::RESUME,
+        self::PROFESSIONAL_PROJECT,
+        self::HOBBIES,
+        self::FRIENDS,
+        self::NETWORKING,
+    );
+
+    public static function profileDegreeToWSDiploma($degree)
+    {
+        switch ($degree) {
+            case Profile::DEGREE_X:
+                return self::DIPLOMA_ING;
+            case Profile::DEGREE_M:
+                return self::DIPLOMA_MASTER;
+            case Profile::DEGREE_D:
+                return self::DIPLOMA_PHD;
+            default:
+                return null;
+        }
+    }
+
+}
+// }}}
+
+// vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
+?>
index aa830a3..767db42 100644 (file)
@@ -36,11 +36,11 @@ class StatsModule extends PLModule
     function handlers()
     {
         return array(
-            'stats'                 => $this->make_hook('stats',     AUTH_COOKIE),
-            'stats/evolution'       => $this->make_hook('evolution', AUTH_COOKIE),
-            'stats/graph'           => $this->make_hook('graph',     AUTH_COOKIE),
-            'stats/graph/evolution' => $this->make_hook('graph_evo', AUTH_COOKIE),
-            'stats/promos'          => $this->make_hook('promos',    AUTH_COOKIE),
+            'stats'                 => $this->make_hook('stats',     AUTH_COOKIE, 'user'),
+            'stats/evolution'       => $this->make_hook('evolution', AUTH_COOKIE, 'user'),
+            'stats/graph'           => $this->make_hook('graph',     AUTH_COOKIE, 'user'),
+            'stats/graph/evolution' => $this->make_hook('graph_evo', AUTH_COOKIE, 'user'),
+            'stats/promos'          => $this->make_hook('promos',    AUTH_COOKIE, 'user'),
 
             'stats/coupures'        => $this->make_hook('coupures',  AUTH_PUBLIC),
         );
index f23dc7b..eaef664 100644 (file)
@@ -28,12 +28,12 @@ class SurveyModule extends PLModule
             'survey'             => $this->make_hook('index',         AUTH_PUBLIC),
             'survey/vote'        => $this->make_hook('vote',          AUTH_PUBLIC),
             'survey/result'      => $this->make_hook('result',        AUTH_PUBLIC),
-            'survey/edit'        => $this->make_hook('edit',          AUTH_COOKIE),
-            'survey/ajax'        => $this->make_hook('ajax',          AUTH_COOKIE),
-            'survey/admin'       => $this->make_hook('admin',         AUTH_MDP, 'admin'),
-            'survey/admin/edit'  => $this->make_hook('adminEdit',     AUTH_MDP, 'admin'),
-            'survey/admin/valid' => $this->make_hook('adminValidate', AUTH_MDP, 'admin'),
-            'survey/admin/del'   => $this->make_hook('adminDelete',   AUTH_MDP, 'admin'),
+            'survey/edit'        => $this->make_hook('edit',          AUTH_COOKIE, 'user'),
+            'survey/ajax'        => $this->make_hook('ajax',          AUTH_COOKIE, 'user'),
+            'survey/admin'       => $this->make_hook('admin',         AUTH_MDP,    'admin'),
+            'survey/admin/edit'  => $this->make_hook('adminEdit',     AUTH_MDP,    'admin'),
+            'survey/admin/valid' => $this->make_hook('adminValidate', AUTH_MDP,    'admin'),
+            'survey/admin/del'   => $this->make_hook('adminDelete',   AUTH_MDP,    'admin'),
         );
     }
     // }}}
index 497a7f8..852c5e8 100644 (file)
@@ -348,6 +348,9 @@ class Survey
         default:
             return null;
         }
+        if (!S::user()->checkPerms(PERMS_USER)) {
+            $where .=  XDB::format(' AND mode = {?}', self::MODE_ALL);
+        }
         $sql = 'SELECT id, title, end, mode
                   FROM surveys
                  WHERE '.$where.'
index 1140b98..e57d63d 100644 (file)
@@ -31,15 +31,10 @@ class XnetModule extends PLModule
             'groups'       => $this->make_hook('groups',       AUTH_PUBLIC),
             'groupes.php'  => $this->make_hook('groups2',      AUTH_PUBLIC),
             'plan'         => $this->make_hook('plan',         AUTH_PUBLIC),
-            'photo'        => $this->make_hook('photo',        AUTH_MDP),
-            'autologin'    => $this->make_hook('autologin',    AUTH_MDP),
-            'login/ext'    => $this->make_hook('login_ext',    AUTH_PUBLIC),
+            'photo'        => $this->make_hook('photo',        AUTH_MDP, 'groups'),
+            'autologin'    => $this->make_hook('autologin',    AUTH_MDP, 'groups'),
             'register/ext' => $this->make_hook('register_ext', AUTH_PUBLIC),
-            'recovery/ext' => $this->make_hook('recovery_ext', AUTH_PUBLIC),
-            'tmpPWD/ext'   => $this->make_hook('tmpPWD_ext',   AUTH_PUBLIC),
-            'edit'         => $this->make_hook('edit',         AUTH_MDP, 'user'),
-            'password'     => $this->make_hook('password',     AUTH_MDP, 'user'),
-
+            'edit'         => $this->make_hook('edit',         AUTH_MDP, 'groups'),
             'Xnet'         => $this->make_wiki_hook(),
         );
     }
@@ -230,146 +225,6 @@ class XnetModule extends PLModule
         exit;
     }
 
-    function handler_login_ext($page)
-    {
-        if (!S::logged()) {
-            $page->changeTpl('xnet/login.tpl');
-        } else {
-            pl_redirect('');
-        }
-    }
-
-    function handler_register_ext($page, $hash = null)
-    {
-        XDB::execute('DELETE FROM  register_pending_xnet
-                            WHERE  DATE_SUB(NOW(), INTERVAL 1 MONTH) > date');
-        $res = XDB::fetchOneAssoc('SELECT  uid, hruid
-                                     FROM  register_pending_xnet
-                                    WHERE  hash = {?}',
-                                  $hash);
-
-        if (is_null($hash) || is_null($res)) {
-            $page->trigErrorRedirect('Cette adresse n\'existe pas ou n\'existe plus sur le serveur.', '');
-        }
-
-        if (Post::has('pwhash') && Post::t('pwhash')) {
-            XDB::query('UPDATE  accounts
-                           SET  password = {?}, state = \'active\', registration_date = NOW()
-                         WHERE  uid = {?} AND state = \'pending\' AND type = \'xnet\'',
-                       Post::t('pwhash'), $res['uid']);
-            XDB::query('DELETE FROM  register_pending_xnet
-                              WHERE  uid = {?}',
-                       $res['uid']);
-
-            S::logger($res['uid'])->log('passwd', '');
-
-            // Try to start a session (so the user don't have to log in); we will use
-            // the password available in Post:: to authenticate the user.
-            Post::kill('wait');
-            Platal::session()->startAvailableAuth();
-
-            $page->changeTpl('xnet/register.success.tpl');
-            $page->assign('email', $res['email']);
-        } else {
-            $page->changeTpl('platal/password.tpl');
-            $page->assign('xnet', true);
-            $page->assign('hruid', $res['hruid']);
-            $page->assign('do_auth', 1);
-        }
-    }
-
-    function handler_recovery_ext($page)
-    {
-        $page->changeTpl('xnet/recovery.tpl');
-
-        if (!Post::has('login')) {
-            return;
-        }
-
-        $user = User::getSilent(Post::t('login'));
-        if (is_null($user)) {
-            $page->trigError('Le compte n\'existe pas.');
-            return;
-        }
-        if ($user->state != 'active') {
-            $page->trigError('Ton compte n\'est pas activé.');
-            return;
-        }
-
-        $page->assign('ok', true);
-
-        $hash = rand_url_id();
-        XDB::execute('INSERT INTO  account_xnet_lost_passwords (uid, date, hash)
-                           VALUES  ({?}, NOW(), {?})',
-                     $user->id(), $hash);
-
-        $mymail = new PlMailer();
-        $mymail->setFrom('"Gestion des mots de passe" <support+password@' . Platal::globals()->mail->domain . '>');
-        $mymail->addTo($user);
-        $mymail->setSubject("Votre certificat d'authentification");
-        $mymail->setTxtBody("Visitez la page suivante qui expire dans six heures :
-http://polytechnique.net/tmpPWD/ext/$hash
-
-Si en cliquant dessus vous n'y arrivez pas, copiez intégralement l'adresse dans la barre de votre navigateur. Si vous n'avez pas utilisé ce lien dans six heures, vous pouvez tout simplement recommencer cette procédure.
-
---
-Polytechnique.org
-\"Le portail des élèves & anciens élèves de l'École polytechnique\"
-
-Email envoyé à " . Post::t('login'));
-        $mymail->send();
-
-        S::logger($user->id())->log('recovery', $user->bestEmail());
-    }
-
-    function handler_tmpPWD_ext($page, $hash = null)
-    {
-        global $globals;
-        XDB::execute('DELETE FROM  account_xnet_lost_passwords
-                            WHERE  DATE_SUB(NOW(), INTERVAL 380 MINUTE) > date');
-
-        $uid = XDB::fetchOneCell('SELECT  uid
-                                    FROM  account_xnet_lost_passwords
-                                   WHERE  hash = {?}',
-                                 $hash);
-        if (is_null($uid)) {
-            $page->trigErrorRedirect("Cette adresse n'existe pas ou n'existe plus sur le serveur.", '');
-        }
-
-        $email = XDB::fetchOneCell('SELECT  email
-                                      FROM  accounts
-                                     WHERE  uid = {?}',
-                                   $uid);
-
-        if (Post::has('pwhash') && Post::t('pwhash')) {
-            $password = Post::t('pwhash');
-            XDB::query('UPDATE  accounts
-                           SET  password = {?}
-                         WHERE  uid = {?} AND state = \'active\'',
-                       $password, $uid);
-            XDB::query('DELETE FROM  account_xnet_lost_passwords
-                              WHERE  hash = {?}',
-                       $hash);
-
-            S::logger($uid)->log('passwd', '');
-
-            // Try to start a session (so the user don't have to log in); we will use
-            // the password available in Post:: to authenticate the user.
-            Post::kill('wait');
-            Platal::session()->startAvailableAuth();
-
-            $page->changeTpl('xnet/register.success.tpl');
-            $page->assign('email', $email);
-        } else {
-            $page->changeTpl('platal/password.tpl');
-            $page->assign('xnet_reset', true);
-            $page->assign('email', $email);
-            $page->assign('do_auth', 1);
-        }
-    }
-
-
-
     function handler_edit($page)
     {
         global $globals;
@@ -421,26 +276,6 @@ Email envoyé à " . Post::t('login'));
         $page->assign('user', $user);
     }
 
-    function handler_password ($page)
-    {
-        if (Post::has('pwhash') && Post::t('pwhash'))  {
-            S::assert_xsrf_token();
-
-            S::set('password', $password = Post::t('pwhash'));
-            XDB::execute('UPDATE  accounts
-                             SET  password = {?}
-                           WHERE  uid={?}', $password,
-                         S::i('uid'));
-            S::logger()->log('passwd');
-            Platal::session()->setAccessCookie(true);
-            $page->changeTpl('platal/password.success.tpl');
-            $page->run();
-        }
-
-        $page->changeTpl('platal/password.tpl');
-        $page->assign('xnet_reset', true);
-        $page->assign('do_auth', 0);
-    }
 }
 
 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
index edee012..f345f13 100644 (file)
@@ -26,10 +26,10 @@ class XnetEventsModule extends PLModule
     function handlers()
     {
         return array(
-            '%grp/events'       => $this->make_hook('events', AUTH_MDP),
-            '%grp/events/sub'   => $this->make_hook('sub',    AUTH_MDP),
-            '%grp/events/csv'   => $this->make_hook('csv',    AUTH_MDP, 'user', NO_HTTPS),
-            '%grp/events/ical'  => $this->make_hook('ical',   AUTH_MDP, 'user', NO_HTTPS),
+            '%grp/events'       => $this->make_hook('events', AUTH_MDP, 'groups'),
+            '%grp/events/sub'   => $this->make_hook('sub',    AUTH_MDP, 'groups'),
+            '%grp/events/csv'   => $this->make_hook('csv',    AUTH_MDP, 'groups', NO_HTTPS),
+            '%grp/events/ical'  => $this->make_hook('ical',   AUTH_MDP, 'groups', NO_HTTPS),
             '%grp/events/edit'  => $this->make_hook('edit',   AUTH_MDP, 'groupadmin'),
             '%grp/events/admin' => $this->make_hook('admin',  AUTH_MDP, 'groupmember'),
         );
index 87daf14..a1a37e2 100644 (file)
@@ -40,15 +40,15 @@ class XnetGrpModule extends PLModule
             '%grp/directory/unact' => $this->make_hook('non_active',            AUTH_MDP, 'groupadmin'),
             '%grp/trombi'          => $this->make_hook('trombi',                AUTH_MDP, 'groupannu'),
             '%grp/geoloc'          => $this->make_hook('geoloc',                AUTH_MDP, 'groupannu'),
-            '%grp/subscribe'       => $this->make_hook('subscribe',             AUTH_MDP),
+            '%grp/subscribe'       => $this->make_hook('subscribe',             AUTH_MDP, 'groups'),
             '%grp/subscribe/valid' => $this->make_hook('subscribe_valid',       AUTH_MDP, 'groupadmin'),
             '%grp/unsubscribe'     => $this->make_hook('unsubscribe',           AUTH_MDP, 'groupmember'),
 
-            '%grp/change_rights'   => $this->make_hook('change_rights',         AUTH_MDP),
+            '%grp/change_rights'   => $this->make_hook('change_rights',         AUTH_MDP, 'groups'),
             '%grp/admin/annuaire'  => $this->make_hook('admin_annuaire',        AUTH_MDP, 'groupadmin'),
             '%grp/member'          => $this->make_hook('admin_member',          AUTH_MDP, 'groupadmin'),
             '%grp/member/new'      => $this->make_hook('admin_member_new',      AUTH_MDP, 'groupadmin'),
-            '%grp/member/new/ajax' => $this->make_hook('admin_member_new_ajax', AUTH_MDP, 'user', NO_AUTH),
+            '%grp/member/new/ajax' => $this->make_hook('admin_member_new_ajax', AUTH_MDP, 'groups', NO_AUTH),
             '%grp/member/del'      => $this->make_hook('admin_member_del',      AUTH_MDP, 'groupadmin'),
             '%grp/member/suggest'  => $this->make_hook('admin_member_suggest',  AUTH_MDP, 'groupadmin'),
 
@@ -671,7 +671,7 @@ class XnetGrpModule extends PLModule
             $append = "\n"
                     . "-- \n"
                     . "Ce message a été envoyé suite à la demande d'inscription de\n"
-                    . S::user()->fullName() . ' (X' . S::v('promo') . ")\n"
+                    . S::user()->fullName(true) . "\n"
                     . "Via le site www.polytechnique.net. Tu peux choisir de valider ou\n"
                     . "de refuser sa demande d'inscription depuis la page :\n"
                     . "http://www.polytechnique.net/" . $globals->asso("diminutif") . "/subscribe/" . S::user()->login() . "\n"
index d018754..c0eaa38 100644 (file)
@@ -28,26 +28,26 @@ class XnetListsModule extends ListsModule
     function handlers()
     {
         return array(
-            '%grp/lists'              => $this->make_hook('lists',    AUTH_MDP, 'groupmember'),
-            '%grp/lists/create'       => $this->make_hook('create',   AUTH_MDP, 'groupmember'),
+            '%grp/lists'              => $this->make_hook('lists',    AUTH_MDP,    'groupmember'),
+            '%grp/lists/create'       => $this->make_hook('create',   AUTH_MDP,    'groupmember'),
 
-            '%grp/lists/members'      => $this->make_hook('members',  AUTH_COOKIE),
-            '%grp/lists/csv'          => $this->make_hook('csv',      AUTH_COOKIE),
-            '%grp/lists/annu'         => $this->make_hook('annu',     AUTH_COOKIE),
-            '%grp/lists/archives'     => $this->make_hook('archives', AUTH_COOKIE),
+            '%grp/lists/members'      => $this->make_hook('members',  AUTH_COOKIE, 'groups'),
+            '%grp/lists/csv'          => $this->make_hook('csv',      AUTH_COOKIE, 'groups'),
+            '%grp/lists/annu'         => $this->make_hook('annu',     AUTH_COOKIE, 'groups'),
+            '%grp/lists/archives'     => $this->make_hook('archives', AUTH_COOKIE, 'groups'),
             '%grp/lists/archives/rss' => $this->make_hook('rss',      AUTH_PUBLIC),
 
-            '%grp/lists/moderate'     => $this->make_hook('moderate', AUTH_MDP),
-            '%grp/lists/admin'        => $this->make_hook('admin',    AUTH_MDP),
-            '%grp/lists/options'      => $this->make_hook('options',  AUTH_MDP),
-            '%grp/lists/delete'       => $this->make_hook('delete',   AUTH_MDP),
+            '%grp/lists/moderate'     => $this->make_hook('moderate', AUTH_MDP,    'groups'),
+            '%grp/lists/admin'        => $this->make_hook('admin',    AUTH_MDP,    'groups'),
+            '%grp/lists/options'      => $this->make_hook('options',  AUTH_MDP,    'groups'),
+            '%grp/lists/delete'       => $this->make_hook('delete',   AUTH_MDP,    'groups'),
 
-            '%grp/lists/soptions'     => $this->make_hook('soptions', AUTH_MDP),
-            '%grp/lists/check'        => $this->make_hook('check',    AUTH_MDP),
-            '%grp/lists/sync'         => $this->make_hook('sync',     AUTH_MDP),
+            '%grp/lists/soptions'     => $this->make_hook('soptions', AUTH_MDP,    'groups'),
+            '%grp/lists/check'        => $this->make_hook('check',    AUTH_MDP,    'groups'),
+            '%grp/lists/sync'         => $this->make_hook('sync',     AUTH_MDP,    'groups'),
 
-            '%grp/alias/admin'        => $this->make_hook('aadmin',   AUTH_MDP, 'groupadmin'),
-            '%grp/alias/create'       => $this->make_hook('acreate',  AUTH_MDP, 'groupadmin'),
+            '%grp/alias/admin'        => $this->make_hook('aadmin',   AUTH_MDP,    'groupadmin'),
+            '%grp/alias/create'       => $this->make_hook('acreate',  AUTH_MDP,    'groupadmin'),
 
             /* hack: lists uses that */
             'profile'                 => $this->make_hook('profile',  AUTH_PUBLIC),
index b849193..7451a07 100644 (file)
@@ -26,9 +26,9 @@ class XnetNlModule extends NewsletterModule
     function handlers()
     {
         return array(
-            '%grp/nl'                   => $this->make_hook('nl',              AUTH_MDP),
-            '%grp/nl/show'              => $this->make_hook('nl_show',         AUTH_MDP),
-            '%grp/nl/search'            => $this->make_hook('nl_search',       AUTH_MDP),
+            '%grp/nl'                   => $this->make_hook('nl',              AUTH_MDP, 'groups'),
+            '%grp/nl/show'              => $this->make_hook('nl_show',         AUTH_MDP, 'groups'),
+            '%grp/nl/search'            => $this->make_hook('nl_search',       AUTH_MDP, 'groups'),
             '%grp/admin/nl'             => $this->make_hook('admin_nl',        AUTH_MDP, 'groupadmin'),
             '%grp/admin/nl/sync'        => $this->make_hook('admin_nl_sync',   AUTH_MDP, 'groupadmin'),
             '%grp/admin/nl/enable'      => $this->make_hook('admin_nl_enable', AUTH_MDP, 'groupadmin'),
index 516f019..fe40261 100644 (file)
@@ -27,6 +27,7 @@ function smarty_function_profile($params, $smarty)
     $with_link  = $params->b('link', true);
     $with_dir   = $params->b('directory', true);
     $with_groupperms = $params->b('groupperms', true);
+    $raw = $params->b('raw', true);
     $user = $params->v('user');
     if (is_int($user) || ctype_digit($user)) {
         $user = User::getWithUID($user);
@@ -37,15 +38,19 @@ function smarty_function_profile($params, $smarty)
     } else {
         $name = pl_entities($user->fullName());
     }
-    if ($with_sex && $user->isFemale()) {
-        $name = '&bull;' . $name;
-    }
     if ($with_promo) {
         $promo = $user->category();
         if ($promo) {
             $name .= ' (' . pl_entities($promo) . ')';
         }
     }
+    if ($raw) {
+        return $name;
+    }
+
+    if ($with_sex && $user->isFemale()) {
+        $name = '&bull;' . $name;
+    }
     if ($with_link) {
         $profile = ($user instanceof Profile) ? $user : $user->profile();
         if ($profile) {
index 7caa9a2..f054e5d 100644 (file)
@@ -28,26 +28,8 @@ function smarty_insert_getUsername()
         return '';
     }
 
-    $domain = Cookie::v('domain', 'login');
-    if ($domain == 'hruid') {
-        return XDB::fetchOneCell('SELECT  hruid
-                                    FROM  accounts
-                                   WHERE  uid = {?}',
-                                 $id);
-    } elseif ($domain == 'alias') {
-        return XDB::fetchOneCell('SELECT  email
-                                    FROM  email_source_account
-                                   WHERE  uid = {?} AND type = \'alias_aux\'',
-                                 $id);
-    } else {
-        return XDB::fetchOneCell('SELECT  email
-                                    FROM  email_source_account
-                                   WHERE  uid = {?} AND type != \'alias_aux\'
-                                ORDER BY  NOT FIND_IN_SET(\'bestalias\', flags), CHAR_LENGTH(email)',
-                                 $id);
-    }
-
-     return '';
+    $user = User::getSilentWithUID($id);
+    return $user->bestEmail();
 }
 
 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
index 6975a2c..9bab8e9 100644 (file)
 </h1>
 <p>
   Bonjour,<br />
-  la page que vous avez demandée
-  (<strong>{if $referer}{$smarty.server.HTTP_REFERER}{else}{$globals->baseurl}/{$platal->pl_self()}{/if}</strong>)
+  {if t($group)}
+    le site du groupe {$group}
+  {else}
+    la page que vous avez demandée
+  {/if}
+  (<strong>{if t($referer)}{$smarty.server.HTTP_REFERER|truncate:120:"...":false}{else}{$globals->baseurl}/{$platal->pl_self()}{/if}</strong>)
   nécessite une authentification.
 </p>
 {else}
     </tr>
     <tr style="white-space: nowrap">
       <td class="titre">
-        Identifiant&nbsp;:
+        Identifiant ou email&nbsp;:
       </td>
       <td>
-        <input type="text" name="username" size="20" maxlength="50" value="{insert name="getUserName"}" />
-        <select name="domain">
-          <option value="login">@alumni. {#globals.mail.domain#} / {#globals.mail.domain2#}</option>
-          <option value="alias" {if t($smarty.cookies.ORGdomain) && $smarty.cookies.ORGdomain eq "alias"}selected="selected"{/if}>
-            @ {#globals.mail.alias_dom#} / {#globals.mail.alias_dom2#}
-          </option>
-          <option value="hruid" {if t($smarty.cookies.ORGdomain) && $smarty.cookies.ORGdomain eq "ax"}selected="selected"{/if}>
-            Identifiant
-          </option>
-        </select>
+        <input type="text" name="username" size="40" maxlength="100" value="{insert name="getUserName"}" />
       </td>
     </tr>
     <tr>
     </tr>
     <tr>
       <td colspan="2">
-        <a href="recovery" style="float: left">Mot de passe perdu&nbsp;?</a>
+        <span style="float: left">
+        Mot de passe perdu&nbsp;:
+        <a href="recovery">Étudiants et diplômés de l'X</a> |
+        <a href="recovery/ext">Extérieurs</a>
+        </span>
+
         <input type="submit" name="submitbtn" value="Me connecter" style="float: right" />
       </td>
     </tr>
     <input type="hidden" name="xorpass"   value="" />
     <input type="hidden" name="username"  value="" />
     <input type="hidden" name="remember"  value="" />
-    <input type="hidden" name="domain"    value="" />
   </div>
 </form>
 
index 9b6a696..d9978f9 100644 (file)
@@ -21,7 +21,7 @@
 {**************************************************************************}
 
 <tr class="pair">
-  <td class="titre">Adresse fournie</td>
+  <td class="titre">Adresse saisie par l'utilisateur</td>
   <td>{$valid->given_text}</td>
 </tr>
 
index 0e6abcc..b08121c 100644 (file)
 {*                                                                        *}
 {**************************************************************************}
 
+<p>L'adresse saisie ci-dessous sert uniquement pour la géolocalisation ;
+utiliser le bouton <em>Éditer</em> pour visualiser sur la carte la nouvelle position de l'adresse.</p>
+<p>La case <em>Utiliser la version modifiée</em> va remplacer l'adresse saisie par l'utilisateur par l'adresse modifiée ci-dessous.
+À n'utiliser qu'en cas d'adresse manifestement invalide, ou pour corriger une faute d'orthographe.</p>
+
 {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 94ef8cf..8b8eeae 100644 (file)
@@ -35,7 +35,7 @@
       <td>raison (pour les refus)&nbsp;:
         <textarea cols='50' rows='10' name='reason' id='raison'>
 -- 
-{profile user=$smarty.session.user promo=true directory=false link=false sex=false}
+{profile user=$smarty.session.user promo=true directory=false link=false sex=false raw=true}
 </textarea>
       </td>
     </tr>
index 20b0714..07af4c0 100644 (file)
@@ -32,7 +32,7 @@
   <tr class="{cycle values="pair,impair"}">
     <td class="titre">{$nl->id}</td>
     <td>{$nl->group}</td>
-    <td>{$nl->name}</td>
+    <td><a href="http://www.polytechnique.net/{$nl->group}/admin/nl">{$nl->name}</a></td>
     <td>{if $nl->hasCustomCss()}Oui{else}Non{/if}</td>
     <td>{$nl->criteria->flags()}</td>
   </tr>
index 3e9abf3..c6a3a1a 100644 (file)
       </td>
       <td>
         {if $issue->isEditable()}
-        Le {valid_date name="send_before_date" value=$issue->getSendBeforeDate() from=3 to=15} vers {html_select_time prefix="send_before_time_" time=$issue->getSendBeforeTime() display_hours=true display_minutes=false display_seconds=false display_meridian=false use_24_hours=true} heures
+        Le {valid_date name="send_before_date" value=$issue->getSendBeforeDate() from=1 to=15} vers {html_select_time prefix="send_before_time_" time=$issue->getSendBeforeTime() display_hours=true display_minutes=false display_seconds=false display_meridian=false use_24_hours=true} heures
         {else}
         Le {$issue->send_before|date_format:"%d/%m/%Y vers %Hh"}
         {/if}
index 9898404..158a168 100644 (file)
@@ -43,10 +43,10 @@ Cette lettre est envoyée par l'AX grâce aux outils de Polytechnique.org.
 
 {if $is_mail}
 archives         : <https://www.polytechnique.org/ax>
-ne plus recevoir : <https://www.polytechnique.org/ax/out/{if $hash}{$hash}{else}nohash{/if}/issue/{$issue->id}>
+ne plus recevoir : <https://www.polytechnique.org/ax/out/{if $hash}{$hash}{else}nohash{/if}/{$issue->id}>
 {else}
 archives         : &lt;https://www.polytechnique.org/ax&gt;
-ne plus recevoir : &lt;https://www.polytechnique.org/ax/out/{if $hash}{$hash}{else}nohash{/if}/issue/{$issue->id}&gt;
+ne plus recevoir : &lt;https://www.polytechnique.org/ax/out/{if $hash}{$hash}{else}nohash{/if}/{$issue->id}&gt;
 {/if}
 
 {if !$is_mail}
index db20550..1a0a225 100644 (file)
@@ -67,7 +67,7 @@
       </tr>
       <tr>
         <td>
-          <input type="hidden" name="username" value="{$email}" />
+          <input type="hidden" name="username" value="{$hruid}" />
           <input type="hidden" name="password" value="" />
           <input type="hidden" name="domain" value="email" />
         </td>
diff --git a/templates/platal/password_recovery_xnet.mail.tpl b/templates/platal/password_recovery_xnet.mail.tpl
new file mode 100644 (file)
index 0000000..d3f0f9e
--- /dev/null
@@ -0,0 +1,37 @@
+{**************************************************************************}
+{*                                                                        *}
+{*  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               *}
+{*                                                                        *}
+{**************************************************************************}
+
+{config_load file="mails.conf" section="password_recovery_xnet"}
+{if $mail_part eq 'head'}
+{subject text="Votre certificat d'authentification"}
+{from full=#from#}
+{elseif $mail_part eq 'text'}
+
+Visitez la page suivante qui expire dans six heures :
+{$globals->baseurl}/tmpPWD/{$hash}
+
+Si en cliquant dessus vous n'y arrivez pas, copiez intégralement l'adresse dans la barre de votre navigateur. Si vous n'avez pas utilisé ce lien dans six heures, vous pouvez tout simplement recommencer cette procédure.
+
+{include file="include/signature.mail.tpl"}
+
+Email envoyé à {$email}
+{/if}
index 284ea62..0628af4 100644 (file)
@@ -43,11 +43,13 @@ $(function() {
   <form action="prefs" method="post" id="form">
   {xsrf_token_field}
   <dl>
+    {if hasPerm('user')}
     <dt>Apparence du site</dt>
     <dd>
       Tu peux changer l'apparence du site en choisissant une autre skin.<br />
       <a href="prefs/skin">Changer de skin</a>
     </dd>
+    {/if}
     <dt>Format des emails envoyés par le site</dt>
     <dd>
       Lorsque le site t'envoie des emails (lettre mensuelle, carnet, ...) ceux-ci peuvent
index f0ddda7..edcc321 100644 (file)
     </td>
     <td><input type="text" {if $errors.deathdate}class="error"{/if} name="deathdate" value="{$deathdate}" /></td>
   </tr>
+  <tr>
+    <td>
+      <span class="titre">Date de naissance de référence</span>
+    </td>
+    <td>
+    {if $birthdate_ref eq 0}
+      <input type="text" {if $errors.birthdate_ref}class="error"{/if} name="birthdate_ref" value="{$birthdate_ref}" />
+    {else}
+      {$birthdate_ref}
+    {/if}
+    </td>
+  </tr>
   {/if}
   <tr>
     <td>
index 823f592..ea76a36 100644 (file)
@@ -41,13 +41,12 @@ $($.closeOnEsc);
 
 //]]></script>
 {/literal}
-
 <div id="fiche">
   <div id="photo" class="part">
     {assign var=photo value=$profile->getPhoto(false)}
     {if $photo}<img alt="Photo de {$profile->fullName()}" src="photo/{$profile->hrid()}{if $with_pending_pic}/req{/if}" width="{$photo->width()}"/>{/if}
 
-    {if $logged && $view eq 'private' && ( $profile->section|smarty:nodefaults || $profile->getBinets()|smarty:nodefaults || ($owner && $owner->groups(true,true)|smarty:nodefaults))}
+    {if $logged && $view->isVisible(#Visibility::EXPORT_AX#) && ( $profile->section|smarty:nodefaults || $profile->getBinets()|smarty:nodefaults || ($owner && $owner->groups(true,true)|smarty:nodefaults))}
       <h2>À l'X&hellip;</h2>
       {if $profile->section}<div><em class="intitule">Section&nbsp;: </em><span>{$profile->section}</span></div>{/if}
 
@@ -55,7 +54,7 @@ $($.closeOnEsc);
       {if $binets|@count}<div><em class="intitule">Binet{if count($binets) > 1}s{/if}&nbsp;: </em>
       <span>{', '|implode:$profile->getBinetsNames()}</span></div>{/if}
 
-      {if $owner && $view eq 'private'}
+      {if $owner && $view->isVisible(#Visibility::EXPORT_AX#)}
         {assign var=groups value=$owner->groups(true,true)}
         {if $groups|@count}<div><em class="intitule">Groupe{if count($groups) > 1}s{/if} et institution{if count($groups) > 1}s{/if} X&nbsp;: </em>
         <span><br/>
@@ -68,8 +67,7 @@ $($.closeOnEsc);
 
     {/if}
 
-    {* 458752 stands for 0x70000 = Profile::NETWORKING_ALL *}
-    {assign var=networking value=$profile->getNetworking(458752)}
+    {assign var=networking value=$profile->getNetworking(#Profile::NETWORKING_ALL#)}
     {if count($networking) > 0}
       <h2>Sur le web...</h2>
       {foreach from=$networking item=network}
@@ -93,7 +91,7 @@ $($.closeOnEsc);
   <div id="fiche_identite" class="part">
     <div class="civilite">
       {if $profile->isFemale()}&bull;{/if}
-        {if $view eq 'private'}{$profile->private_name}{else}{$profile->public_name}{/if}
+        {if $view->isVisible(#Visibility::EXPORT_PRIVATE#)}{$profile->private_name}{else}{$profile->public_name}{/if}
 
       {if $logged}
         &nbsp;{if !$profile->isDead()}<a href="vcard/{$owner->login()}.vcf">{*
@@ -122,7 +120,7 @@ $($.closeOnEsc);
       {/if}
     </div>
 
-    {if $logged && $view eq 'private' && $owner && $owner->state eq 'active'}
+    {if $logged && $view->isVisible(#Visibility::EXPORT_AX#) && $owner && $owner->state eq 'active'}
     <div class='maj'>
       Fiche mise à jour<br />
       le {$profile->last_change|date_format}
@@ -131,9 +129,9 @@ $($.closeOnEsc);
 
     {* 121634816 is Profile::PHONE_LINK_PROFILE | Profile::PHONE_TYPE_ANY = 0x7400000 *}
     {assign var=phones value=$profile->getPhones(121634816)}
-    {if ($logged && $view eq 'private') || count($phones) > 0}
+    {if ($logged && $view->isVisible(#Visibility::EXPORT_AX#)) || count($phones) > 0}
     <div class="contact">
-      {if $logged && $view eq 'private'}
+      {if $logged && $view->isVisible(#Visibility::EXPORT_AX#)}
       <div class='email'>
         {if $profile->isDead()}
         Décédé{if $profile->isFemale()}e{/if} le {$profile->deathdate|date_format}
@@ -144,7 +142,7 @@ $($.closeOnEsc);
         Cette personne n'est pas inscrite à Polytechnique.org,<br />
         <a href="marketing/public/{$owner->login()}" class="popup">clique ici si tu connais son adresse email&nbsp;!</a>
         {else}
-        {if $virtualalias}
+        {if $virtualalias && $view->isVisible(#Visibility::EXPORT_PRIVATE#)}
         <a href="mailto:{$virtualalias}">{$virtualalias}</a><br />
         {/if}
         <a href="mailto:{$owner->bestEmail()}">{$owner->bestEmail()}</a>
@@ -172,7 +170,7 @@ $($.closeOnEsc);
 
       {$profile->promo('details')}
 
-      {if $logged && $view eq 'private' && $profile->mentor_expertise}
+      {if $logged && $view->isVisible(#Visibility::EXPORT_AX#) && $profile->mentor_expertise}
       [<a href="referent/{$profile->hrid()}" class='popup2'>Ma fiche référent</a>]
       {/if}
 
@@ -274,14 +272,14 @@ $($.closeOnEsc);
   </div>
   {/if}
 
-  {if $view eq 'public'}
+  {if $view->level() eq #Visibility::VIEW_PUBLIC#}
   <div class="part">
     <small>
     Cette fiche est publique et visible par tout internaute,<br />
     vous pouvez aussi voir <a href="profile/private/{$profile->hrid()}?display=light">celle&nbsp;réservée&nbsp;aux&nbsp;X</a>.
     </small>
   </div>
-  {elseif $view eq 'ax'}
+  {elseif $view->level() eq #Visibility::VIEW_AX#}
   <div class="part">
     <small>
     Cette fiche est privée et ne recense que les informations transmises à l'AX.
index 605526e..a07bb56 100644 (file)
@@ -68,7 +68,9 @@
 {if $smarty.session.user->googleapps}
 <div class="menu_item"><a href="http://gmail.polytechnique.org/">Emails Google Apps</a></div>
 {/if}
+{if hasPerm('user')}
 <div class="menu_item"><a href="lists">Listes de diffusion</a></div>
+{/if}
 {if hasPerm('payment')}
 <div class="menu_item"><a href="payment">Télépaiements</a></div>
 {/if}
diff --git a/templates/skin/group_login.tpl b/templates/skin/group_login.tpl
new file mode 100644 (file)
index 0000000..047629e
--- /dev/null
@@ -0,0 +1,62 @@
+{**************************************************************************}
+{*                                                                        *}
+{*  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=skin/common.doctype.tpl}
+    <link rel="stylesheet" type="text/css" href="css/default.css" media="all" />
+    {include file=skin/common.header.tpl}
+  </head>
+  <body>
+    {include core=plpage.devel.tpl}
+    {if !$simple}
+      {include file=skin/common.bandeau.tpl}
+    {/if}
+    {if t($smarty.session.suid)}
+    <div id="suid">
+      <a href="exit">
+        Quitter le SU sur {$smarty.session.hruid} ({$smarty.session.perms->flags()})
+      </a>
+    </div>
+    {/if}
+
+  {if $simple}
+    <div id="content">
+      {include core=plpage.content.tpl}
+    </div>
+  {else}
+    <table id="body" cellpadding="0" cellspacing="0">
+      <tr>
+        <td id="body-top">
+          <img src="images/skins/default_headlogo.jpg" alt="[ LOGO ]" style="display: block; float: left;" />
+          <img src="images/skins/default_ban.jpg" alt="[ Polytechnique.org ]" /><br />
+          <img src="images/skins/default_lesX.gif" alt="[LES X SUR LE WEB]" style="padding-top: 0.3em" />
+        </td>
+      </tr>
+      <tr>
+        <td id="content">
+          {include core=plpage.content.tpl}
+        </td>
+      </tr>
+    </table>
+  {/if}
+  </body>
+</html>
+{* vim:set et sw=2 sts=2 sws=2 enc=utf-8: *}
index 294a558..81c3b73 100644 (file)
     {assign var="has_cs" value="true"}
   {/if}
   {/iterate}
+  {if hasPerm('user')}
   <tr class="impair">
     <td colspan="3" style="text-align: right">
       {if $smarty.session.auth}<a href="survey/edit/new">{icon name=page_edit} Proposer un sondage</a>{/if}
     </td>
   </tr>
+  {/if}
 </table>
 {/if}
 
diff --git a/templates/xnet/login.tpl b/templates/xnet/login.tpl
deleted file mode 100644 (file)
index 9356cb3..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-{**************************************************************************}
-{*                                                                        *}
-{*  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               *}
-{*                                                                        *}
-{**************************************************************************}
-
-<h1>Identification</h1>
-
-<form action="{$smarty.server.REQUEST_URI}" method="post" id="login" onsubmit='doChallengeResponse(); return false;'>
-  <table class="bicol">
-    <tr>
-      <td class="titre">Identifiant (adresse email)&nbsp;:</td>
-      <td>
-        <input type="text" name="username" />
-        <input type="hidden" name="domain" value="email" />
-      </td>
-    </tr>
-    <tr>
-      <td class="titre">Mot de passe&nbsp;:</td>
-      <td><input type="password" name="password" size="10" maxlength="256" /></td>
-    </tr>
-    <tr>
-      <td {popup caption='Connexion permanente' width='300' text='Décocher cette case pour que le site oublie ce navigateur.<br />
-        Il est conseillé de décocher la case si cette machine n\'est pas <b>strictement</b> personnelle'} colspan="2">
-        <label><input type="checkbox" name="remember" checked="checked" />
-          Garder l'accès aux services après déconnexion.
-        </label>
-        <br />
-        <a href="recovery/ext">Mot de passe perdu&nbsp;?</a>
-      </td>
-    </tr>
-    <tr>
-      <td colspan="2" class="center"><input  type="submit" name="submitbtn" value="Envoyer" /></td>
-    </tr>
-  </table>
-</form>
-
-<form action="{$smarty.server.REQUEST_URI}" method="post" id="loginsub">
-  <div>
-    <input type="hidden" name="challenge" value="{$smarty.session.challenge}" />
-    <input type="hidden" name="username"  value="" />
-    <input type="hidden" name="remember"  value="" />
-    <input type="hidden" name="response"  value="" />
-    <input type="hidden" name="xorpass"   value="" /> 
-    <input type="hidden" name="domain"    value="" />
-    <input type="hidden" name="auth_type" value="xnet" />
-  </div>
-</form>
-
-{* vim:set et sw=2 sts=2 sws=2 enc=utf-8: *}
index b5ce382..523f9f6 100644 (file)
@@ -27,7 +27,7 @@
 </p>
 <p>
   Votre compte est maintenant activé et votre passe créé. Vous pouvez donc
-  profiter dès à présent des multiples fonctionnalités de Polytechnique.net.
+  profiter dès à présent des multiples fonctionnalités de <a href="http://www.polytechnique.net/">Polytechnique.net</a>.
 </p>
 <p>
   Pour rappel, votre identifiant est&nbsp;: <strong>{$email}</strong>
index b395e5e..e0ce81c 100644 (file)
             <td id="perso">
               {list_all_my_groups}
               {if !$smarty.session.auth}
-                <div>Me connecter&nbsp;:</div>
-                <table style="margin-left: 1em">
-                  <tr>
-                    <td>
-                      <a class="gp" href="login/{if $platal->pl_self() eq 'exit'}index{else}{$platal->pl_self()}{/if}">X, masters, doctorants&hellip;</a>
-                    </td>
-                  </tr>
-                  <tr>
-                    <td><a class="gp" href="login/ext">Extérieurs</a></td>
-                  </tr>
-                </table>
+                <div>
+                  <a href="login/{if $platal->pl_self() eq 'exit'}index{else}{$platal->pl_self()}{/if}">Connexion</a>
+                </div>
               {/if}
             </td>
             <td class="right" style="vertical-align: middle">
diff --git a/upgrade/1.1.3/01_marital.sql b/upgrade/1.1.3/01_marital.sql
new file mode 100644 (file)
index 0000000..111d71f
--- /dev/null
@@ -0,0 +1,5 @@
+UPDATE  email_source_account
+   SET  flags = REPLACE(flags, 'marital', 'usage')
+ WHERE  FIND_IN_SET('marital', flags);
+
+-- vim:set syntax=mysql:
diff --git a/upgrade/1.1.3/02_names.sql b/upgrade/1.1.3/02_names.sql
new file mode 100644 (file)
index 0000000..b8b452a
--- /dev/null
@@ -0,0 +1,12 @@
+UPDATE  profile_public_names
+   SET  lastname_main = REPLACE(lastname_main, '  ', ' ')
+ WHERE  lastname_main LIKE '%  %';
+
+    UPDATE  accounts             AS a
+INNER JOIN  account_profiles     AS ap  ON (ap.uid = a.uid AND FIND_IN_SET('owner', ap.perms))
+INNER JOIN  profile_public_names AS ppn ON (ppn.pid = ap.pid)
+INNER JOIN  profile_display      AS pd  ON (pd.pid = ap.pid)
+       SET  a.lastname = IF(ppn.lastname_ordinary = '', ppn.lastname_main, ppn.lastname_ordinary)
+     WHERE  a.type IN ('x', 'master', 'phd');
+
+-- vim:set syntax=mysql:
diff --git a/upgrade/1.1.3/03_visibility.sql b/upgrade/1.1.3/03_visibility.sql
new file mode 100644 (file)
index 0000000..c93f5ca
--- /dev/null
@@ -0,0 +1,24 @@
+ALTER TABLE profile_addresses CHANGE COLUMN pub pub ENUM('hidden', 'private', 'ax', 'public') NOT NULL DEFAULT 'private';
+ALTER TABLE profile_corps CHANGE COLUMN corps_pub corps_pub ENUM('hidden', 'private', 'ax', 'public') NOT NULL DEFAULT 'private';
+ALTER TABLE profile_job CHANGE COLUMN pub pub ENUM('hidden', 'private', 'ax', 'public') NOT NULL DEFAULT 'private';
+ALTER TABLE profile_job CHANGE COLUMN email_pub email_pub ENUM('hidden', 'private', 'ax', 'public') NOT NULL DEFAULT 'private';
+ALTER TABLE profile_networking CHANGE COLUMN pub pub ENUM('hidden', 'private', 'ax', 'public') NOT NULL DEFAULT 'private';
+ALTER TABLE profile_photos CHANGE COLUMN pub pub ENUM('hidden', 'private', 'ax', 'public') NOT NULL DEFAULT 'private';
+ALTER TABLE profile_phones CHANGE COLUMN pub pub ENUM('hidden', 'private', 'ax', 'public') NOT NULL DEFAULT 'private';
+ALTER TABLE profiles CHANGE COLUMN freetext_pub freetext_pub ENUM('hidden', 'private', 'ax', 'public') NOT NULL DEFAULT 'private';
+ALTER TABLE profiles CHANGE COLUMN medals_pub medals_pub ENUM('hidden', 'private', 'ax', 'public') NOT NULL DEFAULT 'private';
+ALTER TABLE profiles CHANGE COLUMN alias_pub alias_pub ENUM('hidden', 'private', 'ax', 'public') NOT NULL DEFAULT 'private';
+
+DROP TABLE IF EXISTS profile_visibility_enum;
+CREATE TABLE profile_visibility_enum (
+  access_level ENUM('admin', 'private', 'ax', 'public', 'none'),
+  best_display_level ENUM('hidden', 'private', 'ax', 'public') NULL,
+  display_levels SET('hidden', 'private', 'ax', 'public')
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+INSERT INTO profile_visibility_enum (access_level, best_display_level, display_levels) VALUES
+  ('admin', 'hidden', 'hidden,private,ax,public'),
+  ('private', 'private', 'private,ax,public'),
+  ('ax', 'ax', 'ax,public'),
+  ('public', 'public', 'public'),
+  ('none', NULL, '');
diff --git a/upgrade/1.1.3/04_pta.sql b/upgrade/1.1.3/04_pta.sql
new file mode 100644 (file)
index 0000000..cb050e7
--- /dev/null
@@ -0,0 +1,37 @@
+CREATE TABLE IF NOT EXISTS profile_partnersharing_enum (
+  id int(6) unsigned NOT NULL,
+  api_uid int(11) unsigned NULL,
+  shortname varchar(64) NOT NULL DEFAULT '',
+  name varchar(255) NOT NULL DEFAULT '',
+  url varchar(255) NOT NULL DEFAULT '',
+  default_sharing_level enum('admin', 'private', 'ax', 'public', 'none') DEFAULT 'none',
+  has_directory int(1) unsigned NOT NULL DEFAULT 0,
+  has_bulkmail int(1) unsigned NOT NULL DEFAULT 0,
+  PRIMARY KEY (id),
+  FOREIGN KEY (api_uid) REFERENCES accounts (uid) ON DELETE SET NULL ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+INSERT INTO profile_partnersharing_enum
+        SET shortname = 'pta', name = 'ParisTech Alumni', url = 'http://www.paristech-alumni.org', default_sharing_level = 'public', has_directory = 1, has_bulkmail = 1;
+
+CREATE TABLE IF NOT EXISTS profile_partnersharing_settings (
+  pid int(11) unsigned NOT NULL,
+  partner_id int(6) unsigned NOT NULL,
+  exposed_uid varchar(255) NOT NULL,
+  sharing_level enum('admin', 'private', 'ax', 'public', 'none') DEFAULT 'none',
+  allow_email enum('none', 'digest', 'direct') DEFAULT 'direct',
+  last_connection datetime NULL,
+  PRIMARY KEY (pid, partner_id),
+  KEY (partner_id, exposed_uid),
+  FOREIGN KEY (pid) REFERENCES profiles (pid) ON DELETE CASCADE ON UPDATE CASCADE,
+  FOREIGN KEY (partner_id) REFERENCES profile_partnersharing_enum (id) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
+CREATE TABLE IF NOT EXISTS profile_photo_tokens (
+  pid int(11) unsigned NOT NULL,
+  token varchar(255) NOT NULL,
+  expires datetime NOT NULL,
+  PRIMARY KEY (pid),
+  KEY (token),
+  FOREIGN KEY (pid) REFERENCES profiles (pid) ON DELETE CASCADE ON UPDATE CASCADE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
diff --git a/upgrade/1.1.3/05_accounts.sql b/upgrade/1.1.3/05_accounts.sql
new file mode 100644 (file)
index 0000000..b0b4273
--- /dev/null
@@ -0,0 +1,4 @@
+ALTER TABLE account_types
+CHANGE perms perms SET('groups','mail','directory_ax','directory_private','edit_directory','forums','lists','payment','api_user_readonly','gapps', 'user') NOT NULL DEFAULT '';
+
+UPDATE account_types SET perms = CONCAT(perms, ',user') WHERE type IN ('ax', 'fx', 'master', 'phd', 'pi', 'school', 'x');
diff --git a/upgrade/1.1.3/06_authgroupex.sql b/upgrade/1.1.3/06_authgroupex.sql
new file mode 100644 (file)
index 0000000..44d5525
--- /dev/null
@@ -0,0 +1,11 @@
+ALTER TABLE group_auth
+ADD COLUMN group_id SMALLINT(5) UNSIGNED NULL DEFAULT NULL;
+ALTER TABLE group_auth
+ADD INDEX (group_id);
+ALTER TABLE group_auth
+ADD FOREIGN KEY (group_id) REFERENCES groups (id) ON UPDATE CASCADE ON DELETE CASCADE;
+
+ALTER TABLE group_auth
+ADD COLUMN flags SET('allow_xnet', 'group_only') DEFAULT '';
+
+UPDATE group_auth SET flags = 'allow_xnet' WHERE name = 'Polytechnique.net';
diff --git a/upgrade/1.1.3/update.sh b/upgrade/1.1.3/update.sh
new file mode 100755 (executable)
index 0000000..9230e34
--- /dev/null
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+. ../inc/pervasive.sh
+
+###########################################################
+[ "$DATABASE" != "x4dat" ] || die "Cannot target x4dat"
+
+confirm "* Running database upgrade scripts"
+mysql_run_directory .