sql_r = e107::getDb('sql_r'); $this->isAdmin = FALSE; $this->fixed_classes = array(e_UC_PUBLIC => UC_LAN_0, e_UC_GUEST => UC_LAN_1, e_UC_NOBODY => UC_LAN_2, e_UC_MEMBER => UC_LAN_3, e_UC_ADMIN => UC_LAN_5, e_UC_MAINADMIN => UC_LAN_6, e_UC_READONLY => UC_LAN_4, e_UC_NEWUSER => UC_LAN_9 ); $this->text_class_link = array('public' => e_UC_PUBLIC, 'guest' => e_UC_GUEST, 'nobody' => e_UC_NOBODY, 'member' => e_UC_MEMBER, 'admin' => e_UC_ADMIN, 'main' => e_UC_MAINADMIN, 'readonly' => e_UC_READONLY, 'new' => e_UC_NEWUSER); $this->readTree(TRUE); // Initialise the classes on entry } /* Ensure the tree of userclass data is stored in our object. Only read if its either not present, or the $force flag is set */ protected function readTree($force = FALSE) { if (isset($this->class_tree) && count($this->class_tree) && !$force) return $this->class_tree; global $e107; $this->class_tree = array(); $this->class_parents = array(); $array = new ArrayData; if ($temp = $e107->ecache->retrieve_sys(UC_CACHE_TAG)) { $this->class_tree = $array->ReadArray($temp); unset($temp); } else { $this->sql_r->db_Select("userclass_classes", '*', "ORDER BY userclass_parent", 'nowhere'); // The order statement should give a consistent return while ($row = $this->sql_r->db_Fetch(MYSQL_ASSOC)) { $this->class_tree[$row['userclass_id']] = $row; $this->class_tree[$row['userclass_id']]['class_children'] = array(); // Create the child array in case needed } // Add in any fixed classes that aren't already defined foreach ($this->fixed_classes as $c => $d) { // if (!isset($this->class_tree[$c]) && ($c != e_UC_PUBLIC)) if (!isset($this->class_tree[$c])) { switch ($c) { case e_UC_ADMIN : case e_UC_MAINADMIN : $this->class_tree[$c]['userclass_parent'] = e_UC_NOBODY; break; case e_UC_NEWUSER : $this->class_tree[$c]['userclass_parent'] = e_UC_MEMBER; break; default : $this->class_tree[$c]['userclass_parent'] = e_UC_PUBLIC; } $this->class_tree[$c]['userclass_id'] = $c; $this->class_tree[$c]['userclass_name'] = $d; $this->class_tree[$c]['userclass_description'] = 'Fixed class'; $this->class_tree[$c]['userclass_visibility'] = e_UC_PUBLIC; $this->class_tree[$c]['userclass_editclass'] = e_UC_MAINADMIN; $this->class_tree[$c]['userclass_accum'] = $c; $this->class_tree[$c]['userclass_type'] = UC_TYPE_STD; } } $userCache = $array->WriteArray($this->class_tree, FALSE); $e107->ecache->set_sys(UC_CACHE_TAG,$userCache); unset($userCache); } // Now build the tree. // There are just two top-level classes - 'Everybody' and 'Nobody' $this->class_parents[e_UC_PUBLIC] = e_UC_PUBLIC; $this->class_parents[e_UC_NOBODY] = e_UC_NOBODY; foreach ($this->class_tree as $uc) { /* if ($uc['userclass_parent'] == e_UC_PUBLIC) { // Note parent (top level) classes $this->class_parents[$uc['userclass_id']] = $uc['userclass_id']; } else */ if (($uc['userclass_id'] != e_UC_PUBLIC) && ($uc['userclass_id'] != e_UC_NOBODY)) { // if (!array_key_exists($uc['userclass_parent'],$this->class_tree)) if (!isset($this->class_tree[$uc['userclass_parent']])) { echo "Orphaned class record: ID=".$uc['userclass_id']." Name=".$uc['userclass_name']." Parent=".$uc['userclass_parent']."
"; } else { // Add to array $this->class_tree[$uc['userclass_parent']]['class_children'][] = $uc['userclass_id']; } } } } // Given the list of 'base' classes a user belongs to, returns a comma separated list including ancestors. Duplicates stripped public function get_all_user_classes($start_list) { $is = array(); $start_array = explode(',', $start_list); foreach ($start_array as $sa) { // Merge in latest values - should eliminate duplicates as it goes if (isset($this->class_tree[$sa])) { $is = array_merge($is,explode(',',$this->class_tree[$sa]['userclass_accum'])); } } return implode(',',array_unique($is)); } // Returns a list of user classes which can be edited by the specified classlist (defaults to current user's classes) public function get_editable_classes($class_list = USERCLASS_LIST, $asArray = FALSE) { $ret = array(); $blockers = array(e_UC_PUBLIC => 1, e_UC_READONLY => 1, e_UC_MEMBER => 1, e_UC_NOBODY => 1, e_UC_GUEST => 1, e_UC_NEWUSER => 1); $possibles = array_flip(explode(',',$class_list)); unset($possibles[e_UC_READONLY]); foreach ($this->class_tree as $uc => $uv) { if (!isset($blockers[$uc])) { $ec = $uv['userclass_editclass']; if (isset($possibles[$ec])) { // echo $uc." {$ec} {$uv['userclass_description']}
"; $ret[] = $uc; } } } if ($asArray) { return $ret; } return implode(',',$ret); } // Combines the selected editable classes into the main class list for a user. // $combined - the complete list of current class memberships // $possible - the classes which are being edited // $actual - the actual membership of the editable classes // All classes may be passed as comma-separated lists or arrays public function mergeClassLists($combined, $possible, $actual, $asArray = FALSE) { if (!is_array($combined)) { $combined = explode(',',$combined); } if (!is_array($possible)) { $possible = explode(',',$possible); } if (!is_array($actual)) { $actual = explode(',',$actual); } $combined = array_flip($combined); foreach ($possible as $p) { if (in_array($p,$actual)) { // Class must be in final array $combined[$p] = 1; } else { unset($combined[$p]); } } $combined = array_keys($combined); if ($asArray) { return $combined; } return implode(',', $combined); } public function stripFixedClasses($inClasses) { $asArray = TRUE; if (!is_array($inClasses)) { $asArray = FALSE; $inClasses = explode(',',$inClasses); } $inClasses = array_flip($inClasses); foreach ($this->fixed_classes as $k => $v) { if (isset($inClasses[$k])) { unset($inClasses[$k]); } } $inClasses = array_keys($inClasses); if ($asArray) { return ($inClasses); } return implode(',',$inClasses); } // Given a comma separated list, returns the minimum number of class memberships required to achieve this (i.e. strips classes 'above' another in the tree) // Requires the class tree to have been initialised public function normalise_classes($class_list) { $drop_classes = array(); $old_classes = explode(',',$class_list); foreach ($old_classes as $c) { // Look at our parents (which are in 'userclass_accum') - if any of them are contained in old_classes, we can drop them. $tc = array_flip(explode(',',$this->class_tree[$c]['userclass_accum'])); unset($tc[$c]); // Current class should be in $tc anyway foreach ($tc as $tc_c => $v) { if (in_array($tc_c,$old_classes)) { $drop_classes[] = $tc_c; } } } $new_classes = array_diff($old_classes,$drop_classes); return implode(',',$new_classes); } /* Generate a dropdown list of user classes from which to select - virtually as r_userclass() $optlist allows selection of the classes to be shown in the dropdown. All or none can be included, separated by comma. Valid options are: public guest nobody member readonly admin main - main admin new - new users classes - shows all classes matchclass - if 'classes' is set, this option will only show the classes that the user is a member of language - list of languages. blank - puts an empty option at the top of select dropdowns filter - only show those classes where member is in a class permitted to view them - i.e. as the new 'visible to' field - added for 0.8 force - show all classes (subject to the other options, including matchclass) - added for 0.8 $extra_js - can add JS handlers (e.g. 'onclick', 'onchange') if required [ $mode parameter of r_userclass() removed - $optlist is more flexible) ] */ public function uc_dropdown($fieldname, $curval = 0, $optlist = "", $extra_js = '') { global $pref; $show_classes = self::uc_required_class_list($optlist); $text = ''; foreach ($show_classes as $k => $v) { if ($k == e_UC_BLANK) { $text .= "\n"; } else { $s = ($curval == $k && $curval !== '') ? "selected='selected'" : ""; $text .= "\n"; } } // Inverted Classes if(strpos($optlist, "no-excludes") !== TRUE) { $text .= "\n\n"; foreach ($show_classes as $k => $v) { if($k != '0' && $k!=255 && $k !=251) // remove everyone, nobody and readonly from list. { $s = ($curval == ('-'.$k) && $curval !== '') ? "selected='selected'" : ""; $text .= "\n"; } } $text .= "\n"; } if (strpos($optlist, "language") !== FALSE && $pref['multilanguage']) { $text .= "\n"; $tmpl = explode(",",e_LANLIST); foreach($tmpl as $lang) { $s = ($curval == $lang) ? " selected='selected'" : ""; $text .= "\n"; } } // Only return the select box if we've ended up with some options if ($text) $text = "\n\n"; return $text.$curVal; } /* Generate an ordered array classid=>classname - used for dropdown and check box lists If $just_ids is TRUE, array value is just '1' */ public function uc_required_class_list($optlist = '', $just_ids = FALSE) { $ret = array(); if (!$optlist) $optlist = 'public,guest,nobody,member,classes'; // Set defaults to simplify ongoing processing if ($optlist == 'editable') { $temp = array_flip(explode(',',$this->get_editable_classes())); if ($just_ids) return $temp; foreach ($temp as $c => $t) { $temp[$c] = $this->class_tree[$c]['userclass_name']; } return $temp; } $opt_arr = explode(',',$optlist); foreach ($opt_arr as $k => $v) { $opt_arr[$k] = trim($v); } $opt_arr = array_flip($opt_arr); // This also eliminates duplicates which could arise from applying the other options, although shouldn't matter if (isset($opt_arr['force'])) unset($opt_arr['filter']); if (isset($opt_arr['blank'])) { $ret[e_UC_BLANK] = 1; } // Do the 'fixed' classes next foreach ($this->text_class_link as $k => $v) { // if (isset($opt_arr[$k]) || isset($opt_arr['force'])) if (isset($opt_arr[$k])) { $ret[$v] = $just_ids ? '1' : $this->fixed_classes[$v]; } } // Now do the user-defined classes if (isset($opt_arr['classes']) || isset($opt_arr['force'])) { // Display those classes the user is allowed to: // Main admin always sees the lot // a) Mask the 'fixed' user classes which have already been processed // b) Apply the visibility option field ('userclass_visibility') // c) Apply the matchclass option if appropriate foreach($this->class_tree as $uc_id => $row) { if (!array_key_exists($uc_id,$this->fixed_classes) && ( getperms("0") || ( (!isset($optlist["matchclass"]) || check_class($uc_id)) && (!isset($optlist["filter"]) || check_class($row['userclass_visibility'])) ) ) ) { $ret[$uc_id] = $just_ids ? '1' : $this->class_tree[$uc_id]['userclass_name']; } } } /* Above loop slightly changes the display order of earlier code versions. If readonly must be last (after language), delete it from the $text_class_link array, and uncomment the following code if (isset($opt_arr['readonly'])) { $ret[e_UC_READONLY] = $this->class_tree[e_UC_READONLY]['userclass_description']; } */ return $ret; } /* Very similar to r_userclass, but returns a list of check boxes. Doesn't encapsulate it. $fieldname is the name for the array of checkboxes $curval is a comma separated list of class IDs for boxes which are checked. $optlist as for uc_dropdown if $showdescription is TRUE, appends the class description in brackets */ public function uc_checkboxes($fieldname, $curval='', $optlist = '', $showdescription = FALSE) { global $pref; $show_classes = $this->uc_required_class_list($optlist); $curArray = explode(",", $curval); // Array of current values $ret = ""; foreach ($show_classes as $k => $v) { if ($k != e_UC_BLANK) { $c = (in_array($k,$curArray)) ? " checked='checked'" : ""; if ($showdescription) $v .= " (".$this->uc_get_classdescription($k).")"; $ret .= "
\n"; } } if (strpos($optlist, "language") !== FALSE && $pref['multilanguage']) { $ret .= "
\n"; $tmpl = explode(",",e_LANLIST); foreach($tmpl as $lang) { $c = (in_array($lang, $curArray)) ? " checked='checked' " : ""; $ret .= "
"; } } return $ret; } /* Next two routines create an indented tree - for example within a select box or a list of check boxes. For each displayed element, the callback routine is called $treename is the name given to the elements where required $callback is a routine used to generate each element; there are two implemented within this class: select (the default) - generates the option list. Text requires to be encapsulated in a tag set - can also be used with multi-select boxes checkbox - generates a set of checkboxes Alternative callbacks can be used to achieve different layouts/styles $current_value is a single class number for single-select dropdown; comma separated array of class numbers for checkbox list or multi-select $optlist works the same as for other class displays */ protected function vetted_sub_tree($treename, $callback,$listnum,$nest_level,$current_value, $perms, $opt_options) { $ret = ''; $nest_level++; if(isset($this->class_tree[$listnum]['class_children'])) { foreach ($this->class_tree[$listnum]['class_children'] as $p) { // Looks like we don't need to differentiate between function and class calls if (isset($perms[$p])) { $ret .= call_user_func($callback,$treename, $p,$current_value,$nest_level, $opt_options); } $ret .= $this->vetted_sub_tree($treename, $callback,$p,$nest_level,$current_value, $perms, $opt_options); } } return $ret; } public function vetted_tree($treename, $callback='', $current_value='', $optlist = '',$opt_options = '') { $ret = ''; if (!$callback) $callback=array($this,'select'); $current_value = str_replace(' ','',$current_value); // Simplifies parameter passing for the tidy-minded $perms = $this->uc_required_class_list($optlist,TRUE); // List of classes which we can display if (isset($perms[e_UC_BLANK])) { $ret .= call_user_func($callback,$treename, e_UC_BLANK, $current_value,0, $opt_options); } foreach ($this->class_parents as $p) { if (isset($perms[$p])) { $ret .= call_user_func($callback,$treename, $p,$current_value,0, $opt_options); } $ret .= $this->vetted_sub_tree($treename, $callback,$p,0, $current_value, $perms, $opt_options); } return $ret; } // Callback for vetted_tree - Creates the option list for a selection box public function select($treename, $classnum, $current_value, $nest_level) { if ($classnum == e_UC_BLANK) return "\n"; // echo "Display: {$classnum}, {$current_value}, {$nest_level}
"; $tmp = explode(',',$current_value); $sel = in_array($classnum,$tmp) ? " selected='selected'" : ''; if ($nest_level == 0) { $prefix = ''; $style = " style='font-weight:bold; font-style: italic;'"; } elseif ($nest_level == 1) { $prefix = '  '; $style = " style='font-weight:bold'"; } else { $prefix = '  '.str_repeat('--',$nest_level-1).'>'; $style = ''; } return "\n"; } // Callback for vetted_tree - displays indented checkboxes with class name only public function checkbox($treename, $classnum, $current_value, $nest_level) { if ($classnum == e_UC_BLANK) return ''; $tmp = explode(',',$current_value); $chk = in_array($classnum, $tmp) ? " checked='checked'" : ''; if ($nest_level == 0) { $style = " style='font-weight:bold'"; } else { $style = " style='text-indent:".(1.2*$nest_level)."em'"; } return "
".$this->class_tree[$classnum]['userclass_name']."
\n"; } // Callback for vetted_tree - displays indented checkboxes with class name, and description in brackets public function checkbox_desc($treename, $classnum, $current_value, $nest_level) { if ($classnum == e_UC_BLANK) return ''; $tmp = explode(',',$current_value); $chk = in_array($classnum, $tmp) ? " checked='checked'" : ''; if ($nest_level == 0) { $style = " style='font-weight:bold'"; } else { $style = " style='text-indent:".(1.2*$nest_level)."em'"; } return "
".$this->class_tree[$classnum]['userclass_name'].' ('.$this->class_tree[$classnum]['userclass_description'].")
\n"; } /* Return array of all classes, limited according to membership of the userclass_visibility field if $filter is set. Index field - userclass_id Data fields - userclass_name, userclass_description, userclass_editclass */ public function uc_get_classlist($filter = FALSE) { $ret = array(); $this->readTree(FALSE); // Make sure we have data foreach ($this->class_tree as $k => $v) { if (!$filter || check_class($filter)) { $ret[$k] = array('userclass_name' => $v, 'userclass_description' => $v['userclass_description'], 'userclass_editclass' => $v['userclass_editclass']); } } return $ret; } // Return class name for given class ID public function uc_get_classname($id) { if (isset($this->class_tree[$id])) { return $this->class_tree[$id]['userclass_name']; } if (isset($this->fixed_classes[$id])) { return $this->fixed_classes[$id]; } if($id < 0) { $val = abs($id); $name = isset($this->class_tree[$val]['userclass_name']) ? $this->class_tree[$val]['userclass_name'] : $this->fixed_classes[$val]; return str_replace("--CLASS--", $name, UC_LAN_INVERT); } return ''; } public function uc_get_classdescription($id) { if (isset($this->class_tree[$id])) { return $this->class_tree[$id]['userclass_description']; } if (isset($this->fixed_classes[$id])) { return $this->fixed_classes[$id]; // Name and description the same for fixed classes } return ''; } public function uc_get_classicon($id) { if (isset($this->class_tree[$id])) { return $this->class_tree[$id]['userclass_icon']; } return ''; } // Given a class name, look up the ID. Return FALSE if not found public function ucGetClassIDFromName($name) { $this->readTree(); // We have all the info - can just search the array foreach ($this->class_tree as $uc => $info) { if ($info['userclass_name'] == $name) { return $uc; } } return FALSE; // not found } // Utility to remove a specified class ID from the default comma-separated list public function ucRemove($classID, $from, $asArray = FALSE) { $tmp = array_flip(explode(',',$from)); if (isset($tmp[$classID])) { unset($tmp[$classID]); } $tmp = array_keys($tmp); if ($asArray) { return $tmp; } if (count($tmp) == 0) { return ''; } return implode(',',$tmp); } // Utility to add a specified class ID to the default comma-separated list public function ucAdd($classID, $to, $asArray = FALSE) { $tmp = array_flip(explode(',',$to)); $tmp[$classID] = 1; $tmp = array_keys($tmp); if ($asArray) { return $tmp; } return implode(',',$tmp); } // See if a class can be edited (in the sense of the class ID not being fixed) - return TRUE if permitted. public function isEditableClass($classID) { if (($classID >= e_UC_SPECIAL_BASE) && ($classID <= e_UC_SPECIAL_END)) return FALSE; // Don't allow deletion of fixed classes if (isset($this->fixed_classes[$class_id])) return FALSE; // This picks up classes such as e_UC_PUBLIC outside the main range which can't be deleted return TRUE; } /* Return all users in a particular class or set of classes. $classlist is a comma separated list of classes - if the 'predefined' classes are required, they must be included. No spaces allowed $field_list is used to select the returned fields ($user_id is the primary index) ****** Can be verrrrryyyy slow - has to scan the whole user database at present ****** ********* NOT TESTED ********** ***** NOT SURE WHETHER THIS IS REALLY A USER OR A USER CLASS FUNCTION ***** */ public function get_users_in_class($classlist, $field_list = 'user_name, user_loginname', $include_ancestors = FALSE, $order_by = 'user_id') { $ret = array(); if ($include_ancestors) $classlist = $this->get_all_user_classes($classlist); $class_regex = "(^|,)(".str_replace(' ','',str_replace(",", "|", $classlist)).")(,|$)"; $qry = "SELECT 'user_id,{$field_list}' FROM `user` WHERE user_class REGEXP '{$class_regex}' ORDER BY '{$order_by}'"; if ($this->sql_r->db_Select_gen($qry)) { while ($row = $this->sql_r->db_Fetch(MYSQL_ASSOC)) { $ret[$row['user_id']] = $row; } } return $ret; } } //======================================================================== // Functions from previous userclass_class handler //======================================================================== // Implemented for backwards compatibility/convenience. // ************** DEPRECATED - use new class-based functions /* With $optlist you can now specify which classes are shown in the dropdown. All or none can be included, separated by comma (or whatever). Valid options are: public guest nobody member readonly admin main - main admin classes - shows all classes matchclass - if 'classes' is set, this option will only show the classes that the user is a member of language - list of languages. filter - only show those classes where member is in a class permitted to view them - i.e. as the new 'visible to' field force - show all classes (subject to the other options, including matchclass) $mode = 'off' turns off listing of admin/main admin classes unless enabled in $optlist (can probably be deprecated - barely used) */ function r_userclass($fieldname, $curval = 0, $mode = "off", $optlist = "") { // echo "Call r_userclass{$fieldname}, CV: {$curval} opts: {$optlist}
"; global $e_userclass; if ($mode != 'off') { // Handle legacy code if ($optlist) $optlist .= ','; $optlist .= 'admin,main'; if ($mode != 'admin') $optlist .= ',readonly'; } if (!is_object($e_userclass)) $e_userclass = new user_class; return $e_userclass->uc_dropdown($fieldname,$curval,$optlist); } // Very similar to r_userclass, but returns a list of check boxes. (currently only used in newspost.php) // $curval is a comma separated list of class IDs for boxes which are checked. function r_userclass_check($fieldname, $curval = '', $optlist = "") { // echo "Call r_userclass_check: {$curval}
"; global $e_userclass; if (!is_object($e_userclass)) $e_userclass = new user_class; $ret = $e_userclass->uc_checkboxes($fieldname,$curval,$optlist); if ($ret) $ret = "
".$ret."
"; return $ret; } function get_userclass_list() { // echo "Call get_userclass_list
"; global $e_userclass; if (!is_object($e_userclass)) $e_userclass = new user_class; return $e_userclass->uc_get_classlist(); } function r_userclass_name($id) { // echo "Call r_userclass_name
"; global $e_userclass; if (!is_object($e_userclass)) $e_userclass = new user_class; return $e_userclass->uc_get_classname($id); } // Deprecated functions to hopefully be removed function r_userclass_radio($fieldname, $curval = '') { echo "Deprecated function r_userclass_radio not used in core - mutter if you'd like it implemented
"; } //======================================================================== // Admin Class handler - could go into separate file later //======================================================================== class user_class_admin extends user_class { var $field_list = array('userclass_name' => "varchar(100) NOT NULL default ''", 'userclass_description' => "varchar(250) NOT NULL default ''", 'userclass_editclass' => "tinyint(3) unsigned NOT NULL default '0'", 'userclass_parent' => "tinyint(3) unsigned NOT NULL default '0'", 'userclass_accum' => "varchar(250) NOT NULL default ''", 'userclass_visibility' => "tinyint(3) unsigned NOT NULL default '0'", 'userclass_type' =>"tinyint(1) unsigned NOT NULL default '0'", 'userclass_icon' => "varchar(250) NOT NULL default ''" ); // Note - 'userclass_id' intentionally not in this list // Icons to use for graphical tree display // First index - no children, children // Second index - not last item, last item // Third index - closed tree, open tree var $tree_icons = array( ); var $graph_debug = FALSE; // Shows extra info on graphical tree when TRUE function user_class_admin() { parent::user_class(); // Call constructor from ancestor class $this->isAdmin = TRUE; // Have to initialise the images this way - PHP4 won't take a nested array assignment in the variable list $this->tree_icons = array( FALSE => array( // No children FALSE => array( // Not last item FALSE => '', // Closed tree - don't display TRUE => 'branch.gif' ) , TRUE => array( // Last item FALSE => '', // Closed tree - don't display TRUE => 'branchbottom.gif' ) ), TRUE => array( // children FALSE => array( // Not last item FALSE => 'plus.gif', // Closed tree - option to expand TRUE => 'minus.gif' ) , TRUE => array( // Last item FALSE => 'plusbottom.gif', // Closed tree - option to expand TRUE => 'minusbottom.gif' )) ); } /* Next three routines are used to update the database after adding/deleting a class */ public function calc_tree() { // echo "Calc Tree
"; $this->readTree(TRUE); // Make sure we have accurate data foreach ($this->class_parents as $cp) { $rights = array(); $this->rebuild_tree($cp,$rights); // increasing rights going down the tree } } // Internal function, called recursively to rebuild the permissions tree where rights increase going down the tree // $parent is the class number being processed. // $rights is the array of rights accumulated so far in the walk down the tree protected function rebuild_tree($parent, $rights) { if ($this->class_tree[$parent]['userclass_parent'] == e_UC_NOBODY) { $this->topdown_tree($parent); return; } // echo "Bottom up: {$parent}
"; if ($this->class_tree[$parent]['userclass_type'] == UC_TYPE_GROUP) { // echo "Bottom up - skip: {$parent}
"; return; // Probably just stop here for a group class } $rights[] = $parent; $imp_rights = implode(',',$rights); if ($this->class_tree[$parent]['userclass_accum'] != $imp_rights) { $this->class_tree[$parent]['userclass_accum'] = $imp_rights; if (!isset($this->class_tree[$cp]['change_flag'])) $this->class_tree[$parent]['change_flag'] = 'UPDATE'; } foreach ($this->class_tree[$parent]['class_children'] as $cc) { $this->rebuild_tree($cc,$rights); // Recursive call } } // Internal function, called recursively to rebuild the permissions tree where rights increase going up the tree // Returns an array protected function topdown_tree($our_class) { $rights = array($our_class); // Accumulator always has rights to its own class if ($this->class_tree[$our_class]['userclass_type'] == UC_TYPE_GROUP) return array_merge($rights, explode(',',$this->class_tree[$our_class]['userclass_accum'])); // Stop rights accumulation at a group foreach ($this->class_tree[$our_class]['class_children'] as $cc) { $rights = array_merge($rights,$this->topdown_tree($cc)); // Recursive call } $rights = array_unique($rights); $imp_rights = implode(',',$rights); // echo "Class: {$our_class} Rights: {$imp_rights}
"; if ($this->class_tree[$our_class]['userclass_accum'] != $imp_rights) { $this->class_tree[$our_class]['userclass_accum'] = $imp_rights; $this->class_tree[$our_class]['change_flag'] = 'UPDATE'; } return $rights; } public function save_tree() { foreach ($this->class_tree as $tree) { if (isset($tree['change_flag'])) { switch ($tree['change_flag']) { case 'INSERT' : $this->add_new_class($tree); break; case 'UPDATE' : $this->save_edited_class($tree); break; default : continue; } } } } /* Next two routines show a text-based tree with markers to indicate the hierarchy. */ protected function show_sub_tree($listnum,$marker, $add_class = FALSE) { $ret = ''; $marker = '--'.$marker; foreach ($this->class_tree[$listnum]['class_children'] as $p) { $ret .= $marker.$this->class_tree[$p]['userclass_id'].':'.$this->class_tree[$p]['userclass_name']; if ($add_class) $ret .= " (".$this->class_tree[$p]['userclass_accum'].")"; $ret .= " Children: ".count($this->class_tree[$p]['class_children']); $ret .= "
"; $ret .= $this->show_sub_tree($p,$marker, $add_class); } return $ret; } public function show_tree($add_class = FALSE) { $ret = ''; foreach ($this->class_parents as $p) { $ret .= $this->class_tree[$p]['userclass_id'].':'.$this->class_tree[$p]['userclass_name']; if ($add_class) $ret .= " (".$this->class_tree[$p]['userclass_accum'].")"; $ret .= " Children: ".count($this->class_tree[$p]['class_children']); $ret .= "
"; $ret .= $this->show_sub_tree($p,'>', $add_class); } return $ret; } /* Next two routines generate a graphical tree, including option to open/close branches */ protected function show_graphical_subtree($listnum, $indent_images, $is_last = FALSE) { $num_children = count($this->class_tree[$listnum]['class_children']); $is_open = TRUE; $tag_name = 'uclass_tree_'.$listnum; $ret = "
\n"; foreach ($indent_images as $im) { $ret .= "class icon"; } // If this link has children, wrap the next image in a link and an expand/hide option if ($num_children) { $ret .= "class icon"; $ret .= "\n"; } else { $ret .= "class icon\n"; } $name_line = ''; if ($this->graph_debug) { $name_line = $this->class_tree[$listnum]['userclass_id'].":"; } // if ($this->graph_debug) { $name_line = varset($this->class_tree[$listnum]['userclass_id'], 'XXX').":"; } if ($this->class_tree[$listnum]['userclass_type'] == UC_TYPE_GROUP) { $name_line .= ''.$this->class_tree[$listnum]['userclass_name'].' '.UCSLAN_84; // Highlight groups } else { $name_line .= $this->class_tree[$listnum]['userclass_name']; } if ($this->graph_debug) $name_line .= "[vis:".$this->class_tree[$listnum]['userclass_visibility'].", edit:".$this->class_tree[$listnum]['userclass_editclass']."] = ".$this->class_tree[$listnum]['userclass_accum']." Children: ".implode(',',$this->class_tree[$listnum]['class_children']); // Next (commented out) line gives a 'conventional' link $ret .= "class icon".$name_line."
"; // $ret .= "class icon".$this->class_tree[$listnum]['userclass_name'].""; //$ret .= "class icon //".$name_line.""; // vertical-align: middle doesn't work! Nor does text-top if ($num_children) { $ret .= "
\n"; $image_level = count($indent_images); if ($is_last) { $indent_images[] = 'linebottom.gif'; } else { $indent_images[] = 'line.gif'; } foreach ($this->class_tree[$listnum]['class_children'] as $p) { $num_children--; if ($num_children) { // Use icon indicating more below $ret .= $this->show_graphical_subtree($p, $indent_images, FALSE); } else { // else last entry on this tree $ret .= $this->show_graphical_subtree($p, $indent_images, TRUE); } } $ret .= "
"; } return $ret; } public function show_graphical_tree($show_debug=FALSE) { $this->graph_debug = $show_debug; $indent_images = array(); $ret = "
class icon
"; // Just a generic icon here to provide a visual anchor $num_parents = count($this->class_parents); foreach ($this->class_parents as $p) { $num_parents--; $ret .= $this->show_graphical_subtree($p, $indent_images, ($num_parents == 0)); } return $ret; } // Creates an array which contains only DB fields (i.e. strips the added status) protected function copy_rec($classrec, $inc_id = FALSE) { $ret = array(); if ($inc_id && isset($classrec['userclass_id'])) $ret['userclass_id'] = $classrec['userclass_id']; foreach ($this->field_list as $fl => $val) { if (isset($classrec[$fl])) $ret[$fl] = $classrec[$fl]; } return $ret; } // Return an unused class ID - FALSE if none spare. Misses the predefined classes. public function findNewClassID() { $i = 1; // Start by allocating a new class with a number higher than any previously allocated foreach ($this->class_tree as $id => $r) { if ($id < e_UC_SPECIAL_BASE) { $i = max($i,$id); } } $i++; if ($i < e_UC_SPECIAL_BASE) return $i; // Looks like we've had a lot of activity in classes - try and find a gap. for ($i = 1; ($i < e_UC_SPECIAL_BASE); $i++) { if (!isset($this->class_tree[$i])) return $i; } // Big system!! Assign a class in the 0.8-only block above 255 for ($i = e_UC_SPECIAL_END+1; ($i < 32767); $i++) { if (!isset($this->class_tree[$i])) return $i; } return FALSE; // Just in case all classes assigned! } // Add new class. Class ID must be in the passed record. // Return TRUE on success, FALSE on failure public function add_new_class($classrec) { if (!isset($classrec['userclass_id'])) { return FALSE; } if ($classrec['userclass_type'] == UC_TYPE_GROUP) { // Need to make sure our ID is in the accumulation array $temp = explode(',',$classrec['userclass_accum']); if (!in_array($classrec['userclass_id'], $temp)) { $temp[] = $classrec['userclass_id']; $classrec['userclass_accum'] = implode(',',$temp); } } if ($this->sql_r->db_Insert('userclass_classes',$this->copy_rec($classrec, TRUE)) === FALSE) { return FALSE; } $this->clearCache(); return TRUE; } public function save_edited_class($classrec) { if (!$classrec['userclass_id']) { echo "Programming bungle on save
"; return FALSE; } $qry = ''; $spacer = ''; if ($classrec['userclass_type'] == UC_TYPE_GROUP) { // Need to make sure our ID is in the accumulation array $temp = explode(',',$classrec['userclass_accum']); if (!in_array($classrec['userclass_id'], $temp)) { $temp[] = $classrec['userclass_id']; $classrec['userclass_accum'] = implode(',',$temp); } } foreach ($this->field_list as $fl => $val) { if (isset($classrec[$fl])) { $qry .= $spacer."`".$fl."` = '".$classrec[$fl]."'"; $spacer = ", "; } } if ($this->sql_r->db_Update('userclass_classes', $qry." WHERE `userclass_id`='{$classrec['userclass_id']}'") === FALSE) { return FALSE; } $this->clearCache(); return TRUE; } public function queryCanDeleteClass($classID) { if (($classID >= e_UC_SPECIAL_BASE) && ($classID <= e_UC_SPECIAL_END)) return FALSE; // Don't allow deletion of fixed classes if (isset($this->fixed_classes[$class_id])) return FALSE; // This picks up classes such as e_UC_PUBLIC outside the main range which can't be deleted if (!isset($this->class_tree[$class_id])) return FALSE; if (count($this->class_tree[$class_id]['class_children'])) return FALSE; // Can't delete class with descendants foreach ($this->class_tree as $c) { if ($c['userclass_editclass'] == $classID) return FALSE; if ($c['userclass_visibility'] == $classID) return FALSE; } return TRUE; } // Delete a class // Return TRUE on success, FALSE on error public function delete_class($classID) { if (self::queryCanDeleteClass($classID) === FALSE) return FALSE; if ($this->sql_r->db_Delete('userclass_classes', "`userclass_id`='{$classID}'") === FALSE) return FALSE; $this->clearCache(); $this->readTree(TRUE); // Re-read the class tree return TRUE; } // Delete a class, and all users who are members of that class. // Return TRUE on success, FALSE on error public function deleteClassAndUsers($classID) { if (self::delete_class($classID) === TRUE) { if ($this->sql_r->db_Select('user', 'user_id, user_class', "user_class REGEXP '(^|,){$classID}(,|$)'")) { $sql2 = new db; while ($row = $this->sql_r->db_Fetch()) { $newClass = self::ucRemove($classID,$row['user_class']); $sql2->db_Update('user', "user_class = '{$newClass}' WHERE user_id = {$row['user_id']}"); } } return TRUE; } return FALSE; } // Moved in from e_userclass class // $uinfoArray is array(uid=>user_class) // Adds all users in list to class $ci public function class_add($cid, $uinfoArray) { $e107 = e107::getInstance(); $uc_sql = new db; foreach($uinfoArray as $uid => $curclass) { if ($curclass) { $newarray = array_unique(array_merge(explode(',', $curclass), array($cid))); $new_userclass = implode(',', $newarray); } else { $new_userclass = $cid; } $uc_sql->db_Update('user', "user_class='".$e107->tp->toDB($new_userclass, true)."' WHERE user_id=".intval($uid)); } } // Moved in from e_userclass class // $uinfoArray is array(uid=>user_class) // Removes all users in list from class $ci public function class_remove($cid, $uinfoArray) { $e107 = e107::getInstance(); $uc_sql = new db; foreach($uinfoArray as $uid => $curclass) { $newarray = array_diff(explode(',', $curclass), array('', $cid)); $new_userclass = implode(',', $newarray); $uc_sql->db_Update('user', "user_class='".$e107->tp->toDB($new_userclass, true)."' WHERE user_id=".intval($uid)); } } // Certain fields on admin records have constraints on their values. // Checks the passed array, and updates any values which are unacceptable. // Returns TRUE if nothing changed, FALSE if changes made public function checkAdminInfo(&$data, $id) { $ret = TRUE; if (($id < e_UC_SPECIAL_BASE) || ($id > e_UC_SPECIAL_END)) return TRUE; if (isset($data['userclass_parent'])) { if (($data['userclass_parent'] < e_UC_SPECIAL_BASE) || ($data['userclass_parent'] > e_UC_SPECIAL_END)) { $data['userclass_parent'] = e_UC_NOBODY; $ret = FALSE; } } if (isset($data['userclass_editclass'])) { if ($id == e_UC_MAINADMIN) { if ($data['userclass_editclass'] < e_UC_MAINADMIN) { $data['userclass_editclass'] = e_UC_MAINADMIN; $ret = FALSE; } } elseif (($data['userclass_editclass'] < e_UC_SPECIAL_BASE) || ($data['userclass_editclass'] > e_UC_SPECIAL_END)) { $data['userclass_editclass'] = e_UC_MAINADMIN; $ret = FALSE; } } return $ret; } // Set default tree structure public function set_default_structure() { // If they don't exist, we need to create class records for the 'standard' user classes $init_list = array( array('userclass_id' => e_UC_MEMBER, 'userclass_name' => UC_LAN_3, 'userclass_description' => UCSLAN_75, 'userclass_editclass' => e_UC_MAINADMIN, 'userclass_parent' => e_UC_PUBLIC, 'userclass_visibility' => e_UC_MEMBER ), array('userclass_id' => e_UC_ADMINMOD, 'userclass_name' => UC_LAN_8, 'userclass_description' => UCSLAN_74, 'userclass_editclass' => e_UC_MAINADMIN, 'userclass_parent' => e_UC_MAINADMIN, 'userclass_visibility' => e_UC_MEMBER ), array('userclass_id' => e_UC_ADMIN, 'userclass_name' => UC_LAN_5, 'userclass_description' => UCSLAN_76, 'userclass_editclass' => e_UC_MAINADMIN, 'userclass_parent' => e_UC_ADMINMOD, 'userclass_visibility' => e_UC_MEMBER ), array('userclass_id' => e_UC_MAINADMIN, 'userclass_name' => UC_LAN_6, 'userclass_description' => UCSLAN_77, 'userclass_editclass' => e_UC_MAINADMIN, 'userclass_parent' => e_UC_NOBODY, 'userclass_visibility' => e_UC_MEMBER ), array('userclass_id' => e_UC_MODS, 'userclass_name' => UC_LAN_7, 'userclass_description' => UCSLAN_78, 'userclass_editclass' => e_UC_MAINADMIN, 'userclass_parent' => e_UC_ADMINMOD, 'userclass_visibility' => e_UC_MEMBER ), array('userclass_id' => e_UC_NEWUSER, 'userclass_name' => UC_LAN_9, 'userclass_description' => UCSLAN_87, 'userclass_editclass' => e_UC_MAINADMIN, 'userclass_parent' => e_UC_MEMBER, 'userclass_visibility' => e_UC_ADMIN ) ); foreach ($init_list as $entry) { if ($this->sql_r->db_Select('userclass_classes','*',"userclass_id='".$entry['userclass_id']."' ")) { $this->sql_r->db_Update('userclass_classes', "userclass_parent='".$entry['userclass_parent']."', userclass_visibility='".$entry['userclass_visibility']."' WHERE userclass_id='".$entry['userclass_id']."'"); } else { $this->add_new_class($entry); } } } public function clearCache() { $e107 = e107::getInstance(); $e107->ecache->clear_sys(UC_CACHE_TAG); } } ?>