Ticket #132: usergroup.3.php

File usergroup.3.php, 13.0 kB (added by skippy, 11 months ago)

minor tweak to logic in UserGroup::update()

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                if ( isset( $this->db_permissions_denied[$id] ) ) {
166                    // this should make the db array match
167                    // its corresponding temp array
168                    unset( $this->db_permissions_denied[$id] );
169                }
170            }
171        }
172        if ( ! empty( $this->toggle_permissions['deny'] ) ) {
173            $ids= implode( ',', $this->toggle_permissions['deny'];
174            $results= DB::query( "UPDATE {groups_permissions} set denied=1 WHERE id IN ($ids)" );
175            //update DB arrays again
176            foreach ( $this->toggle_permissions['deny'] as $id ) {
177                $this->db_permissions_denied[ $id ]= $id;
178                if  ( isset( $this->db_permissions_granted[$id] ) ) {
179                    // this should make the db array match
180                    // its corresponding temp array
181                    unset( $this->db_permissions_granted[$id] );
182                }
183            }
184        }
185
186        // now, we compare the current list of granted permissions with
187        // those currently in the DB.  If any new ones have been added,
188        // update the DB to reflect this
189        if ( $this->permissions_granted != $this->db_permissions_granted ) {
190            $granted= array_diff_assoc( $this->permissions_granted, $this->db_permissions_granted );
191            if ( count( $granted ) > 0 ) {
192            // one or more permissions granted
193                foreach( $granted as $perm ) {
194                    DB::query('INSERT INTO {groups_permissions} (group_id, permission_id, denied ) VALUES (?, ?, 0)', array( $this->id, $perm) );
195                }
196            }
197        }
198
199        // and do the same for the denied permissions
200        if ( $this->permissions_denied != $this->db_permissions_denied ) {
201            $denied= array_diff_assoc( $this->permissions_denied, $this->db_permissions_denied );
202            if ( count( $denied ) > 0 ) {
203                // one or more permissions denied
204                foreach( $denied as $perm ) {
205                    DB::query('INSERT INTO {groups_permissions} (group_id, permission_id, denied) VAlUED (?, ? 1)', array( $this->id, $perm ) );
206                }
207            }
208        }
209
210        // finally, were any previously assigned permissions
211        // (granted or denied) removed?  If so, take them out of the DB
212        if ( count( $this->permissions_revoked ) > 0 ) {
213            // one or more permissions revoked
214            foreach ( $this->permissions_revoked as $perm ) {
215                DB::query( 'DELETE FROM {users_groups} WHERE group_id=? AND permission_id=?', array( $this->id, $perm ) );
216            }
217        }
218
219        Plugins::act('usergroup_update_after', $this);
220
221        // *whew* We're all done!
222    }
223
224    /**
225     * Delete a UserGroup
226    **/
227    public function delete()
228    {
229        $allow= true;
230        // plugins have the opportunity to prevent deletion
231        $allow= Plugins::filter('usergroup_delete_allow', $allow, $this);
232         if ( ! $allow ) {
233             return;
234        }
235
236        Plugins::act('usergroup_delete_before', $this);
237        // remove all this group's permissions
238        $results= DB::query( 'DELETE FROM {groups_permissions} WHERE group_id=?', array( $this->id ) );
239        // remove all this group's members
240        $results= DB::query( 'DELETE FROM {users_groups} WHERE group_id=?', array( $this->id ) );
241        // remove this group
242        $result= parent::deleteRecord( DB::table('groups'), array( 'id' => $this->id ) );
243        Plugins::act('usergroup_delete_after', $this);
244        return $result;
245    }
246
247    /**
248     * function members
249     * returns an array of user IDs belogning to this UserGroup
250     * @return array an array of user IDs
251    **/
252    public function members()
253    {
254        return $this->member_ids;
255    }
256   
257    /**
258     * Add a user to this group
259     * @param mixed a user ID or name
260    **/
261    public function add( $id )
262    {
263        if ( ! is_int( $id ) ) {
264            $user= User::get( $id );
265            $id= $user->id;
266        }
267        if ( isset( $self->member_ids[ $id ] ) ) {
268            // this user is already a member
269            return false;
270        }
271        $self->member_ids[$id]= $id;
272        return true;
273    }
274
275    /**
276     * Remove a user from this group
277     * @param mixed a user ID or name
278    **/
279    public function remove( $id )
280    {
281        if ( ! is_int( $id ) ) {
282            $user= User::get( $id );
283            $id= $user->id;
284        }
285        if ( ! isset( $self->member_ids[ $id ] ) ) {
286            // this user is not a member of this group
287            return false;
288        }
289        unset($self->member_ids[ $id ]);
290        return true;
291    }
292
293    /**
294     * Assign a new permission to this group
295     * @param int A permission ID
296    **/
297    public function grant( $permission )
298    {
299        // is this permisson currently assigned to this group?
300        if ( isset( $this->permissions_granted[ $permission ] ) ) {
301            // we can short-circuit and stop processing
302            reutrn true;
303        }
304       
305        // is this permission currently denied to ths group?
306        if ( isset( $permission, $this->permissions_denied[ $permission ] ) ) {
307            // we need to toggle the denied bit, which is a single
308            // UPDATE operation, rather than a DELETE + INSERT
309            $this->toggle_permissions['grant']= $permission;
310
311            // we also need to update the temporary variable
312            // so that the can() method works as expected
313            // even before calls to update() occur
314            unset( $this->permissions_denied[ $permission ] );
315        }
316
317        // finally, we grant this permission
318        $this->permissions_granted[ $permission ]= $permission;
319    }
320
321    /**
322     * Deny a permission to this group
323     * @param int The permission ID to be denied
324    **/
325    public function deny( $permission )
326    {
327        // short-circuit: is this permission already denied?
328        if ( isset( $this->permissions_denied[ $permission ] ) ) {
329            return true;
330        }
331
332        // is this permission currently granted?
333        if ( isset( $this->permissions_granted[ $permission] ) ) {
334            // we need to toggle the denied bit, which is a single
335            // UPDATE operation, rather than a DELETE + INSERT
336            $this->toggle_permissions['deny']= $permission;
337
338            // we also need to update the temporary variable
339            // so that the can() method works as expected
340            // even before calls to update() occur
341            unset( $this->permissions_granted[ $permission ] );
342        }
343
344        // finally, we deny the permission
345        $this->permissions_denied[ $permission ] = $permission;
346    }
347
348    /**
349     * Remove a permission from a group
350     * @param int a permission ID
351    **/
352    public function revoke( $permission )
353    {
354        if ( isset( $this->permissons_granted[ $permission ] ) ) {
355            unset( $this->permissions_granted[ $permission ] );
356        }
357        if ( isset( $permission, $this->permissions_denied[ $permission ] ) ) {
358            unset( $this->permissions_denied[ $permission] );
359        }
360        $this->permissions_revoked[ $permission ]= $permission;
361    }
362
363    /**
364     * Determine whether members of a group can do something
365     * @param string a text description of a permission
366     * @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.
367    **/
368    public function can( $permission )
369    {
370        if ( isset( $this->permissions_denied[ $permission ] ) ) {
371            return false;
372        }
373        if ( isset( $this->permissions_granted[ $permission ] ) ) {
374            return true;
375        }
376        return '';
377    }
378
379    /**
380     * Fetch a group from the database by ID or name.
381     * This is a wrapper for get_by_id() and get_by_name()
382     * @param mixed $group A group ID or name
383     * @return mixed UserGroup object, or boolean FALSE
384    */
385    public static function get( $group )
386    {
387        if ( is_int( $group ) ) {
388            return self::get_by_id( $group );
389        } else {
390            return self::get_by_name( $group );
391        }
392    }
393
394    /**
395     * Select a group from the DB by its ID
396     * @param int A group ID
397     * @return mixed A UserGroup object, or boolean FALSE
398    **/
399    public static function get_by_id( $id )
400    {
401        if ( 0 == $id ) {
402            return false;
403        }
404        return DB::get_row( 'SELECT * FROM {groups} WHERE id=?', array( $id ), 'UserGroup' );
405    }
406
407    /**
408     * Select a group from the DB by its name
409     * @param string A group name
410     * @return mixed A UserGroup object, or boolean FALSE
411    **/
412    public static function get_by_name( $name )
413    {
414        if ( '' == $name ) {
415            return false;
416        }
417        return DB::get_row( 'SELECT * FROM {groups} WHERE name=?', array( $name ), 'UserGroup' );
418    }
419
420    /**
421     * Determine whether a group exists
422     * @param mixed The name or ID of the group
423     * @return bool Whether the group exists or not
424    **/
425    public static function exists( $group )
426    {
427        if ( is_int( $group ) ) {
428            $query= 'id';
429        } else {
430            $query= 'name';
431        }
432        return DB::query("SELECT id FROM {groups} WHERE $query=?", array( $group ) );
433    }
434
435}
436?>