Refactor Visibility to use a database instead of IN ({?}) queries.
authorRaphaël Barrois <raphael.barrois@polytechnique.org>
Tue, 5 Jul 2011 21:41:15 +0000 (23:41 +0200)
committerRaphaël Barrois <raphael.barrois@polytechnique.org>
Sat, 23 Jul 2011 15:05:38 +0000 (17:05 +0200)
Signed-off-by: Raphaël Barrois <raphael.barrois@polytechnique.org>
16 files changed:
classes/address.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]
include/profilefields.inc.php
include/userset.inc.php
include/vcard.inc.php
modules/profile.php
modules/profile/jobs.inc.php
modules/profile/page.inc.php
templates/profile/profile.tpl
upgrade/1.1.2/06_visibility.sql

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 4a16ca9..0cb0703 100644 (file)
@@ -333,17 +333,17 @@ class Phone
             $success = (!$phone->error && ($phone->format() || $phone->isEmpty()) && $success);
             if (!$phone->isEmpty()) {
                 // Restrict phone visibility to $maxPublicity
-                if (!is_null($maxPublicity) && $maxPublicity->isVisible($phone->pub)) {
-                    $phone->pub = $maxPublicity->level();
+                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)) {
+            if (!is_null($maxPublicity) && Visibility::isLessRestrictive($phone->pub, $maxPublicity)) {
                 // Restrict phone visibility to $maxPublicity
-                $phone->pub = $maxPublicity->level();
+                $phone->pub = $maxPublicity;
             }
             $phones[] = call_user_func(array($phone, $function));
         }
@@ -354,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;
     }
 
@@ -374,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);
     }
 }
 
@@ -391,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) {
@@ -403,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 b5456fe..6b06231 100644 (file)
@@ -160,14 +160,11 @@ class Profile implements PlExportable
     private $visibility = null;
 
 
-    private function __construct(array $data, ProfileVisibility $visibility = null)
+    private function __construct(array $data, Visibility $visibility)
     {
         $this->data = $data;
         $this->pid = $this->data['pid'];
         $this->hrpid = $this->data['hrpid'];
-        if ($visibility == null) {
-            $visibility = ProfileVisibility::defaultForRead();
-        }
         $this->visibility = $visibility;
     }
 
@@ -387,7 +384,7 @@ class Profile implements PlExportable
     public function displayEmail()
     {
         $o = $this->owner();
-        if ($o != null && $this->isVisible(ProfileVisibility::VIS_PRIVATE)) {
+        if ($o != null && $this->isVisible(Visibility::EXPORT_PRIVATE)) {
             return $o->bestEmail();
         } else {
             return $this->email_directory;
@@ -877,7 +874,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());
@@ -887,7 +884,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)
@@ -953,7 +950,7 @@ class Profile implements PlExportable
         );
     }
 
