More filters:
[platal.git] / classes / userfilter.php
index c19e338..00f9ce9 100644 (file)
@@ -21,6 +21,9 @@
 
 interface UserFilterCondition
 {
+    const COND_TRUE  = 'TRUE';
+    const COND_FALSE = 'FALSE';
+
     /** Check that the given user matches the rule.
      */
     public function buildCondition(UserFilter &$uf);
@@ -30,6 +33,13 @@ abstract class UFC_OneChild implements UserFilterCondition
 {
     protected $child;
 
+    public function __construct($child = null)
+    {
+        if (!is_null($child) && ($child instanceof UserFilterCondition)) {
+            $this->setChild($child);
+        }
+    }
+
     public function setChild(UserFilterCondition &$cond)
     {
         $this->child =& $cond;
@@ -40,17 +50,38 @@ abstract class UFC_NChildren implements UserFilterCondition
 {
     protected $children = array();
 
+    public function __construct()
+    {
+        $children = func_get_args();
+        foreach ($children as &$child) {
+            if (!is_null($child) && ($child instanceof UserFilterCondition)) {
+                $this->addChild($child);
+            }
+        }
+    }
+
     public function addChild(UserFilterCondition &$cond)
     {
         $this->children[] =& $cond;
     }
+
+    protected function catConds(array $cond, $op, $fallback)
+    {
+        if (count($cond) == 0) {
+            return $fallback;
+        } else if (count($cond) == 1) {
+            return $cond[0];
+        } else {
+            return '(' . implode(') ' . $op . ' (', $cond) . ')';
+        }
+    }
 }
 
 class UFC_True implements UserFilterCondition
 {
     public function buildCondition(UserFilter &$uf)
     {
-        return 'TRUE';
+        return self::COND_TRUE;
     }
 }
 
@@ -58,7 +89,7 @@ class UFC_False implements UserFilterCondition
 {
     public function buildCondition(UserFilter &$uf)
     {
-        return 'FALSE';
+        return self::COND_FALSE;
     }
 }
 
@@ -66,7 +97,14 @@ class UFC_Not extends UFC_OneChild
 {
     public function buildCondition(UserFilter &$uf)
     {
-        return 'NOT (' . $this->child->buildCondition($uf) . ')';
+        $val = $this->child->buildCondition($uf);
+        if ($val == self::COND_TRUE) {
+            return self::COND_FALSE;
+        } else if ($val == self::COND_FALSE) {
+            return self::COND_TRUE;
+        } else {
+            return 'NOT (' . $val . ')';
+        }
     }
 }
 
@@ -75,13 +113,21 @@ class UFC_And extends UFC_NChildren
     public function buildCondition(UserFilter &$uf)
     {
         if (empty($this->children)) {
-            return 'FALSE';
+            return self::COND_FALSE;
         } else {
+            $true = self::COND_FALSE;
             $conds = array();
             foreach ($this->children as &$child) {
-                $conds[] = $child->buildCondition($uf);
+                $val = $child->buildCondition($uf);
+                if ($val == self::COND_TRUE) {
+                    $true = self::COND_TRUE;
+                } else if ($val == self::COND_FALSE) {
+                    return self::COND_FALSE;
+                } else {
+                    $conds[] = $val;
+                }
             }
-            return '(' . implode (') AND (', $conds) . ')';
+            return $this->catConds($conds, 'AND', $true);
         }
     }
 }
@@ -91,13 +137,21 @@ class UFC_Or extends UFC_NChildren
     public function buildCondition(UserFilter &$uf)
     {
         if (empty($this->children)) {
-            return 'TRUE';
+            return self::COND_TRUE;
         } else {
+            $true = self::COND_TRUE;
             $conds = array();
             foreach ($this->children as &$child) {
-                $conds[] = $child->buildCondition($uf);
+                $val = $child->buildCondition($uf);
+                if ($val == self::COND_TRUE) {
+                    return self::COND_TRUE;
+                } else if ($val == self::COND_FALSE) {
+                    $true = self::COND_FALSE;
+                } else {
+                    $conds[] = $val;
+                }
             }
-            return '(' . implode (') OR (', $conds) . ')';
+            return $this->catConds($conds, 'OR', $true);
         }
     }
 }
@@ -184,11 +238,95 @@ class UFC_Name implements UserFilterCondition
     }
 }
 
