Ticket #132: usergroup.2.php

File usergroup.2.php, 12.7 kB (added by skippy, 11 months ago)

revised version of usergroup.php with new methods and better logic

Line 
1<?php
2/**
3* Habari UserGroup Class
4* @package Habari
5**/
6class UserGroup extends QueryRecord
7{
8    /**
9     * Static storage for this group's info
10    **/
11    // these first three hold the original values as fetched from the DB
12    // These are associative arrays where the key and value are the same.
13    // This allows us to use isset() for various checks, rather than
14    // in_array(), and it allows us to avoid some array iterations
15    private $db_member_ids= null;
16    private $db_permissions_granted= null;
17    private $db_permissons_denied= null;
18
19    // these next three hold changes before they're committed to the DB
20    private $member_ids= null;
21    private $permissions_granted= null;
22    private $permissons_denied= null;
23    private $permissions_revoked= null;
24    private $toggle_permissions= null;
25
26    /**
27     * get default fields for this record
28     * @return array an array of the fields used in the UserGroup table
29    **/
30    public static function default_fields()
31    {
32        return array(
33            'id' => '',
34            'name' => ''
35        );
36    }
37
38    /**
39     * Constructor for the UserGroup class
40     * @param array $paramarray an associative array of UserGroup fields
41    **/
42    public function __construct( $paramarray= array() )
43    {
44        $this->fields= array_merge(
45            self::default_fields(),
46            $this->fields );
47        parent::__construct( $paramarray );
48        $this->exclude_fields('id');
49
50        // set up default, empty properties
51        $this->permissions_revoked= array();
52        $this->db_member_ids= array();
53        $this->db_permissions_granted= array();
54        $this->db_permissions_denied= array();
55        $this->toggle_permission['grant']= array();
56        $this->toggle_permissions['deny']= array();
57       
58        // if we have an ID, load this UserGroup's members & permissions
59        if ( $this->id ) {
60            if ( $result= DB::get_column( 'SELECT user_id FROM {users_groups} WHERE group_id= ?', array( $this->id ) ) ) {
61                foreach( $result as $id ) {
62                    $this->db_member_ids[ $id ]= $id;
63                }
64            }
65
66            if ( $result= DB::get_column( 'SELECT permission_id FROM {groups_permissions} WHERE group_id=? AND denied=0 ', array( $this->id ) ) ) {
67                foreach ( $result as $granted ) {
68                    $this->db_permissions_granted[ $granted ]= $granted;
69                }
70            }
71
72            if ( $result= DB::get_column( 'SELECT permission_id FROM {groups_permissions} WHERE group_id=? AND denied=1', array( $this->id ) ) ) {
73                foreach( $result as $denied ) {
74                    $this->permissions_denied[ $denied ]= $denied;
75                }
76            }
77        }
78
79        // set the temporary variables to hold the initial values
80        // as pulled from the DB, if any
81        $this->member_ids= $this->db_member_ids;
82        $this->permissions_granted= $this->db_permissions_granted;
83        $this->permissions_denied= $this->db_permissions_denied;
84    }
85
86    /**
87     * Create a new UserGroup object and save it to the database
88     * @param array An associative array of UserGroup fields
89     * @return UserGroup the UserGroup that was created
90    **/
91    public static function create( $paramarray )
92    {
93        $usergroup= new UserGroup( $paramarray );
94        if ( $usergroup->insert() ) {
95            return $usergroup;
96        } else {
97            return $false;
98        }
99    }
100
101    /**
102     * Save a new UserGroup to the UserGroup table
103    **/
104    public function insert()
105    {
106        $allow= true;
107        // plugins have the opportunity to prevent insertion
108        $allow= Plugins::filter('usergroup_insert_allow', $allow, $this);
109        if ( ! $allow ) {
110            return false;
111        }
112        Plugins::act('usergroup_insert_before', $this);
113        $this->exclude_fields('id');
114        $result= parent::insertRecord( DB::table('users') );
115        $this->fields['id']= DB::last_insert_id();
116        EventLog::log('New group created: ' . $this->name, 'info', 'default', 'habari');
117        Plugins::act('usergroup_insert_after', $this);
118        return $result;
119    }
120
121    /**
122     * Updates an existing UserGroup in the DB
123    **/
124    public function update()
125    {
126        $allow= true;
127        // plugins have the opportunity to prevent modification
128        $allow= Plugins::filter('usergroup_update_allow', $allow, $this);
129        if ( ! $allow ) {
130            return false;
131        }
132        Plugins::act('usergroup_update_before', $this);
133`
134        // figure out what needs to be changed
135        // we do this by comparing the various temporary arrays against
136        // the arrays that hold the DB values
137        if ( $this->member_ids != $this->db_member_ids ) {
138            $added= array_diff_assoc( $this->member_ids, $this->db_member_ids);
139            if ( count( $added ) > 0 ) {
140                // one or more members added to this group
141                foreach ( $added as $id )
142                    DB::query('INSERT INTO {users_groups} (user_id, group_id) VALUES (?, ?)', array( $id, $this->id) );
143                }
144            }
145            $removed= array_diff_assoc( $this->db_member_ids, $this->member_ids );
146            if ( count( $removed ) > 0 ) {
147                // one or more members removed from this group
148                foreach ( $removed as $id ) {
149                    DB::query('DELETE FROM {users_groups} WHERE user_id=? AND group_id=?', array( $id, $this->id ) );
150                }
151            }
152        }
153
154        // were any permissions toggled?  We do this to economize hits
155        // to the DB: rather than execute separate DELETE then INSERT
156        // commands, we can execute a single UPDATE
157        if ( ! empty( $this->toggle_permissions['grant'] ) ) {
158            // grant permissions previously denied
159            $ids= implode( ',', $this->toggle_permissions['grant'];
160            $results= DB::query( "UPDATE {groups_permissions} set denied=0 WHERE id IN ($ids)" );
161            //update DB arrays, to prevent the next set of checks
162            // from trying to insert this permission
163            foreach ( $this->toggle_permissions['grant'] as $id ) {
164                $this->db_permissions_granted[ $id ]= $id;
165                unset( $this->permissions_denied[$id] );
166            }
167        }
168        if ( ! empty( $this->toggle_permissions['deny'] ) ) {
169            $ids= implode( ',', $this->toggle_permissions['deny'];
170            $results= DB::query( "UPDATE {groups_permissions} set denied=1 WHERE id IN ($ids)" );
171            //update DB arrays again
172            foreach ( $this->toggle_permissions['deny'] as $id ) {
173                $this->db_permissions_denied[ $id ]= $id;
174                 unset( $this->permissions_granted[$id] );
175            }
176        }
177
178        // now, we compare the current list of granted permissions with
179        // those currently in the DB.  If any new ones have been added,
180        // update the DB to reflect this
181        if ( $this->permissions_granted != $this->db_permissions_granted ) {
182            $granted= array_diff_assoc( $this->permissions_granted, $this->db_permissions_granted );
183            if ( count( $granted ) > 0 ) {
184            // one or more permissions granted
185                foreach( $granted as $perm ) {
186                    DB::query('INSERT INTO {groups_permissions} (group_id, permission_id, denied ) VALUES (?, ?, 0)', array( $this->id, $perm) );
187                }
188            }
189        }
190
191        // and do the same for the denied permissions
192        if ( $this->permissions_denied != $this->db_permissions_denied ) {
193            $denied= array_diff_assoc( $this->permissions_denied, $this->db_permissions_denied );
194            if ( count( $denied ) > 0 ) {
195                // one or more permissions denied
196                foreach( $denied as $perm ) {
197                    DB::query('INSERT INTO {groups_permissions} (group_id, permission_id, denied) VAlUED (?, ? 1)', array( $this->id, $perm ) );
198                }
199            }
200        }
201
202        // finally, were any previously assigned permissions
203        // (granted or denied) removed?  If so, take them out of the DB
204        if ( count( $this->permissions_revoked ) > 0 ) {
205            // one or more permissions revoked
206            foreach ( $this->permissions_revoked as $perm ) {
207                DB::query( 'DELETE FROM {users_groups} WHERE group_id=? AND permission_id=?', array( $this->id, $perm ) );
208            }
209        }
210
211        Plugins::act('usergroup_update_after', $this);
212
213        // *whew* We're all done!
214    }
215
216    /**
217     * Delete a UserGroup
218    **/
219    public function delete()
220    {
221        $allow= true;
222        // plugins have the opportunity to prevent deletion
223        $allow= Plugins::filter('usergroup_delete_allow', $allow, $this);
224         if ( ! $allow ) {
225             return;
226        }
227
228        Plugins::act('usergroup_delete_before', $this);
229        // remove all this group's permissions
230        $results= DB::query( 'DELETE FROM {groups_permissions} WHERE group_id=?', array( $this->id ) );
231        // remove all this group's members
232        $results= DB::query( 'DELETE FROM {users_groups} WHERE group_id=?', array( $this->id ) );
233        // remove this group
234        $result= parent::deleteRecord( DB::table('groups'), array( 'id' => $this->id ) );
235        Plugins::act('usergroup_delete_after', $this);
236        return $result;
237    }
238
239    /**
240     * function members
241     * returns an array of user IDs belogning to this UserGroup
242     * @return array an array of user IDs
243    **/
244    public function members()
245    {
246        return $this->member_ids;
247    }
248   
249    /**
250     * Add a user to this group
251     * @param mixed a user ID or name
252    **/
253    public function add( $id )
254    {
255        if ( ! is_int( $id ) ) {
256            $user= User::get( $id );
257            $id= $user->id;
258        }
259        if ( isset( $self->member_ids[ $id ] ) ) {
260            // this user is already a member
261            return false;
262        }
263        $self->member_ids[$id]= $id;
264        return true;
265    }
266
267    /**
268     * Remove a user from this group
269     * @param mixed a user ID or name
270    **/
271    public function remove( $id )
272    {
273        if ( ! is_int( $id ) ) {
274            $user= User::get( $id );
275            $id= $user->id;
276        }
277        if ( ! isset( $self->member_ids[ $id ] ) ) {
278            // this user is not a member of this group
279            return false;
280        }
281        unset($self->member_ids[ $id ]);
282        return true;
283    }
284
285    /**
286     * Assign a new permission to this group
287     * @param int A permission ID
288    **/
289    public function grant( $permission )
290    {
291        // is this permisson currently assigned to this group?
292        if ( isset( $this->permissions_granted[ $permission ] ) ) {
293            // we can short-circuit and stop processing
294            reutrn true;
295        }
296       
297        // is this permission currently denied to ths group?
298        if ( isset( $permission, $this->permissions_denied[ $permission ] ) ) {
299            // we need to toggle the denied bit, which is a single
300            // UPDATE operation, rather than a DELETE + INSERT
301            $this->toggle_permissions['grant']= $permission;
302
303            // we also need to update the temporary variable
304            // so that the can() method works as expected
305            // even before calls to update() occur
306            unset( $this->permissions_denied[ $permission ] );
307        }
308
309        // finally, we grant this permission
310        $this->permissions_granted[ $permission ]= $permission;
311    }
312
313    /**
314     * Deny a permission to this group
315     * @param int The permission ID to be denied
316    **/
317    public function deny( $permission )
318    {
319        // short-circuit: is this permission already denied?
320        if ( isset( $this->permissions_denied[ $permission ] ) ) {
321            return true;
322        }
323
324        // is this permission currently granted?
325        if ( isset( $this->permissions_granted[ $permission] ) ) {
326            // we need to toggle the denied bit, which is a single
327            // UPDATE operation, rather than a DELETE + INSERT
328            $this->toggle_permissions['deny']= $permission;
329
330            // we also need to update the temporary variable
331            // so that the can() method works as expected
332            // even before calls to update() occur
333            unset( $this->permissions_granted[ $permission ] );
334        }
335
336        // finally, we deny the permission
337        $this->permissions_denied[ $permission ] = $permission;
338    }
339
340    /**
341     * Remove a permission from a group
342     * @param int a permission ID
343    **/
344    public function revoke( $permission )
345    {
346        if ( isset( $this->permissons_granted[ $permission ] ) ) {
347            unset( $this->permissions_granted[ $permission ] );
348        }
349        if ( isset( $permission, $this->permissions_denied[ $permission ] ) ) {
350            unset( $this->permissions_denied[ $permission] );
351        }
352        $this->permissions_revoked[ $permission ]= $permission;
353    }
354
355    /**
356     * Determine whether members of a group can do something
357     * @param string a text description of a permission
358     * @return mixed If a permission is denied to this group, return boolean FALSE; if a permission is granted, return boolean TRUE; if a permission is neither granted nor denied, return an empty string.
359    **/
360    public function can( $permission )
361    {
362        if ( isset( $this->permissions_denied[ $permission ] ) ) {
363            return false;
364        }
365        if ( isset( $this->permissions_granted[ $permission ] ) ) {
366            return true;
367        }
368        return '';
369    }
370
371    /**
372     * Fetch a group from the database by ID or name.
373     * This is a wrapper for get_by_id() and get_by_name()
374     * @param mixed $group A group ID or name
375     * @return mixed UserGroup object, or boolean FALSE
376    */
377    public static function get( $group )
378    {
379        if ( is_int( $group ) ) {
380            return self::get_by_id( $group );
381        } else {
382            return self::get_by_name( $group );
383        }
384    }
385
386    /**
387     * Select a group from the DB by its ID
388     * @param int A group ID
389     * @return mixed A UserGroup object, or boolean FALSE
390    **/
391    public static function get_by_id( $id )
392    {
393        if ( 0 == $id ) {
394            return false;
395        }
396        return DB::get_row( 'SELECT * FROM {groups} WHERE id=?', array( $id ), 'UserGroup' );
397    }
398
399    /**
400     * Select a group from the DB by its name
401     * @param string A group name
402     * @return mixed A UserGroup object, or boolean FALSE
403    **/
404    public static function get_by_name( $name )
405    {
406        if ( '' == $name ) {
407            return false;
408        }
409        return DB::get_row( 'SELECT * FROM {groups} WHERE name=?', array( $name ), 'UserGroup' );
410    }
411
412    /**
413     * Determine whether a group exists
414     * @param mixed The name or ID of the group
415     * @return bool Whether the group exists or not
416    **/
417    public static function exists( $group )
418    {
419        if ( is_int( $group ) ) {
420            $query= 'id';
421        } else {
422            $query= 'name';
423        }
424        return DB::query("SELECT id FROM {groups} WHERE $query=?", array( $group ) );
425    }
426
427}
428?>