-    private static function fetchProfileData(array $pids, $respect_order = true, $fields = 0x0000, ProfileVisibility $visibility = null)
+    private static function fetchProfileData(array $pids, $respect_order = true, $fields = 0x0000, $visibility = null)
     {
         if (count($pids) == 0) {
             return null;
@@ -965,8 +962,8 @@ class Profile implements PlExportable
             $order = '';
         }
 
-        if ($visibility == null) {
-            $visibility = ProfileVisibility::defaultForRead();
+        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,
@@ -1002,13 +999,13 @@ class Profile implements PlExportable
                               WHERE  p.pid IN {?}
                            GROUP BY  p.pid
                                      ' . $order,
-                           $visibility->isVisible(ProfileVisibility::VIS_PRIVATE), // CV
+                           $visibility->isVisible(Visibility::EXPORT_PRIVATE), // CV
                            $visibility->level(), // freetext
-                           $visibility->isVisible(ProfileVisibility::VIS_PRIVATE), // section
-                           $visibility->isVisible(ProfileVisibility::VIS_PRIVATE), // nickname
+                           $visibility->isVisible(Visibility::EXPORT_PRIVATE), // section
+                           $visibility->isVisible(Visibility::EXPORT_PRIVATE), // nickname
                            $visibility->level(), // mobile
                            $visibility->level(), // photo
-                           $visibility->isVisible(ProfileVisibility::VIS_PRIVATE), // deltaten_message
+                           $visibility->isVisible(Visibility::EXPORT_PRIVATE), // deltaten_message
                            $pids
                        );
         return new ProfileIterator($it, $pids, $fields, $visibility);
@@ -1048,10 +1045,10 @@ class Profile implements PlExportable
 
     /** Return the profile associated with the given login.
      */
-    public static function get($login, $fields = 0x0000, ProfileVisibility $visibility = null)
+    public static function get($login, $fields = 0x0000, $visibility = null)
     {
-        if ($visibility == null) {
-            $visibility = ProfileVisibility::defaultForRead();
+        if ($visibility === null) {
+            $visibility = Visibility::defaultForRead();
         }
 
         if (is_array($login)) {
@@ -1068,26 +1065,26 @@ 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;
         }
     }
 
-    public static function iterOverUIDs($uids, $respect_order = true, $fields = 0x0000, ProfileVisibility $visibility = null)
+    public static function iterOverUIDs($uids, $respect_order = true, $fields = 0x0000, $visibility = null)
     {
         return self::iterOverPIDs(self::getPIDsFromUIDs($uids), $respect_order, $fields, $visibility);
     }
 
-    public static function iterOverPIDs($pids, $respect_order = true, $fields = 0x0000, ProfileVisibility $visibility = null)
+    public static function iterOverPIDs($pids, $respect_order = true, $fields = 0x0000, $visibility = null)
     {
         return self::fetchProfileData($pids, $respect_order, $fields, $visibility);
     }
 
     /** Return profiles for the list of pids.
      */
-    public static function getBulkProfilesWithPIDs(array $pids, $fields = 0x0000, ProfileVisibility $visibility = null)
+    public static function getBulkProfilesWithPIDs(array $pids, $fields = 0x0000, $visibility = null)
     {
         if (count($pids) == 0) {
             return array();
@@ -1102,7 +1099,7 @@ class Profile implements PlExportable
 
     /** Return profiles for uids.
      */
-    public static function getBulkProfilesWithUIDS(array $uids, $fields = 0x000, ProfileVisibility $visibility = null)
+    public static function getBulkProfilesWithUIDS(array $uids, $fields = 0x000, $visibility = null)
     {
         if (count($uids) == 0) {
             return array();
@@ -1270,12 +1267,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 = ProfileVisibility::defaultForRead();
+        if ($visibility === null) {
+            $visibility = Visibility::defaultForRead();
         }
 
         $this->fields = $fields;
@@ -1306,7 +1303,7 @@ class ProfileIterator implements PlIterator
 
     private function fillProfile(array $vals)
     {
-        $pf = Profile::get($vals[0], $this->visibility);
+        $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 1dd8d6c..0000000
+++ /dev/null
@@ -1,124 +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
-{
-    /** Visibility levels.
-     * none => Can't see anything
-     * public => Can see public data
-     * ax => Can see AX and public data
-     * private => Can see private, AX and public data
-     * hidden => Can only be seen by admins
-     */
-    const VIS_NONE    = 'none';
-    const VIS_PUBLIC  = 'public';
-    const VIS_AX      = 'ax';
-    const VIS_PRIVATE = 'private';
-    const VIS_HIDDEN  = 'hidden';
-
-    private $level;
-
-    static private $v_levels = array(
-        self::VIS_NONE      => 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),
-        self::VIS_HIDDEN    => array(self::VIS_HIDDEN, self::VIS_PRIVATE, self::VIS_AX, self::VIS_PUBLIC),
-    );
-
-    public function __construct($level = null)
-    {
-        $this->level = $level;
-    }
-
-    public function level()
-    {
-        if ($this->level == null) {
-            return self::VIS_PUBLIC;
-        } else {
-            return $this->level;
-        }
-    }
-
-    public static function defaultForRead($max_level = null)
-    {
-        if (!S::logged()) {
-            $vis = new ProfileVisibility(self::VIS_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 = new ProfileVisibility(self::VIS_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 ProfileVisibility.
-     *
-     * @param $level The visibility level to restrict to
-     * @return A new ProfileVisibility instance, whose level is min($this->level, $level)
-     */
-    public function restrict($level = null)
-    {
-        if ($level != null && !$this->isVisible($level)) {
-            $level = $this->level();
-        }
-
-        return new ProfileVisibility($level);
-    }
-
-    public function levels()
-    {
-        return self::$v_levels[$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 b7ac5f2..8647ddd 100644 (file)
@@ -236,17 +236,17 @@ class User extends PlUser
      */
     public function readVisibility()
     {
-        $level = ProfileVisibility::VIS_NONE;
+        $level = Visibility::VIEW_NONE;
         if ($this->is_admin) {
-            $level = ProfileVisibility::VIS_HIDDEN;
+            $level = Visibility::VIEW_ADMIN;
         } elseif ($this->checkPerms('directory_private')) {
-            $level = ProfileVisibility::VIS_PRIVATE;
+            $level = Visibility::VIEW_PRIVATE;
         } elseif ($this->checkPerms('directory_ax')) {
-            $level = ProfileVisibility::VIS_AX;
+            $level = Visibility::VIEW_AX;
         } else {
-            $level = ProfileVisibility::VIS_PUBLIC;
+            $level = Visibility::VIEW_PUBLIC;
         }
-        return new ProfileVisibility($level);
+        return Visibility::get($level);
     }
 
     /** Retrieve the 'general' edit visibility.
@@ -254,23 +254,18 @@ class User extends PlUser
      *
      * Rules are:
      *  - Only admins can edit the 'hidden' fields
-     *  - If someone has 'directory_edit' and 'directory_ax': AX level
-     *  - If someone has 'directory_edit' and 'directory_private': Private level
+     *  - If someone has 'directory_edit' (which is actually directory_ax_edit): AX level
      *  - Otherwise, nothing.
      */
     public function editVisibility()
     {
-        $level = ProfileVisibility::VIS_NONE;
+        $level = Visibility::VIEW_NONE;
         if ($this->is_admin) {
-            $level = ProfileVisibility::VIS_HIDDEN;
+            $level = Visibility::VIEW_ADMIN;
         } elseif ($this->checkPerms('directory_edit')) {
-            if ($this->checkPerms('directory_ax')) {
-                $level = ProfileVisibility::VIS_AX;
-            } elseif ($this->checkPerms('directory_private')) {
-                $level = ProfileVisibility::VIS_PRIVATE;
-            }
+            $level = Visibility::VIEW_AX;
         }
-        return new ProfileVisibility($level);
+        return Visibility::get($level);
     }
 
     // We do not want to store the password in the object.
@@ -368,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->visibility->equals($visibility)) {
+            return Profile::get($this, $fields, $visibility);
         }
         return $this->_profile;
     }
index d392073..2314915 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,29 +119,50 @@ class UserFilter extends PlFilter
         }
 
         // This will set the visibility to the default correct level.
-        $this->profile_visibility = ProfileVisibility::defaultForRead();
+        $this->visibility = Visibility::defaultForRead();
     }
 
-    public function isVisible($level)
-    {
-        return $this->profile_visibility->isVisible($level);
-    }
-
-    public function getVisibilityLevel()
-    {
-        return $this->profile_visibility->level();
-    }
-
-    public function getVisibilityLevels()
-    {
-        return $this->profile_visibility->levels();
+    /** 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)
     {
-        // Fields are ordered as ('hidden', 'private', 'ax', 'public', 'none')
-        // Which gives 'ax' >= 'private'
-        return XDB::format($field . ' IN {?}', $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()
@@ -395,12 +418,12 @@ class UserFilter extends PlFilter
         return User::iterOverUIDs($this->getUIDs($limit));
     }
 
-    public function getProfiles($limit = null, $fields = 0x0000, ProfileVisibility $visibility = null)
+    public function getProfiles($limit = null, $fields = 0x0000, $visibility = null)
     {
         return Profile::getBulkProfilesWithPIDs($this->getPIDs($limit), $fields, $visibility);
     }
 
-    public function getProfile($pos = 0, $fields = 0x0000, ProfileVisibility $visibility = null)
+    public function getProfile($pos = 0, $fields = 0x0000, $visibility = null)
     {
         $pid = $this->getPID($pos);
         if ($pid == null) {
@@ -410,7 +433,7 @@ class UserFilter extends PlFilter
         }
     }
 
-    public function iterProfiles($limit = null, $fields = 0x0000, ProfileVisibility $visibility = null)
+    public function iterProfiles($limit = null, $fields = 0x0000, $visibility = null)
     {
         return Profile::iterOverPIDs($this->getPIDs($limit), true, $fields, $visibility);
     }
@@ -620,6 +643,36 @@ class UserFilter extends PlFilter
         return $joins;
     }
 
+    /** 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;
+    }
+
+    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;
+    }
+
     /** PERMISSIONS
      */
     private $at = false;
index 9c361c1..351ad2a 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,9 +668,7 @@ class UFC_NameTokens extends UserFilterCondition
             if ($this->general_type) {
                 $c .= XDB::format(' AND ' . $sub . '.general_type = {?}', $this->general_type);
             }
-            if (!$uf->isVisible(ProfileVisibility::VIS_PRIVATE)) {
-                $c .= XDB::format(' AND ' . $sub . '.general_type != \'nickname\'');
-            }
+            $c .= ' AND (' . $uf->getVisibilityConditionAbsolute(Visibility::EXPORT_PRIVATE) . ' OR ' . $sub . '.general_type != \'nickname\')';
             $conds[] = $c;
         }
 
@@ -922,12 +920,9 @@ class UFC_Binet extends UserFilterCondition
 
     public function buildCondition(PlFilter $uf)
     {
-        // Binets are private.
-        if (!$uf->isVisible(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);
     }
 }
 // }}}
@@ -947,11 +942,8 @@ class UFC_Section extends UserFilterCondition
     public function buildCondition(PlFilter $uf)
     {
         // Sections are private.
-        if (!$uf->isVisible(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);
     }
 }
 // }}}
@@ -1109,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);
@@ -1152,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;
     }
 }
@@ -1186,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;
     }
 }
@@ -1224,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;
     }
 }
@@ -1254,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);
     }
 }