+class UFC_Dead implements UserFilterCondition
+{
+    private $dead;
+    public function __construct($dead)
+    {
+        $this->dead = $dead;
+    }
+
+    public function buildCondition(UserFilter &$uf)
+    {
+        if ($this->dead) {
+            return 'p.deathdate IS NOT NULL';
+        } else {
+            return 'p.deathdate IS NULL';
+        }
+    }
+}
+
+class UFC_Registered implements UserFilterCondition
+{
+    private $active;
+    public function __construct($active = false)
+    {
+        $this->only_active = $active;
+    }
+
+    public function buildCondition(UserFilter &$uf)
+    {
+        if ($this->active) {
+            return 'a.uid IS NOT NULL AND a.state = \'active\'';
+        } else {
+            return 'a.uid IS NOT NULL AND a.state != \'pending\'';
+        }
+    }
+}
+
+class UFC_Sex implements UserFilterCondition
+{
+    private $sex;
+    public function __construct($sex)
+    {
+        $this->sex = $sex;
+    }
+
+    public function buildCondition(UserFilter &$uf)
+    {
+        if ($this->sex != User::GENDER_MALE && $this->sex != User::GENDER_FEMALE) {
+            return self::COND_FALSE;
+        } else {
+            return XDB::format('p.sex = {?}', $this->sex);
+        }
+    }
+}
+
+class UFC_Group implements UserFilterCondition
+{
+    private $group;
+    private $admin;
+    public function __construct($group, $admin = false)
+    {
+        $this->group = $group;
+        $this->admin = $admin;
+    }
+
+    public function buildCondition(UserFilter &$uf)
+    {
+        $sub = $uf->addGroupFilter($this->group);
+        $where = 'gpm' . $sub . '.perms IS NOT NULL';
+        if ($this->admin) {
+            $where .= ' AND gpm' . $sub . '.perms = \'admin\'';
+        }
+        return $where;
+    }
+}
+
 class UserFilter
 {
     private $root;
     private $query = null;
 
+    public function __construct($cond = null)
+    {
+        if (!is_null($cond)) {
+            if ($cond instanceof UserFilterCondition) {
+                $this->setCondition($cond);
+            }
+        }
+    }
+
     private function buildQuery()
     {
         if (is_null($this->query)) {
@@ -217,7 +355,7 @@ class UserFilter
             }
             $str .= $table . ' AS ' . $key;
             if (isset($infos[2])) {
-                $str .= ' ON (' . str_replace(array('$ME', '$PID'), array($key, 'p.pid'), $infos[2]) . ')';
+                $str .= ' ON (' . str_replace(array('$ME', '$PID', '$UID'), array($key, 'p.pid', 'a.uid'), $infos[2]) . ')';
             }
             $str .= "\n";
         }
@@ -226,7 +364,7 @@ class UserFilter
 
     private function buildJoins()
     {
-        $joins = $this->educationJoins() + $this->nameJoins();
+        $joins = $this->educationJoins() + $this->nameJoins() + $this->groupJoins();
         return $this->formatJoin($joins);
     }
 
@@ -244,15 +382,36 @@ class UserFilter
      */
     public function filter(array $users)
     {
+        $this->buildQuery();
+        $table = array();
+        $uids  = array();
+        foreach ($users as $user) {
+            $uids[] = $user->id();
+            $table[$user->id()] = $user;
+        }
+        $fetched = XDB::fetchColumn('SELECT  a.uid
+                                    ' . $this->query . ' AND a.uid IN (' . implode(', ', $uids) . ')
+                                   GROUP BY  a.uid');
         $output = array();
-        foreach ($users as &$user) {
-            if ($this->checkUser($user)) {
-                $output[] = $user;
-            }
+        foreach ($fetched as $uid) {
+            $output[] = $table[$uid];
         }
         return $output;
     }
 
+    public function getUIDs()
+    {
+        $this->buildQuery();
+        return XDB::fetchColumn('SELECT  a.uid
+                                ' . $this->query . '
+                               GROUP BY  a.uid');
+    }
+
+    public function getUsers()
+    {
+        return User::getBuildUsersWithUIDs($this->getUIDs());
+    }
+
     public function setCondition(UserFilterCondition &$cond)
     {
         $this->root =& $cond;
@@ -261,28 +420,17 @@ class UserFilter
 
     static public function getLegacy($promo_min, $promo_max)
     {
-        $min = null;
         if ($promo_min != 0) {
             $min = new UFC_Promo('>=', self::GRADE_ING, intval($promo_min));
+        } else {
+            $min = new UFC_True();
         }
-        $max = null;
         if ($promo_max != 0) {
             $max = new UFC_Promo('<=', self::GRADE_ING, intval($promo_max));
-        }
-        $uf = new UserFilter();
-        if (is_null($max) && is_null($min)) {
-            $uf->setCondition(new UFC_True());
-        } else if (is_null($max)) {
-            $uf->setCondition($min);
-        } else if (is_null($min)) {
-            $uf->setCondition($max);
         } else {
-            $cond = new UFC_And();
-            $cond->addChild($min);
-            $cond->addChild($max);
-            $uf->setCondition($cond);
+            $max = new UFC_True();
         }
-        return $uf;
+        return new UserFilter(new UFC_And($min, $max));
     }
 
 
@@ -395,6 +543,46 @@ class UserFilter
         }
         return $joins;
     }
+
+
+    /** GROUPS
+     */
+    private $gpm = array();
+    private $gpm_o = 0;
+    public function addGroupFilter($group = null)
+    {
+        if (!is_null($group)) {
+            if (ctype_digit($group)) {
+                $index = $sub = $group;
+            } else {
+                $index = $group;
+                $sub   = preg_replace('/[^a-z0-9]/i', '', $group);
+            }
+        } else {
+            $sub = 'group_' . $this->gpm_o++;
+            $index = null;
+        }
+        $sub = '_' . $sub;
+        $this->gpm[$sub] = $index;
+        return $sub;
+    }
+
+    private function groupJoins()
+    {
+        $joins = array();
+        foreach ($this->gpm as $sub => $key) {
+            if (is_null($key)) {
+                $joins['gpa' . $sub] = array('inner', 'groupex.asso');
+                $joins['gpm' . $sub] = array('left', 'groupex.membres', '$ME.uid = $UID AND $ME.asso_id = gpa' . $sub . '.id');
+            } else if (ctype_digit($key)) {
+                $joins['gpm' . $sub] = array('left', 'groupex.membres', '$ME.uid = $UID AND $ME.asso_id = ' . $key);
+            } else {
+                $joins['gpa' . $sub] = array('inner', 'groupex.asso', XDB::format('$ME.diminutif = {?}', $key));
+                $joins['gpm' . $sub] = array('left', 'groupex.membres', '$ME.uid = $UID AND $ME.asso_id = gpa' . $sub . '.id');
+            }
+        }
+        return $joins;
+    }
 }
 
 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8: