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;
}
// 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()
static public function sortByName()
{
- return array(new UFO_Name(Profile::LASTNAME), new UFO_Name(Profile::FIRSTNAME));
+ return array(new UFO_Name());
}
static public function sortByPromo()
{
- return array(new UFO_Promo(), new UFO_Name(Profile::LASTNAME), new UFO_Name(Profile::FIRSTNAME));
+ return array(new UFO_Promo(), new UFO_Name());
}
static private function getDBSuffix($string)
return $joins;
}
- /** NAMES
- */
-
- static public function assertName($name)
- {
- if (!DirEnum::getID(DirEnum::NAMETYPES, $name)) {
- Platal::page()->kill('Invalid name type: ' . $name);
- }
- }
-
- private $pn = array();
- public function addNameFilter($type, $variant = null)
- {
- $this->requireProfiles();
- if (!is_null($variant)) {
- $ft = $type . '_' . $variant;
- } else {
- $ft = $type;
- }
- $sub = '_' . $ft;
- self::assertName($ft);
-
- if (!is_null($variant) && $variant == 'other') {
- $sub .= $this->option++;
- }
- $this->pn[$sub] = DirEnum::getID(DirEnum::NAMETYPES, $ft);
- return $sub;
- }
-
- protected function nameJoins()
- {
- $joins = array();
- foreach ($this->pn as $sub => $type) {
- $joins['pn' . $sub] = PlSqlJoin::left('profile_name', '$ME.pid = $PID AND $ME.typeid = {?}', $type);
- }
- return $joins;
- }
-
/** NAMETOKENS
*/
private $name_tokens = array();
/** ADDRESSES
*/
- private $with_pa = false;
- public function addAddressFilter()
+ private $types = array();
+ public function addAddressFilter($type)
{
$this->requireProfiles();
$this->with_pa = true;
- return 'pa';
- }
-
- private $with_pac = false;
- public function addAddressCountryFilter()
- {
- $this->requireProfiles();
- $this->addAddressFilter();
- $this->with_pac = true;
- return 'gc';
- }
- private $with_pal = false;
- public function addAddressLocalityFilter()
- {
- $this->requireProfiles();
- $this->addAddressFilter();
- $this->with_pal = true;
- return 'gl';
+ $sub = '_' . $this->option++;
+ $this->types[$type] = $sub;
+ return $sub;
}
protected function addressJoins()
{
$joins = array();
- if ($this->with_pa) {
- $joins['pa'] = PlSqlJoin::left('profile_addresses', '$ME.pid = $PID');
- }
- if ($this->with_pac) {
- $joins['gc'] = PlSqlJoin::left('geoloc_countries', '$ME.iso_3166_1_a2 = pa.countryID');
- }
- if ($this->with_pal) {
- $joins['gl'] = PlSqlJoin::left('geoloc_localities', '$ME.id = pa.localityID');
+ foreach ($this->types as $type => $sub) {
+ $joins['pa' . $sub] = PlSqlJoin::inner('profile_addresses', '$ME.pid = $PID');
+ $joins['pac' . $sub] = PlSqlJoin::inner('profile_addresses_components',
+ '$ME.pid = pa' . $sub . '.pid AND $ME.jobid = pa' . $sub . '.jobid AND $ME.groupid = pa' . $sub . '.groupid AND $ME.type = pa' . $sub . '.type AND $ME.id = pa' . $sub . '.id');
+ $joins['pace' . $sub] = PlSqlJoin::inner('profile_addresses_components_enum',
+ '$ME.id = pac' . $sub . '.component_id AND FIND_IN_SET({?}, $ME.types)', $type);
}
+
return $joins;
}
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