@@ -1281,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->isVisible(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->isVisible(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) . ' )';
     }
 }
 // }}}
@@ -1322,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);
@@ -1367,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) {
@@ -1403,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) {
@@ -1421,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');
     }
 }
 // }}}
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 8d4ef1b..6bcf258 100644 (file)
@@ -50,12 +50,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 +67,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 +77,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 +91,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];
@@ -333,7 +333,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 +368,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 +398,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 +455,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 +468,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 +490,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 +536,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 +587,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 +607,16 @@ 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
+                                       IF(pve.best_display_level + 0 <= email_pub + 0, email, NULL) AS user_email
                                  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 +690,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 +707,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
@@ -753,7 +758,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);
             }
index 75c432d..62b5a02 100644 (file)
@@ -424,7 +424,7 @@ class AddressesView implements PlView
     public function apply(PlPage $page)
     {
         $pids = $this->set->getIds(new PlLimit());
-        $visibility = ProfileVisibility::defaultForRead(ProfileVisibility::VIS_AX);
+        $visibility = Visibility::defaultForRead(Visibility::VIEW_AX);
         pl_cached_content_headers('text/x-csv', 1);
 
         $csv = fopen('php://output', 'w');
index ee6ee0c..b544ef1 100644 (file)
@@ -30,7 +30,7 @@ class VCard extends PlVCard
     public function __construct($photos = true, $freetext = null)
     {
         PlVCard::$folding = false;
-        $this->visibility = ProfileVisibility::defaultForRead(ProfileVisibility::VIS_PRIVATE);
+        $this->visibility = Visibility::defaultForRead(Visibility::VIEW_PRIVATE);
         $this->freetext = $freetext;
         $this->photos   = $photos;
     }
index b9c5698..6e606af 100644 (file)
@@ -225,13 +225,13 @@ class ProfileModule extends PLModule
 
         // Determines the access level at which the profile will be displayed.
         if (Env::v('view') == 'public') {
-            $view = ProfileVisibility::VIS_PUBLIC;
+            $view = Visibility::VIEW_PUBLIC;
         } else if (Env::v('view') == 'ax') {
-            $view = ProfileVisibility::VIS_AX;
+            $view = Visibility::VIEW_AX;
         } else {
-            $view = ProfileVisibility::VIS_PRIVATE;
+            $view = Visibility::VIEW_PRIVATE;
         }
-        $vis = ProfileVisibility::defaultForRead($view);
+        $visibility = Visibility::defaultForRead($view);
 
         // Display pending picture
         if (S::logged() && Env::v('modif') == 'new') {
@@ -242,7 +242,7 @@ class ProfileModule extends PLModule
         if (is_null($pid)) {
             $owner = User::getSilent($id);
             if ($owner) {
-                $profile = $owner->profile(true, Profile::FETCH_ALL, $vis);
+                $profile = $owner->profile(true, Profile::FETCH_ALL, $visibility);
                 if ($profile) {
                     $pid = $profile->id();
                 }
@@ -250,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, $vis);
+            $profile = Profile::get($pid, Profile::FETCH_ALL, $visibility);
             $owner = $profile->owner();
         }
         if (is_null($pid)) {
@@ -259,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);
@@ -278,7 +279,7 @@ class ProfileModule extends PLModule
 
         $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 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 13004b4..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 && $profile->isVisible(#ProfileVisibility::VIS_AX#) && ( $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 && $profile->isVisible(#ProfileVisibility::VIS_AX#)}
+      {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/>
@@ -92,7 +91,7 @@ $($.closeOnEsc);
   <div id="fiche_identite" class="part">
     <div class="civilite">
       {if $profile->isFemale()}&bull;{/if}
-        {if $profile->isVisible(#ProfileVisibility::VIS_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">{*
@@ -121,7 +120,7 @@ $($.closeOnEsc);
       {/if}
     </div>
 
-    {if $logged && $profile->isVisible(#ProfileVisibility::VIS_AX#) && $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}
@@ -130,9 +129,9 @@ $($.closeOnEsc);
 
     {* 121634816 is Profile::PHONE_LINK_PROFILE | Profile::PHONE_TYPE_ANY = 0x7400000 *}
     {assign var=phones value=$profile->getPhones(121634816)}
-    {if ($logged && $profile->isVisible(#ProfileVisibility::VIS_AX#)) || count($phones) > 0}
+    {if ($logged && $view->isVisible(#Visibility::EXPORT_AX#)) || count($phones) > 0}
     <div class="contact">
-      {if $logged && $profile->isVisible(#ProfileVisibility::VIS_AX#)}
+      {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}
@@ -143,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 && $profile->isVisible(#ProfileVisibility::VIS_PRIVATE#)}
+        {if $virtualalias && $view->isVisible(#Visibility::EXPORT_PRIVATE#)}
         <a href="mailto:{$virtualalias}">{$virtualalias}</a><br />
         {/if}
         <a href="mailto:{$owner->bestEmail()}">{$owner->bestEmail()}</a>
@@ -171,7 +170,7 @@ $($.closeOnEsc);
 
       {$profile->promo('details')}
 
-      {if $logged && $profile->isVisible(#ProfileVisibility::VIS_AX#) && $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}
 
@@ -273,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 f0dbffe..c93f5ca 100644 (file)
@@ -1,5 +1,24 @@
-ALTER TABLE profile_addresses CHANGE COLUMN pub pub ENUM('hidden', 'private', 'ax', 'public', 'none') NOT NULL DEFAULT 'private';
-ALTER TABLE profile_corps CHANGE COLUMN pub pub ENUM('hidden', 'private', 'ax', 'public', 'none') NOT NULL DEFAULT 'private';
-ALTER TABLE profile_job CHANGE COLUMN pub pub ENUM('hidden', 'private', 'ax', 'public', 'none') NOT NULL DEFAULT 'private';
-ALTER TABLE profile_job CHANGE COLUMN email_pub email_pub ENUM('hidden', 'private', 'ax', 'public', 'none') NOT NULL DEFAULT 'private';
-ALTER TABLE profile_phone CHANGE COLUMN pub pub ENUM('hidden', 'private', 'ax', 'public', 'none') NOT NULL DEFAULT 'private';
+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, '');