1<?php 2/* 3 * e107 website system 4 * 5 * Copyright (C) 2008-2017 e107 Inc (e107.org) 6 * Released under the terms and conditions of the 7 * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) 8 * 9 * User class functions 10 * 11 */ 12 13/** 14 * 15 * @package e107 16 * @subpackage e107_handlers 17 * 18 * This class handles all user-related user class functions. Admin functions inherit from it. 19 */ 20 21if (!defined('e107_INIT')) { exit; } 22 23 24e107::includeLan(e_LANGUAGEDIR.e_LANGUAGE.'/lan_userclass.php'); 25 26 27/* 28Fixed classes occupy a numeric block from e_UC_SPECIAL_BASE to e_UC_SPECIAL_END, plus zero = e_UC_PUBLIC 29(Note that in 0.7/1.0, class numbers stopped at 255. Now they can be up to 65535). 30For info: 31define("e_UC_PUBLIC", 0); 32define("e_UC_MAINADMIN", 250); 33define("e_UC_READONLY", 251); 34define("e_UC_GUEST", 252); 35define("e_UC_MEMBER", 253); 36define("e_UC_ADMIN", 254); 37define("e_UC_NOBODY", 255); 38*/ 39define('e_UC_ADMINMOD' ,249); // Admins (includes main admins) 40define('e_UC_MODS' ,248); // Moderators (who aren't admins) 41define('e_UC_NEWUSER' ,247); // Users in 'probationary' period 42define('e_UC_BOTS' ,246); // Reserved to identify search bots 43 // 243..245 reserved for future predefined user classes 44define('e_UC_SPECIAL_BASE' ,243); // Assign class IDs 243 and above for fixed/special purposes 45define('e_UC_SPECIAL_END' ,255); // Highest 'special' class 46 47define('UC_ICON_DIR', e_IMAGE_ABS.'generic/'); // Directory for the icons used in the admin tree displays 48 49define('e_UC_BLANK' ,'-32767'); // Code for internal use - needs to be large to avoid confusion with 'not a member of...' 50define('UC_TYPE_STD' , '0'); // User class is 'normal' 51define('UC_TYPE_GROUP' , '1'); // User class is a group or list of subsidiary classes 52 53define('UC_CACHE_TAG', 'nomd5_classtree'); 54 55 56class user_class 57{ 58 public $class_tree; // Simple array, filled with current tree. Additional field class_children is an array of child user classes (by ID) 59 protected $class_parents; // Array of class IDs of 'parent' (i.e. top level) classes 60 61 public $fixed_classes = array(); // The 'predefined' core classes (constants beginning 'e_UC_') (would be nice to have this R/O outside) 62 public $text_class_link = array(); // List of 'core' user classes and the related constants 63 64 protected $sql_r; // We'll use our own DB to avoid interactions 65 protected $isAdmin; // Set true if we're an instance of user_class_admin 66 67 68 // Constructor 69 public function __construct() 70 { 71 $this->sql_r = e107::getDb('sql_r'); 72 $this->isAdmin = FALSE; 73 74 $this->fixed_classes = array( 75 e_UC_PUBLIC => UC_LAN_0, 76 e_UC_GUEST => UC_LAN_1, 77 e_UC_NOBODY => UC_LAN_2, 78 e_UC_MEMBER => UC_LAN_3, 79 e_UC_ADMIN => UC_LAN_5, 80 e_UC_MAINADMIN => UC_LAN_6, 81 e_UC_READONLY => UC_LAN_4, 82 e_UC_NEWUSER => UC_LAN_9, 83 e_UC_BOTS => UC_LAN_10 84 ); 85 86 87 88 $this->text_class_link = array('public' => e_UC_PUBLIC, 'guest' => e_UC_GUEST, 'nobody' => e_UC_NOBODY, 'member' => e_UC_MEMBER, 89 'admin' => e_UC_ADMIN, 'main' => e_UC_MAINADMIN, 'new' => e_UC_NEWUSER, 'mods' => e_UC_MODS, 90 'bots' => e_UC_BOTS, 'readonly' => e_UC_READONLY); 91 92 93 94 $this->readTree(TRUE); // Initialise the classes on entry 95 } 96 97 98 public function getFixedClassDescription($id) 99 { 100 if(isset($this->fixed_classes[$id])) 101 { 102 return $this->fixed_classes[$id]; 103 } 104 105 return false; 106 } 107 108 109 /** 110 * Take a key value such as 'member' and return it's numerical value. 111 * @param $text 112 * @return bool 113 */ 114 public function getClassFromKey($text) 115 { 116 if(isset($this->text_class_link[$text])) 117 { 118 return $this->text_class_link[$text]; 119 } 120 121 return false; 122 } 123 124 125 126 /** 127 * Return value of isAdmin 128 */ 129 public function isAdmin() 130 { 131 return $this->isAdmin; 132 } 133 134 /** 135 * Ensure the tree of userclass data is stored in our object ($this->class_tree). 136 * Only read if its either not present, or the $force flag is set. 137 * Data is cached if enabled 138 * 139 * @param boolean $force - set to TRUE to force a re-read of the info regardless. 140 * @return none 141 */ 142 public function readTree($force = FALSE) 143 { 144 if (isset($this->class_tree) && count($this->class_tree) && !$force) return; 145 146 $e107 = e107::getInstance(); 147 148 $this->class_tree = array(); 149 $this->class_parents = array(); 150 151 152 153 if ($temp = $e107->ecache->retrieve_sys(UC_CACHE_TAG)) 154 { 155 $this->class_tree = e107::unserialize($temp); 156 unset($temp); 157 } 158 else 159 { 160 if($this->sql_r->field('userclass_classes','userclass_parent') && $this->sql_r->select('userclass_classes', '*', 'ORDER BY userclass_parent,userclass_name', 'nowhere')) // The order statement should give a consistent return 161 { 162 while ($row = $this->sql_r->fetch()) 163 { 164 $this->class_tree[$row['userclass_id']] = $row; 165 $this->class_tree[$row['userclass_id']]['class_children'] = array(); // Create the child array in case needed 166 } 167 } 168 169 // Add in any fixed classes that aren't already defined (they historically didn't have a DB entry, although now its facilitated (and necessary for tree structure) 170 foreach ($this->fixed_classes as $c => $d) 171 { 172 if (!isset($this->class_tree[$c])) 173 { 174 switch ($c) 175 { 176 case e_UC_ADMIN : 177 case e_UC_MAINADMIN : 178 $this->class_tree[$c]['userclass_parent'] = e_UC_NOBODY; 179 break; 180 case e_UC_NEWUSER : 181 $this->class_tree[$c]['userclass_parent'] = e_UC_MEMBER; 182 break; 183 default : 184 $this->class_tree[$c]['userclass_parent'] = e_UC_PUBLIC; 185 } 186 $this->class_tree[$c]['userclass_id'] = $c; 187 $this->class_tree[$c]['userclass_name'] = $d; 188 $this->class_tree[$c]['userclass_description'] = 'Fixed class'; 189 $this->class_tree[$c]['userclass_visibility'] = e_UC_PUBLIC; 190 $this->class_tree[$c]['userclass_editclass'] = e_UC_MAINADMIN; 191 $this->class_tree[$c]['userclass_accum'] = $c; 192 $this->class_tree[$c]['userclass_type'] = UC_TYPE_STD; 193 } 194 } 195 196 $userCache = e107::serialize($this->class_tree, FALSE); 197 $e107->ecache->set_sys(UC_CACHE_TAG,$userCache); 198 unset($userCache); 199 } 200 201 202 // Now build the tree. 203 // There are just two top-level classes - 'Everybody' and 'Nobody' 204 $this->class_parents[e_UC_PUBLIC] = e_UC_PUBLIC; 205 $this->class_parents[e_UC_NOBODY] = e_UC_NOBODY; 206 foreach ($this->class_tree as $uc) 207 { 208 if (($uc['userclass_id'] != e_UC_PUBLIC) && ($uc['userclass_id'] != e_UC_NOBODY)) 209 { 210 if (!isset($this->class_tree[$uc['userclass_parent']])) 211 { 212 echo "Orphaned class record: ID=".$uc['userclass_id']." Name=".$uc['userclass_name']." Parent=".$uc['userclass_parent']."<br />"; 213 } 214 else 215 { // Add to array 216 $this->class_tree[$uc['userclass_parent']]['class_children'][] = $uc['userclass_id']; 217 } 218 } 219 } 220 } 221 222 223 224 /** 225 * Given the list of 'base' classes a user belongs to, returns a comma separated list including ancestors. Duplicates stripped 226 * 227 * @param string $startList - comma-separated list of classes user belongs to 228 * @param boolean $asArray - if TRUE, result returned as array; otherwise result returned as string 229 * @return string|array of user classes; format determined by $asArray 230 */ 231 public function get_all_user_classes($startList, $asArray = FALSE) 232 { 233 $is = array(); 234 $start_array = explode(',', $startList); 235 236 237 238 foreach ($start_array as $sa) 239 { // Merge in latest values - should eliminate duplicates as it goes 240 $is[] = $sa; // add parent to the flat list first 241 if (isset($this->class_tree[$sa])) 242 { 243 if($this->class_tree[$sa]['userclass_accum']) 244 { 245 $is = array_merge($is,explode(',',$this->class_tree[$sa]['userclass_accum'])); 246 } 247 } 248 } 249 if ($asArray) 250 { 251 return array_unique($is); 252 } 253 return implode(',',array_unique($is)); 254 } 255 256 257 258 /** 259 * Returns a list of user classes which can be edited by the specified classlist 260 * 261 * @param string $classList - comma-separated list of classes to consider - default current user's class list 262 * @param boolean $asArray - if TRUE, result returned as array; otherwise result returned as string 263 * @return string|array of user classes; format determined by $asArray 264 */ 265 public function get_editable_classes($classList = USERCLASS_LIST, $asArray = FALSE) 266 { 267 $ret = array(); 268 $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, e_UC_BOTS => 1); 269 $possibles = array_flip(explode(',',$classList)); 270 unset($possibles[e_UC_READONLY]); 271 272 foreach ($this->class_tree as $uc => $uv) 273 { 274 if (!isset($blockers[$uc])) 275 { 276 $ec = $uv['userclass_editclass']; 277 // $ec = $uv['userclass_visibility']; 278 if (isset($possibles[$ec])) 279 { 280 $ret[] = $uc; 281 } 282 } 283 } 284 if ($asArray) { return $ret; } 285 return implode(',',$ret); 286 } 287 288 289 290 /** 291 * Combines the selected editable classes into the main class list for a user. 292 * 293 * @param array|string $combined - the complete list of current class memberships 294 * @param array|string $possible - the classes which are being edited 295 * @param array|string $actual - the actual membership of the editable classes 296 * @param boolean $asArray - if TRUE, result returned as array; otherwise result returned as string 297 * @return string|array of user classes; format determined by $asArray 298 */ 299 public function mergeClassLists($combined, $possible, $actual, $asArray = FALSE) 300 { 301 if (!is_array($combined)) { $combined = explode(',',$combined); } 302 if (!is_array($possible)) { $possible = explode(',',$possible); } 303 if (!is_array($actual)) { $actual = explode(',',$actual); } 304 $combined = array_flip($combined); 305 foreach ($possible as $p) 306 { 307 if (in_array($p,$actual)) 308 { // Class must be in final array 309 $combined[$p] = 1; 310 } 311 else 312 { 313 unset($combined[$p]); 314 } 315 } 316 $combined = array_keys($combined); 317 if ($asArray) { return $combined; } 318 return implode(',', $combined); 319 } 320 321 322 323 /** 324 * Remove the fixed classes from a class list 325 * Removes all classes in the reserved block, as well as e_UC_PUBLIC 326 * @param array|string $inClasses - the complete list of current class memberships 327 * @return string|array of user classes; format is the same as $inClasses 328 */ 329 public function stripFixedClasses($inClasses) 330 { 331 $asArray = TRUE; 332 if (!is_array($inClasses)) 333 { 334 $asArray = FALSE; 335 $inClasses = explode(',',$inClasses); 336 } 337 /* 338 $inClasses = array_flip($inClasses); 339 foreach ($this->fixed_classes as $k => $v) 340 { 341 if (isset($inClasses[$k])) { unset($inClasses[$k]); } 342 } 343 $inClasses = array_keys($inClasses); 344 */ 345 foreach ($inClasses as $k => $uc) 346 { 347 if ((($uc >= e_UC_SPECIAL_BASE) && ($uc <= e_UC_SPECIAL_END)) || ($uc == e_UC_PUBLIC)) 348 { 349 unset($inClasses[$k]); 350 } 351 } 352 if ($asArray) { return ($inClasses); } 353 return implode(',',$inClasses); 354 } 355 356 357 358 /** 359 * 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) 360 * Requires the class tree to have been initialised 361 * 362 * @param array|string $classList - the complete list of current class memberships 363 * 364 * @return string|array of user classes; format is the same as $classList 365 */ 366 public function normalise_classes($classList) 367 { 368 if (is_array($classList)) 369 { 370 $asArray = TRUE; 371 $oldClasses = $classList; 372 } 373 else 374 { 375 $asArray = FALSE; 376 $oldClasses = explode(',',$classList); 377 } 378 $dropClasses = array(); 379 foreach ($oldClasses as $c) 380 { // Look at our parents (which are in 'userclass_accum') - if any of them are contained in oldClasses, we can drop them. 381 $tc = array_flip(explode(',',$this->class_tree[$c]['userclass_accum'])); 382 unset($tc[$c]); // Current class should be in $tc anyway 383 foreach ($tc as $tc_c => $v) 384 { 385 if (in_array($tc_c,$oldClasses)) 386 { 387 $dropClasses[] = $tc_c; 388 } 389 } 390 } 391 $newClasses = array_diff($oldClasses,$dropClasses); 392 if ($asArray) { return $newClasses; } 393 return implode(',',$newClasses); 394 } 395 396 397 /** 398 * @param string $optlist - comma-separated list of classes/class types to be included in the list 399 It allows selection of the classes to be shown in the dropdown. All or none can be included, separated by comma. Valid options are: 400 public 401 guest 402 nobody 403 member 404 readonly 405 admin 406 main - main admin 407 new - new users 408 bots - search bot class 409 classes - shows all classes 410 matchclass - if 'classes' is set, this option will only show the classes that the user is a member of 411 * @return array 412 */ 413 public function getClassList($optlist) 414 { 415 return $this->uc_required_class_list($optlist); 416 } 417 418 419 /** 420 * Generate a dropdown list of user classes from which to select - virtually as the deprecated r_userclass() function did 421 * [ $mode parameter of r_userclass() removed - $optlist is more flexible) ] 422 * 423 * @param string $fieldname - name of select list 424 * @param mixed $curval - current selected value (empty string if no current value) 425 * @param string $optlist - comma-separated list of classes/class types to be included in the list 426 It allows selection of the classes to be shown in the dropdown. All or none can be included, separated by comma. Valid options are: 427 public 428 guest 429 nobody 430 member 431 readonly 432 admin 433 main - main admin 434 new - new users 435 bots - search bot class 436 classes - shows all classes 437 matchclass - if 'classes' is set, this option will only show the classes that the user is a member of 438 blank - puts an empty option at the top of select dropdowns 439 440 filter - only show those classes where member is in a class permitted to view them - e.g. as the new 'visible to' field - added for 2.0 441 force - show all classes (subject to the other options, including matchclass) - added for 2.0 442 all - alias for 'force' 443 444 no-excludes - if present, doesn't show the 'not member of' list 445 is-checkbox - if present, suppresses the <optgroup...> construct round the 'not member of' list 446 447 editable - can only appear on its own - returns list of those classes the user can edit (manage) 448 449 * @param string $extra_js - can add JS handlers (e.g. 'onclick', 'onchange') if required 450 */ 451 public function uc_dropdown($fieldname, $curval = 0, $optlist = '', $extra_js = '') 452 { 453 $show_classes = self::uc_required_class_list($optlist); // Get list of classes which meet criteria 454 455 $text = ''; 456 foreach ($show_classes as $k => $v) 457 { 458 if ($k == e_UC_BLANK) 459 { 460 $text .= "<option value=''> </option>\n"; 461 } 462 else 463 { 464 $s = ($curval == $k && $curval !== '') ? "selected='selected'" : ''; 465 $text .= "<option class='uc-select' value='".$k."' ".$s.">".$v."</option>\n"; 466 } 467 } 468 469 if(is_array($extra_js)) 470 { 471 $options = $extra_js; 472 unset($extra_js); 473 } 474 475 $class = "tbox form-control"; 476 477 if(!empty($options['class'])) 478 { 479 $class .= " ".$options['class']; 480 } 481 482 483 // Inverted Classes 484 if(strpos($optlist, 'no-excludes') === FALSE) 485 { 486 if (strpos($optlist, 'is-checkbox') !== FALSE) 487 { 488 $text .= "\n".UC_LAN_INVERTLABEL."<br />\n"; 489 } 490 else 491 { 492 $text .= "\n"; 493 $text .= '<optgroup label=\''.UC_LAN_INVERTLABEL.'\'>'; 494 $text .= "\n"; 495 } 496 foreach ($show_classes as $k => $v) 497 { 498 if($k != e_UC_PUBLIC && $k != e_UC_NOBODY && $k != e_UC_READONLY) // remove everyone, nobody and readonly from list. 499 { 500 $s = ($curval == ('-'.$k) && $curval !== '') ? "selected='selected'" : ''; 501 $text .= "<option class='uc-select-inverted' value='-".$k."' ".$s.">".str_replace("[x]", $v, UC_LAN_INVERT)."</option>\n"; 502 } 503 } 504 $text .= "</optgroup>\n"; 505 } 506 507 // Only return the select box if we've ended up with some options 508 if ($text) $text = "\n<select class='".$class."' name='{$fieldname}' id='{$fieldname}' {$extra_js}>\n".$text."</select>\n"; 509 return $text; 510 } 511 512 513 514 /** 515 * Generate an ordered array classid=>classname - used for dropdown and check box lists 516 * 517 * @param string $optlist - comma-separated list of classes/class types to include (see uc_dropdown for details) 518 * @param boolean $just_ids - if TRUE, each returned array value is '1'; otherwise it is the class name 519 * @return array of user classes; ky is numeric class id, value is '1' or class name according to $just_ids 520 */ 521 public function uc_required_class_list($optlist = '', $just_ids = FALSE) 522 { 523 $ret = array(); 524 $opt_arr = array(); 525 526 if ($optlist) 527 { 528 $opt_arr = array_map('trim', explode(',',$optlist)); 529 } 530 531 $opt_arr = array_flip($opt_arr); // This also eliminates duplicates which could arise from applying the other options, although shouldn't matter 532 533 if (isset($opt_arr['no-excludes'])) unset($opt_arr['no-excludes']); 534 if (isset($opt_arr['is-checkbox'])) unset($opt_arr['is-checkbox']); 535 536 if (count($opt_arr) == 0) 537 { 538 $opt_arr = array('public' => 1, 'guest' => 1, 'nobody' => 1, 'member' => 1, 'classes' => 1); 539 } 540 541 if (isset($opt_arr['all'])) 542 { 543 unset($opt_arr['all']); 544 $opt_arr['force'] = 1; 545 } 546 547 if (isset($opt_arr['editable'])) 548 { 549 $temp = array_flip(explode(',',$this->get_editable_classes())); 550 if ($just_ids) return $temp; 551 foreach ($temp as $c => $t) 552 { 553 $temp[$c] = $this->class_tree[$c]['userclass_name']; 554 } 555 return $temp; 556 } 557 558 559 560 if (isset($opt_arr['force'])) unset($opt_arr['filter']); 561 562 if (isset($opt_arr['blank'])) 563 { 564 $ret[e_UC_BLANK] = 1; 565 } 566 567 // Do the 'fixed' classes next 568 foreach ($this->text_class_link as $k => $v) 569 { 570 // if (isset($opt_arr[$k]) || isset($opt_arr['force'])) 571 if (isset($opt_arr[$k])) 572 { 573 $ret[$v] = $just_ids ? '1' : $this->fixed_classes[$v]; 574 } 575 } 576 577 // Now do the user-defined classes 578 if (isset($opt_arr['classes']) || isset($opt_arr['force'])) 579 { // Display those classes the user is allowed to: 580 // Main admin always sees the lot 581 // a) Mask the 'fixed' user classes which have already been processed 582 // b) Apply the visibility option field ('userclass_visibility') 583 // c) Apply the matchclass option if appropriate 584 foreach($this->class_tree as $uc_id => $row) 585 { 586 if (!array_key_exists($uc_id,$this->fixed_classes) 587 && ( getperms('0') 588 || ( 589 (!isset($opt_arr['matchclass']) || check_class($uc_id)) 590 && 591 (!isset($opt_arr['filter']) || check_class($row['userclass_visibility'])) 592 ) 593 ) 594 ) 595 { 596 $ret[$uc_id] = $just_ids ? '1' : $this->class_tree[$uc_id]['userclass_name']; 597 } 598 } 599 } 600 /* Above loop slightly changes the display order of earlier code versions. 601 If readonly must be last, delete it from the $text_class_link array, and uncomment the following code 602 603 if (isset($opt_arr['readonly'])) 604 { 605 $ret[e_UC_READONLY] = $this->class_tree[e_UC_READONLY]['userclass_description']; 606 } 607 */ 608 609 return $ret; 610 } 611 612 613 /** 614 * Very similar to self::uc_dropdown, but returns a list of check boxes. Doesn't encapsulate it. 615 * 616 * @param string $fieldname is the name for the array of checkboxes 617 * @param string $curval is a comma separated list of class IDs for boxes which are checked. 618 * @param string $optlist as for uc_dropdown 619 * @param boolean $showdescription - if TRUE, appends the class description in brackets 620 * @param boolean $asArray - if TRUE, result returned as array; otherwise result returned as string 621 * 622 * return string|array according to $asArray 623 * @return array|string 624 */ 625 public function uc_checkboxes($fieldname, $curval='', $optlist = '', $showdescription = FALSE, $asArray = FALSE) 626 { 627 $show_classes = $this->uc_required_class_list($optlist); 628 $frm = e107::getForm(); 629 630 $curArray = explode(',', $curval); // Array of current values 631 $ret = array(); 632 633 foreach ($show_classes as $k => $v) 634 { 635 if ($k != e_UC_BLANK) 636 { 637 // $c = (in_array($k,$curArray)) ? " checked='checked'" : ''; 638 $c = (in_array($k,$curArray)) ? true : false; 639 if ($showdescription) $v .= ' ('.$this->uc_get_classdescription($k).')'; 640 //$ret[] = "<div class='field-spacer'><input type='checkbox' class='checkbox' name='{$fieldname}[{$k}]' id='{$fieldname}-{$k}' value='{$k}'{$c} /><label for='{$fieldname}-{$k}'>".$v."</label></div>\n"; 641 $name = $fieldname.'['.$k.']'; 642 $ret[] = $frm->checkbox($name,$k,$c,$v); 643 //$ret[] = "<div class='field-spacer'><input type='checkbox' class='checkbox' name='{$fieldname}[{$k}]' id='{$fieldname}-{$k}' value='{$k}'{$c} /><label for='{$fieldname}-{$k}'>".$v."</label></div>\n"; 644 645 } 646 } 647 if ($asArray) return $ret; 648 return implode('', $ret); 649 } 650 651 652 653 /** 654 * Used by @see{vetted_tree()} to generate lower levels of tree 655 * 656 * @param string $listnum - class number of the parent. Is negative if the class is 'Everyone except...' (Must be a string because 0 == -0) 657 * @param integer $nest_level - indicates our level in the tree - 0 is the top level; increases as we descend the tree. Positive value. 658 * @param string $current_value - comma-separated list of integers indicating classes selected. (Spaces not permitted) 659 * @param array $perms - list of classes we are allowed to display 660 * @param string $opt_options - passed to callback function; not otherwise used 661 */ 662 protected function vetted_sub_tree($treename, $callback, $listnum, $nest_level, $current_value, $perms, $opt_options) 663 { 664 $ret = ''; 665 $nest_level++; 666 $listIndex = abs($listnum); 667 $classSign = (substr($listnum, 0, 1) == '-') ? '-' : '+'; 668 //echo "Subtree: {$listnum}, {$nest_level}, {$current_value}, {$classSign}:{$listIndex}<br />"; 669 if(isset($this->class_tree[$listIndex]['class_children'])) 670 { 671 foreach ($this->class_tree[$listIndex]['class_children'] as $p) 672 { 673 $classValue = $classSign.$p; 674 // Looks like we don't need to differentiate between function and class calls 675 if (isset($perms[$p])) 676 { 677 $ret .= call_user_func($callback, $treename, $classValue, $current_value, $nest_level, $opt_options); 678 } 679 680 $ret .= $this->vetted_sub_tree($treename, $callback, $classValue, $nest_level, $current_value, $perms, $opt_options); 681 } 682 683 684 } 685 return $ret; 686 } 687 688 689 /** 690 * create an indented tree - for example within a select box or a list of check boxes. 691 * For each displayed element, the callback routine is called 692 * @param string $treename is the name given to the elements where required 693 * @param function|object $callback is a routine used to generate each element; there are three implemented within this class: 694 * select (the default) - generates the option list. Text requires to be encapsulated in a <select......./select> tag set 695 * - can also be used with multi-select boxes 696 * checkbox - generates a set of checkboxes 697 * checkbox_desc - generates a set of checkboxes with the class description in brackets 698 * Alternative callbacks can be used to achieve different layouts/styles 699 * @param integer|string $current_value - single class number for single-select dropdown; comma separated array of class numbers for checkbox list or multi-select 700 * @param string $optlist works the same as for @see uc_dropdown() 701 * @param string $opt_options - passed to callback function; not otherwise used 702 * @return string - formatted HTML for tree 703 */ 704 public function vetted_tree($treename, $callback='', $current_value='', $optlist = '', $opt_options = '') 705 { 706 $ret = ''; 707 if (!$callback) $callback=array($this,'select'); 708 $current_value = str_replace(' ','',$current_value); // Simplifies parameter passing for the tidy-minded 709 $notCheckbox = (strpos($optlist, 'is-checkbox') === FALSE); 710 711 $perms = $this->uc_required_class_list($optlist,TRUE); // List of classes which we can display 712 if (isset($perms[e_UC_BLANK])) 713 { 714 $ret .= call_user_func($callback, $treename, e_UC_BLANK, $current_value, 0, $opt_options); 715 } 716 foreach ($this->class_parents as $p) 717 { 718 if (isset($perms[$p])) 719 { 720 $ret .= call_user_func($callback, $treename, $p, $current_value, 0, $opt_options); 721 } 722 $ret .= $this->vetted_sub_tree($treename, $callback, $p, 0, $current_value, $perms, $opt_options); 723 } 724 725 726 // Inverted classes. (negative values for exclusion). 727 if(strpos($optlist, 'no-excludes') === FALSE) 728 { 729 if ($notCheckbox) 730 { 731 $ret .= "\n"; 732 $ret .= '<optgroup label=\''.UC_LAN_INVERTLABEL.'\'>'; 733 $ret .= "\n"; 734 } 735 else 736 { 737 $ret .= "\n".UC_LAN_INVERTLABEL."<br />\n"; 738 } 739 foreach ($this->class_parents as $k => $p) // Currently key and data are the same 740 { 741 //echo "Class parent: {$k}:{$p}<br />"; 742 if($k != e_UC_PUBLIC && $k != e_UC_NOBODY && $k != e_UC_READONLY) // remove everyone, nobody and readonly from list. 743 { 744 if (isset($perms[$p])) 745 { 746 $ret .= call_user_func($callback, $treename, '-'.$p, $current_value, 0, $opt_options); 747 } 748 } 749 $ret .= $this->vetted_sub_tree($treename, $callback, '-'.$p, 0, $current_value, $perms, $opt_options); 750 } 751 if ($notCheckbox) 752 { 753 $ret .= "</optgroup>\n"; 754 } 755 } 756 return $ret; 757 } 758 759 760 /** 761 * Callback for vetted_tree - Creates the option list for a selection box 762 * It is the caller's responsibility to enclose this list in a <select...../select> structure 763 * Can be used as a basis for similar functions 764 * 765 * @param string $treename - name of tree elements (not used with select; used with checkboxes, for example) 766 * @param string $classnum - user class being displayed. This may be negative to indicate 'everyone but...' 767 * - special numeric part e_UC_BLANK adds a blank option in the list. 768 * @param integer|string $current_value - single class number for single-select dropdown; comma separated array of class numbers for checkbox list or multi-select 769 * @param integer $nest_level - 'depth' of this item in the tree. Zero is base level. May be used to indent or highlight dependent on level 770 * @param string $opt_options - passed to callback function; not otherwise used 771 * 772 * @return string - option list 773 */ 774 public function select($treename, $classnum, $current_value, $nest_level, $opt_options = '') 775 { 776 $classIndex = abs($classnum); // Handle negative class values 777 $classSign = (substr($classnum, 0, 1) == '-') ? '-' : ''; 778 if ($classIndex == e_UC_BLANK) return "<option value=''> </option>\n"; 779 $tmp = explode(',',$current_value); 780 $sel = in_array($classnum, $tmp) ? " selected='selected'" : ''; 781 if ($nest_level == 0) 782 { 783 $prefix = ''; 784 $style = " style='font-weight:bold; font-style: italic;'"; 785 } 786 elseif ($nest_level == 1) 787 { 788 $prefix = ' '; 789 $style = " style='font-weight:bold'"; 790 } 791 else 792 { 793 $prefix = ' '.str_repeat('--',$nest_level-1).'>'; 794 $style = ''; 795 } 796 $ucString = $this->class_tree[$classIndex]['userclass_name']; 797 if ($classSign == '-') 798 { 799 $ucString = str_replace('[x]', $ucString, UC_LAN_INVERT); 800 } 801 return "<option value='{$classSign}{$classIndex}'{$sel}{$style}>".$prefix.$ucString."</option>\n"; 802 } 803 804 805 /** 806 * Callback for vetted_tree - displays indented checkboxes with class name only 807 * See @link select for parameter details 808 */ 809 public function checkbox($treename, $classnum, $current_value, $nest_level, $opt_options = '') 810 { 811 $frm = e107::getForm(); 812 813 $classIndex = abs($classnum); // Handle negative class values 814 $classSign = (substr($classnum, 0, 1) == '-') ? '-' : ''; 815 816 if ($classIndex == e_UC_BLANK) return ''; 817 818 $tmp = explode(',',$current_value); 819 $chk = in_array($classnum, $tmp) ? " checked='checked'" : ''; 820 $style = ""; 821 822 if ($nest_level == 0) 823 { 824 $style = " style='font-weight:bold'"; 825 } 826 elseif($nest_level > 1) 827 { 828 $style = " style='text-indent:".(1.2 * $nest_level)."em'"; 829 } 830 831 $ucString = $this->class_tree[$classIndex]['userclass_name']; 832 833 if ($classSign == '-') 834 { 835 $ucString = str_replace('[x]', $ucString, UC_LAN_INVERT); 836 } 837 838 $checked = in_array($classnum, $tmp) ? true : false; 839 return "<div {$style}>".$frm->checkbox($treename.'[]',$classSign.$classIndex,$checked,array('label'=> $ucString))."</div>\n"; 840 841 842 return "<div {$style}><input type='checkbox' class='checkbox' name='{$treename}[]' id='{$treename}_{$classSign}{$classIndex}' value='{$classSign}{$classIndex}'{$chk} />".$ucString."</div>\n"; 843 } 844 845 846 /** 847 * Callback for vetted_tree - displays indented checkboxes with class name, and description in brackets 848 * See @link select for parameter details 849 */ 850 public function checkbox_desc($treename, $classnum, $current_value, $nest_level, $opt_options = '') 851 { 852 $classIndex = abs($classnum); // Handle negative class values 853 $classSign = (substr($classnum, 0, 1) == '-') ? '-' : ''; 854 855 if ($classIndex == e_UC_BLANK) return ''; 856 857 $tmp = explode(',',$current_value); 858 $chk = in_array($classnum, $tmp) ? " checked='checked'" : ''; 859 860 if ($nest_level == 0) 861 { 862 $style = " style='font-weight:bold'"; 863 } 864 else 865 { 866 $style = " style='text-indent:".(0.3 * $nest_level)."em'"; 867 } 868 869 $id = "{$treename}_{$classnum}"; 870 871 $ucString = $this->class_tree[$classIndex]['userclass_name']; 872 873 if ($classSign == '-') 874 { 875 $ucString = str_replace('[x]', $ucString, UC_LAN_INVERT); 876 } 877 878 $description = $ucString.' ('.$this->class_tree[$classIndex]['userclass_description'].")"; 879 880 $id ="{$treename}_{$classSign}{$classnum}"; 881 882 return "<div class='checkbox' {$style}>".e107::getForm()->checkbox($treename.'[]', $classnum , $chk, array("id"=>$id,'label'=>$description))."</div>\n"; 883 884 // return "<div {$style}><input type='checkbox' class='checkbox' name='{$treename}[]' id='{$treename}_{$classSign}{$classnum}' value='{$classSign}{$classnum}'{$chk} />".$this->class_tree[$classIndex]['userclass_name'].' ('.$this->class_tree[$classIndex]['userclass_description'].")</div>\n"; 885 } 886 887 888 889 890 /** 891 * Return array of all classes, limited according to membership of the userclass_visibility field if $filter is set. 892 * @param string|integer $filter - user class or class list in format acceptable to check_class() 893 * @return array of class elements, each itself an array: 894 * Index field - userclass_id 895 * Data fields - userclass_name, userclass_description, userclass_editclass 896 */ 897 public function uc_get_classlist($filter = FALSE) 898 { 899 $ret = array(); 900 $this->readTree(FALSE); // Make sure we have data 901 foreach ($this->class_tree as $k => $v) 902 { 903 if (!$filter || check_class($filter)) 904 { 905 $ret[$k] = array('userclass_name' => $v, 'userclass_description' => $v['userclass_description'], 'userclass_editclass' => $v['userclass_editclass']); 906 } 907 } 908 return $ret; 909 } 910 911 912 913 /** 914 * Return class name for given class ID 915 * Handles 'not a member of...' construct by replacing '--CLASS--' in UC_LAN_INVERT with the class name 916 * @param integer $id - class number. A negative number indicates 'not a member of...' 917 * @return string class name 918 */ 919 public function getName($id) 920 { 921 $cn = abs($id); 922 $ucString = 'Class:'.$id; // Debugging aid - this should be overridden 923 924 if (isset($this->class_tree[$cn])) 925 { 926 $ucString = $this->class_tree[$cn]['userclass_name']; 927 } 928 elseif (isset($this->fixed_classes[$cn])) 929 { 930 $ucString = $this->fixed_classes[$cn]; 931 } 932 933 if($id < 0) 934 { 935 //$val = abs($id); 936 //$name = isset($this->class_tree[$val]['userclass_name']) ? $this->class_tree[$val]['userclass_name'] : $this->fixed_classes[$val]; 937 $ucString = str_replace('[x]', $ucString, UC_LAN_INVERT); 938 } 939 return $ucString; 940 } 941 942 943 944 /** 945 * Return a key-name identifier for given class ID 946 * @param integer $id - class number. A negative number indicates 'not a member of...' 947 * @return string class name ke 948 */ 949 public function getIdentifier($id) 950 { 951 $cn = abs($id); 952 953 $ucString = ''; 954 955 $fixedClasses = array_flip($this->text_class_link); 956 957 if(isset($fixedClasses[$cn])) 958 { 959 return $fixedClasses[$cn]; 960 } 961 962 if(isset($this->class_tree[$cn])) 963 { 964 return e107::getForm()->name2id($this->class_tree[$cn]['userclass_name']); 965 } 966 967 return $ucString; 968 } 969 970 971 /** 972 * Return class description for given class ID 973 * @param integer $id - class number. Must be >= 0 974 * @return string class description 975 */ 976 public function getDescription($id) 977 { 978 $id = intval($id); 979 980 if(isset($this->class_tree[$id])) 981 { 982 return $this->class_tree[$id]['userclass_description']; 983 } 984 if (isset($this->fixed_classes[$id])) 985 { 986 return $this->fixed_classes[$id]; // Name and description the same for fixed classes 987 } 988 return ''; 989 } 990 991 992 993 /** 994 * BC Alias. of getName(); 995 * @deprecated 996 * @param $id 997 * @return string 998 */ 999 public function uc_get_classname($id) 1000 { 1001 return $this->getName($id); 1002 } 1003 1004 1005 1006 1007 /** 1008 * BC Alias of getDescription 1009 * @deprecated 1010 * @param $id 1011 * @return mixed 1012 */ 1013 public function uc_get_classdescription($id) 1014 { 1015 return $this->getDescription($id); 1016 } 1017 1018 1019 1020 /** 1021 * Return class icon for given class ID 1022 * @param integer $id - class number. Must be >= 0 1023 * @return string class icon if defined, otherwise empty string 1024 */ 1025 public function uc_get_classicon($id) 1026 { 1027 if (isset($this->class_tree[$id])) 1028 { 1029 return $this->class_tree[$id]['userclass_icon']; 1030 } 1031 return ''; 1032 } 1033 1034 1035 1036 1037 1038 /** 1039 * Look up class ID for a given class name 1040 * @param string $name - class name 1041 * @return integer|boolean FALSE if not found, else user class ID 1042 */ 1043 public function getID($name) 1044 { 1045 $this->readTree(); 1046 // We have all the info - can just search the array 1047 foreach ($this->class_tree as $uc => $info) 1048 { 1049 if ($info['userclass_name'] == $name) 1050 { 1051 return $uc; 1052 } 1053 } 1054 return FALSE; // not found 1055 } 1056 1057 1058 1059 1060 1061 /** 1062 * BC Alias of getID(); 1063 * @param $name 1064 * @return mixed 1065 */ 1066 public function ucGetClassIDFromName($name) 1067 { 1068 return $this->getID($name); 1069 1070 } 1071 1072 /** 1073 * Utility to remove a specified class ID from the default comma-separated list 1074 * Optional conversion to array of classes 1075 * @param integer $classID - class number. Must be >= 0 1076 * @param string $from - comma separated list of class numbers 1077 * @param boolean $asArray - if TRUE, result returned as array; otherwise result returned as string 1078 * @return string class description 1079 */ 1080 public function ucRemove($classID, $from, $asArray = FALSE) 1081 { 1082 $tmp = array_flip(explode(',',$from)); 1083 if (isset($tmp[$classID])) 1084 { 1085 unset($tmp[$classID]); 1086 } 1087 $tmp = array_keys($tmp); 1088 if ($asArray) { return $tmp; } 1089 if (count($tmp) == 0) { return ''; } 1090 return implode(',',$tmp); 1091 } 1092 1093 1094 1095 /** 1096 * Utility to add a specified class ID to the default comma-separated list 1097 * Optional conversion to array of classes 1098 * @param integer $classID - class number. Must be >= 0 1099 * @param string $to - comma separated list of class numbers 1100 * @param boolean $asArray - if TRUE, result returned as array; otherwise result returned as string 1101 * @return string class description 1102 */ 1103 public function ucAdd($classID, $to, $asArray = FALSE) 1104 { 1105 $tmp = array_flip(explode(',',$to)); 1106 $tmp[$classID] = 1; 1107 $tmp = array_keys($tmp); 1108 if ($asArray) { return $tmp; } 1109 return implode(',',$tmp); 1110 } 1111 1112 1113 1114 /** 1115 * See if a class can be edited (in the sense of the class ID not being fixed) 1116 * @param integer $classID - class number. Must be >= 0 1117 * @return boolean - TRUE if class can be edited 1118 */ 1119 public function isEditableClass($classID) 1120 { 1121 if (($classID >= e_UC_SPECIAL_BASE) && ($classID <= e_UC_SPECIAL_END)) return FALSE; // Don't allow deletion of fixed classes 1122 if (isset($this->fixed_classes[$classID])) return FALSE; // This picks up classes such as e_UC_PUBLIC outside the main range which can't be deleted 1123 return TRUE; 1124 } 1125 1126 1127 /** 1128 * @deprecated Alias of getUsersInClass() 1129 * @param $classes 1130 * @param string $fieldList 1131 * @param bool $includeAncestors 1132 * @param string $orderBy 1133 * @return array 1134 */ 1135 public function get_users_in_class($classes, $fieldList = 'user_name, user_loginname', $includeAncestors = FALSE, $orderBy = 'user_id') 1136 { 1137 return $this->getUsersInClass($classes, $fieldList, $includeAncestors, $orderBy); 1138 } 1139 1140 1141 /** 1142 * Return all users in a particular class or set of classes. 1143 * 1144 * Could potentially be verrrrryyyy slow - has to scan the whole user database at present. 1145 * @param string $$classes - comma separated list of classes 1146 * @param string $fields - comma separated list of fields to be returned. `user_id` is always returned as the key of the array entry, prefix with 'ue.' to retrieve extended user fields. 1147 * @param boolean $includeAncestors - if TRUE, also looks for classes in the hierarchy; otherwise checks exactly the classes passed 1148 * @param string $orderBy - optional field name to define the order of entries in the results array 1149 * @return array indexed by user_id, each element is an array (database row) containing the requested fields 1150 */ 1151 public function getUsersInClass($classes, $fields = 'user_name, user_loginname', $includeAncestors = false, $orderBy = 'user_id') 1152 { 1153 1154 $classes = str_replace(' ','', $classes); // clean up white spaces 1155 1156 $classList = explode(",", $classes); 1157 1158 if(empty($classList)) 1159 { 1160 return array(); 1161 } 1162 1163 if($includeAncestors === true) 1164 { 1165 $classList = $this->get_all_user_classes($classes, true); 1166 } 1167 1168 $classList = array_flip($classList); 1169 1170 $qry = array(); 1171 1172 if(isset($classList[e_UC_MEMBER])) 1173 { 1174 $qry[] = "user_ban = 0"; 1175 unset($classList[e_UC_MEMBER]); 1176 } 1177 1178 if(isset($classList[e_UC_ADMIN])) 1179 { 1180 $qry[] = "user_admin = 1"; 1181 unset($classList[e_UC_ADMIN]); 1182 } 1183 1184 if(isset($classList[e_UC_MAINADMIN])) 1185 { 1186 $qry[] = "user_perms = '0' OR user_perms = '0.'"; 1187 unset($classList[e_UC_MAINADMIN]); 1188 } 1189 1190 if(!empty($classList)) 1191 { 1192 $class_regex = implode('|', array_flip($classList)); 1193 $regex = "(^|,)(".e107::getParser()->toDB($class_regex).")(,|$)"; 1194 $qry[] = "user_class REGEXP '{$regex}' "; 1195 } 1196 1197 if(empty($qry)) 1198 { 1199 return array(); 1200 } 1201 1202 $sql = e107::getDb('sql_r'); 1203 1204 $ret = array(); 1205 1206 $lj = strpos($fields,'ue.') !== false ? "LEFT JOIN `#user_extended` AS ue ON user_id = ue.user_extended_id " : ""; 1207 1208 $query = "SELECT user_id,{$fields} FROM `#user` ".$lj." WHERE ".implode(" OR ",$qry)." ORDER BY ".$orderBy; 1209 1210 if ($sql->gen($query)) 1211 { 1212 while ($row = $sql->fetch()) 1213 { 1214 $row['user_id'] = (int) $row['user_id']; 1215 $ret[$row['user_id']] = $row; 1216 } 1217 1218 } 1219 1220 return $ret; 1221 } 1222 1223 1224 /** 1225 * Clear user class cache 1226 * @return void 1227 */ 1228 public function clearCache() 1229 { 1230 e107::getCache()->clear_sys(UC_CACHE_TAG); 1231 } 1232} 1233 1234 1235/**======================================================================== 1236 * Functions from previous userclass_class handler 1237 * ======================================================================== 1238 * Implemented for backwards compatibility/convenience. 1239 1240 * ************** DEPRECATED - use new class-based functions ************** 1241 * 1242 * Functionality should be sufficiently similar to the original functions to not notice (and will 1243 * be more consistent with the preferred class-based functions). 1244 * 1245 * Refer to the corresponding class-based functions for full details 1246 * 1247 * Consistent interface: 1248 * 1249 * @param string $fieldname - name to use for the element 1250 * @param mixed $curval - current value of field - as CSV if multiple values 1251 * @param string $mode = (off|admin...) - affects display - 1252 * @param string $optlist - comma-separated list which specifies the classes to be shown: 1253 * public 1254 * guest 1255 * nobody 1256 * member 1257 * readonly 1258 * admin 1259 * main - main admin 1260 * classes - shows all classes 1261 * matchclass - if 'classes' is set, this option will only show the classes that the user is a member of 1262 * 1263 * filter - only show those classes where member is in a class permitted to view them - i.e. as the new 'visible to' field 1264 * force - show all classes (subject to the other options, including matchclass) 1265 * 1266 * $mode = 'off' turns off listing of admin/main admin classes unless enabled in $optlist (can probably be deprecated - barely used) 1267 * 1268 */ 1269 1270 1271function r_userclass($fieldname, $curval = 0, $mode = "off", $optlist = "") 1272{ 1273 // echo "Call r_userclass{$fieldname}, CV: {$curval} opts: {$optlist}<br />"; 1274 global $e_userclass; 1275 if ($mode != 'off') 1276 { // Handle legacy code 1277 if ($optlist) $optlist .= ','; 1278 $optlist .= 'admin,main'; 1279 if ($mode != 'admin') $optlist .= ',readonly'; 1280 } 1281 if (!is_object($e_userclass)) $e_userclass = new user_class; 1282 return $e_userclass->uc_dropdown($fieldname,$curval,$optlist); 1283} 1284 1285 1286// Very similar to r_userclass, but returns a list of check boxes. (currently only used in newspost.php) 1287// $curval is a comma separated list of class IDs for boxes which are checked. 1288function r_userclass_check($fieldname, $curval = '', $optlist = "") 1289{ 1290 // echo "Call r_userclass_check: {$curval}<br />"; 1291 global $e_userclass; 1292 if (!is_object($e_userclass)) $e_userclass = new user_class; 1293 $ret = $e_userclass->uc_checkboxes($fieldname,$curval,$optlist); 1294 if ($ret) $ret = "<div class='check-block'>".$ret."</div>"; 1295 return $ret; 1296} 1297 1298 1299 1300function get_userclass_list() 1301{ 1302// echo "Call get_userclass_list<br />"; 1303 global $e_userclass; 1304 if (!is_object($e_userclass)) $e_userclass = new user_class; 1305 return $e_userclass->uc_get_classlist(); 1306} 1307 1308 1309 1310function r_userclass_name($id) 1311{ 1312// echo "Call r_userclass_name<br />"; 1313 global $e_userclass; 1314 if (!is_object($e_userclass)) $e_userclass = new user_class; 1315 return $e_userclass->uc_get_classname($id); 1316} 1317 1318 1319 1320 1321 1322// Deprecated functions to hopefully be removed 1323function r_userclass_radio($fieldname, $curval = '') 1324{ 1325 echo "Deprecated function r_userclass_radio not used in core - mutter if you'd like it implemented<br />"; 1326} 1327 1328//======================================================================== 1329// Admin Class handler - could go into separate file later 1330//======================================================================== 1331 1332class user_class_admin extends user_class 1333{ 1334 protected $field_list = array('userclass_name' => "varchar(100) NOT NULL default ''", 1335 'userclass_description' => "varchar(250) NOT NULL default ''", 1336 'userclass_editclass' => "tinyint(3) unsigned NOT NULL default '0'", 1337 'userclass_parent' => "tinyint(3) unsigned NOT NULL default '0'", 1338 'userclass_accum' => "varchar(250) NOT NULL default ''", 1339 'userclass_visibility' => "tinyint(3) unsigned NOT NULL default '0'", 1340 'userclass_type' =>"tinyint(1) unsigned NOT NULL default '0'", 1341 'userclass_icon' => "varchar(250) NOT NULL default ''" 1342 ); // Note - 'userclass_id' intentionally not in this list 1343 1344 1345 protected $graph_debug = FALSE; // Shows extra info on graphical tree when TRUE 1346 1347 // Icons to use for graphical tree display 1348 // First index - no children, children 1349 // Second index - not last item, last item 1350 // Third index - closed tree, open tree 1351 protected $tree_icons = array( 1352 FALSE => array( // No children 1353 FALSE => array( // Not last item 1354 FALSE => '', // Closed tree - don't display 1355 TRUE => 'branch.gif' 1356 ) 1357 , 1358 TRUE => array( // Last item 1359 FALSE => '', // Closed tree - don't display 1360 TRUE => 'branchbottom.gif' 1361 ) 1362 ), 1363 TRUE => array( // children 1364 FALSE => array( // Not last item 1365 FALSE => 'plus.gif', // Closed tree - option to expand 1366 TRUE => 'minus.gif' 1367 ) 1368 , 1369 TRUE => array( // Last item 1370 FALSE => 'plusbottom.gif', // Closed tree - option to expand 1371 TRUE => 'minusbottom.gif' 1372 )) 1373 ); 1374 1375 protected $top_icon = null; 1376 1377 1378 /** 1379 * Constructor 1380 */ 1381 public function __construct() 1382 { 1383 parent::__construct(); 1384 if (!(getperms('4') || getperms('0'))) return; 1385 1386 $this->isAdmin = TRUE; // We have full class management rights 1387 1388 $pref = e107::getPref(); 1389 1390 $style = ($pref['admincss'] == 'admin_dark.css') ? ' icon-white' : ''; 1391 $this->top_icon = "<i class='icon-user{$style}'></i> "; 1392 } 1393 1394 1395 1396 /** 1397 * Next three routines are used to update the database after adding/deleting a class 1398 * calc_tree is the public interface 1399 * @return none 1400 */ 1401 public function calc_tree() 1402 { 1403 $this->readTree(TRUE); // Make sure we have accurate data 1404 foreach ($this->class_parents as $cp) 1405 { 1406 $rights = array(); 1407 $this->rebuild_tree($cp,$rights); // increasing rights going down the tree 1408 } 1409 } 1410 1411 1412 1413 /* 1414 * Internal function, called recursively to rebuild the permissions tree where rights increase going down the tree 1415 * If the permissions change, sets the 'change_flag' to force rewrite to DB (by other code) 1416 * @param integer $parent is the class number being processed. 1417 * @param array $rights is the array of rights accumulated so far in the walk down the tree 1418 */ 1419 protected function rebuild_tree($parent, $rights) 1420 { 1421 1422 if($this->class_tree[$parent]['userclass_parent'] == e_UC_NOBODY) 1423 { 1424 $this->topdown_tree($parent); 1425 return null; 1426 } 1427 1428 if($this->class_tree[$parent]['userclass_type'] == UC_TYPE_GROUP) 1429 { 1430 return null; // Probably just stop here for a group class 1431 } 1432 1433 $rights[] = $parent; 1434 $imp_rights = implode(',', $rights); 1435 1436 if($this->class_tree[$parent]['userclass_accum'] != $imp_rights) 1437 { 1438 $this->class_tree[$parent]['userclass_accum'] = $imp_rights; 1439 1440 if(!isset($this->class_tree[$parent]['change_flag'])) 1441 { 1442 $this->class_tree[$parent]['change_flag'] = 'UPDATE'; 1443 } 1444 } 1445 1446 1447 if(!empty($this->class_tree[$parent]['class_children'])) 1448 { 1449 foreach ($this->class_tree[$parent]['class_children'] as $cc) 1450 { 1451 $this->rebuild_tree($cc,$rights); // Recursive call 1452 } 1453 } 1454 } 1455 1456 1457 /* 1458 * Internal function, called recursively to rebuild the permissions tree where rights increase going up the tree 1459 * @param integer $our_class - ID of class being processed 1460 * @return array of class permissions 1461 */ 1462 protected function topdown_tree($our_class) 1463 { 1464 $rights = array($our_class); // Accumulator always has rights to its own class 1465 1466 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 1467 1468 foreach ($this->class_tree[$our_class]['class_children'] as $cc) 1469 { 1470 $rights = array_merge($rights,$this->topdown_tree($cc)); // Recursive call 1471 } 1472 $rights = array_unique($rights); 1473 $imp_rights = implode(',',$rights); 1474 if ($this->class_tree[$our_class]['userclass_accum'] != $imp_rights) 1475 { 1476 $this->class_tree[$our_class]['userclass_accum'] = $imp_rights; 1477 $this->class_tree[$our_class]['change_flag'] = 'UPDATE'; 1478 } 1479 return $rights; 1480 } 1481 1482 1483 1484 /** 1485 * Called after recalculating the tree to save changed records to the database 1486 * @return none 1487 */ 1488 public function save_tree() 1489 { 1490 foreach ($this->class_tree as $tree) 1491 { 1492 if (isset($tree['change_flag'])) 1493 { 1494 switch ($tree['change_flag']) 1495 { 1496 case 'INSERT' : 1497 $this->add_new_class($tree); 1498 break; 1499 case 'UPDATE' : 1500 $this->save_edited_class($tree); 1501 break; 1502 1503 1504 } 1505 } 1506 } 1507 } 1508 1509 1510 1511 /** 1512 * Next two routines show a text-based tree with markers to indicate the hierarchy. 1513 * Intended primarily for debugging 1514 */ 1515 protected function show_sub_tree($listnum,$marker, $add_class = FALSE) 1516 { 1517 $ret = ''; 1518 $marker = '--'.$marker; 1519 foreach ($this->class_tree[$listnum]['class_children'] as $p) 1520 { 1521 $ret .= $marker.$this->class_tree[$p]['userclass_id'].':'.$this->class_tree[$p]['userclass_name']; 1522 if ($add_class) $ret .= " (".$this->class_tree[$p]['userclass_accum'].")"; 1523 $ret .= " Children: ".count($this->class_tree[$p]['class_children']); 1524 $ret .= "<br />"; 1525 $ret .= $this->show_sub_tree($p,$marker, $add_class); 1526 } 1527 return $ret; 1528 } 1529 1530 1531 /** 1532 * Show a text-based sub-tree, optionally with helpful debug info 1533 * @param boolean $add_class - TRUE includes a list of accumulated rights in each line 1534 * @return string text for display 1535 */ 1536 public function show_tree($add_class = FALSE) 1537 { 1538 $ret = ''; 1539 foreach ($this->class_parents as $p) 1540 { 1541 $ret .= $this->class_tree[$p]['userclass_id'].':'.$this->class_tree[$p]['userclass_name']; 1542 if ($add_class) $ret .= " (".$this->class_tree[$p]['userclass_accum'].")"; 1543 $ret .= " Children: ".count($this->class_tree[$p]['class_children']); 1544 $ret .= "<br />"; 1545 $ret .= $this->show_sub_tree($p,'>', $add_class); 1546 } 1547 return $ret; 1548 } 1549 1550 1551 1552 1553 /* 1554 * Next two routines generate a graphical tree, including option to open/close branches 1555 * 1556 * function show_graphical subtree() is for internal use, called from function show_graphical_tree 1557 * 1558 * @param int $listnum - class number of first element to display, along with its children 1559 * @param array $indent_images - array of images with which to start each line 1560 * @param boolean $is_last - TRUE if this is the last element on the current branch of the tree 1561 */ 1562 protected function show_graphical_subtree($listnum, $indent_images, $is_last = FALSE) 1563 { 1564 $tree = vartrue($this->class_tree[$listnum]['class_children'], array()); 1565 $num_children = count($tree); 1566 $is_open = TRUE; 1567 $tag_name = 'uclass_tree_'.$listnum; 1568 1569 $ret = "<div class='uclass_tree' >\n"; 1570 1571 foreach ($indent_images as $im) 1572 { 1573 $ret .= "<img src='".UC_ICON_DIR.$im."' alt='class icon' />"; 1574 } 1575 // If this link has children, wrap the next image in a link and an expand/hide option 1576 if ($num_children) 1577 { 1578 $ret .= "<span onclick=\"javascript: expandit('{$tag_name}'); expandit('{$tag_name}_p'); expandit('{$tag_name}_m')\"><img src='".UC_ICON_DIR.$this->tree_icons[TRUE][$is_last][TRUE]."' alt='class icon' id='{$tag_name}_m' />"; 1579 $ret .= "<img src='".UC_ICON_DIR.$this->tree_icons[TRUE][$is_last][FALSE]."' style='display:none' id='{$tag_name}_p' alt='class icon' /></span>\n"; 1580 } 1581 else 1582 { 1583 $ret .= "<img src='".UC_ICON_DIR.$this->tree_icons[FALSE][$is_last][$is_open]."' alt='class icon' />\n"; 1584 } 1585 $name_line = ''; 1586 if ($this->graph_debug) { $name_line = $this->class_tree[$listnum]['userclass_id'].":"; } 1587 // if ($this->graph_debug) { $name_line = varset($this->class_tree[$listnum]['userclass_id'], 'XXX').":"; } 1588 1589 if ($this->class_tree[$listnum]['userclass_type'] == UC_TYPE_GROUP) 1590 { 1591 $name_line .= '<b>'.$this->class_tree[$listnum]['userclass_name'].'</b> ('.UCSLAN_81.').'; // Highlight groups 1592 } 1593 else 1594 { 1595 $name_line .= $this->class_tree[$listnum]['userclass_name']; 1596 } 1597 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']); 1598 // Next (commented out) line gives a 'conventional' link 1599 //$ret .= "<img src='".UC_ICON_DIR."topicon.png' alt='class icon' /><a style='text-decoration: none' class='userclass_edit' href='".e_ADMIN_ABS."userclass2.php?config.edit.{$this->class_tree[$listnum]['userclass_id']}'>".$name_line."</a></div>"; 1600 if($this->queryCanEditClass($this->class_tree[$listnum]['userclass_id'])) 1601 { 1602 $url = e_SELF.'?mode=main&action=edit&id='.$this->class_tree[$listnum]['userclass_id']; 1603 $onc = ''; 1604 } 1605 else 1606 { 1607 $url = '#'; 1608 $onc = " onclick=\"alert('".str_replace("'", "\\'", (stripslashes(UCSLAN_90)))."'); return false;\""; 1609 } 1610 1611 $ret .= $this->top_icon."<a style='text-decoration: none' class='userclass_edit'{$onc} href='{$url}'>".$name_line."</a></div>"; 1612 //$ret .= "<img src='".UC_ICON_DIR."topicon.png' alt='class icon' /> 1613 //<span style='cursor:pointer; vertical-align: bottom' onclick=\"javascript: document.location.href='".e_ADMIN."userclass2.php?config.edit.{$this->class_tree[$listnum]['userclass_id']}'\">".$name_line."</span></div>"; 1614 // vertical-align: middle doesn't work! Nor does text-top 1615 1616 if ($num_children) 1617 { 1618 $ret .= "<div class='uclass_tree' id='{$tag_name}'>\n"; 1619 $image_level = count($indent_images); 1620 if ($is_last) 1621 { 1622 $indent_images[] = 'linebottom.gif'; 1623 } 1624 else 1625 { 1626 $indent_images[] = 'line.gif'; 1627 } 1628 if (isset($this->class_tree[$listnum]['class_children'])) foreach ($this->class_tree[$listnum]['class_children'] as $p) 1629 { 1630 $num_children--; 1631 if ($num_children) 1632 { // Use icon indicating more below 1633 $ret .= $this->show_graphical_subtree($p, $indent_images, FALSE); 1634 } 1635 else 1636 { // else last entry on this tree 1637 $ret .= $this->show_graphical_subtree($p, $indent_images, TRUE); 1638 } 1639 } 1640 $ret .= "</div>"; 1641 } 1642 return $ret; 1643 } 1644 1645 1646 1647 /** 1648 * Create graphical class tree, including clickable links to expand/contract branches. 1649 * 1650 * @param boolean $show_debug - TRUE to display additional information against each class 1651 * @return string - text for display 1652 */ 1653 public function show_graphical_tree($show_debug=FALSE) 1654 { 1655 $this->graph_debug = $show_debug; 1656 $indent_images = array(); 1657 1658 $ret = " 1659 <div class='uclass_tree' style='height:16px'> 1660 ".$this->top_icon." 1661 <span style='top:3px'></span> 1662 </div>"; // Just a generic icon here to provide a visual anchor 1663 1664 $num_parents = count($this->class_parents); 1665 foreach ($this->class_parents as $p) 1666 { 1667 $num_parents--; 1668 $ret .= $this->show_graphical_subtree($p, $indent_images, ($num_parents == 0)); 1669 } 1670 return $ret; 1671 } 1672 1673 1674 1675 /** 1676 * Creates an array which contains only DB fields (i.e. strips any added status) 1677 * Copies only those valid fields which are found in the source array 1678 * @param array $classrec - array of class-related information 1679 * @param boolean $inc_id - TRUE to include the class id field (if present in the original) 1680 * @return array of class info suitable for writing to DB 1681 */ 1682 protected function copy_rec($classrec, $inc_id = FALSE) 1683 { 1684 $ret = array(); 1685 if ($inc_id && isset($classrec['userclass_id'])) $ret['userclass_id'] = $classrec['userclass_id']; 1686 foreach ($this->field_list as $fl => $val) 1687 { 1688 if (isset($classrec[$fl])) $ret[$fl] = $classrec[$fl]; 1689 } 1690 return $ret; 1691 } 1692 1693 1694 1695 /** 1696 * Return an unused class ID. Misses the predefined classes. 1697 * Initially tries to find an unused class ID less than e_UC_SPECIAL_BASE 1698 * Then attempts to find a gap in the lower numbered classes 1699 * Finally allocates a class number above e_UC_SPECIAL_END 1700 * @return integer|boolean - class ID if available; otherwise FALSE 1701 */ 1702 public function findNewClassID() 1703 { 1704 $i = 1; 1705 // Start by allocating a new class with a number higher than any previously allocated 1706 foreach ($this->class_tree as $id => $r) 1707 { 1708 if ($id < e_UC_SPECIAL_BASE) 1709 { 1710 $i = max($i,$id); 1711 } 1712 } 1713 $i++; 1714 if ($i < e_UC_SPECIAL_BASE) return $i; 1715 1716 // Looks like we've had a lot of activity in classes - try and find a gap. 1717 for ($i = 1; ($i < e_UC_SPECIAL_BASE); $i++) 1718 { 1719 if (!isset($this->class_tree[$i])) return $i; 1720 } 1721 // Big system!! Assign a class in the 2.0-only block above 255 1722 for ($i = e_UC_SPECIAL_END+1; ($i < 32767); $i++) 1723 { 1724 if (!isset($this->class_tree[$i])) return $i; 1725 } 1726 1727 return FALSE; // Just in case all classes assigned! 1728 } 1729 1730 1731 1732 1733 /** 1734 * Add new class. Class ID must be in the passed record. 1735 * @param array $classrec - user class data 1736 * @return boolean TRUE on success, FALSE on failure 1737 */ 1738 public function add_new_class($classrec) 1739 { 1740 if (!isset($classrec['userclass_id'])) 1741 { 1742 return FALSE; 1743 } 1744 if ($classrec['userclass_type'] == UC_TYPE_GROUP) 1745 { // Need to make sure our ID is in the accumulation array 1746 $temp = explode(',',$classrec['userclass_accum']); 1747 if (!in_array($classrec['userclass_id'], $temp)) 1748 { 1749 $temp[] = $classrec['userclass_id']; 1750 $classrec['userclass_accum'] = implode(',',$temp); 1751 } 1752 } 1753 if ($this->sql_r->db_Insert('userclass_classes',$this->copy_rec($classrec, TRUE)) === FALSE) 1754 { 1755 return FALSE; 1756 } 1757 //findNewClassID() always returns an unused ID. 1758 //if a plugin.xml adds more than one <class ...> within <userClasses..> tag 1759 //it will add 1 class only because class_tree never updates itself after adding classes and will return the same unnused ID 1760 $this->class_tree[$classrec['userclass_id']] = $classrec; 1761 $this->clearCache(); 1762 return TRUE; 1763 } 1764 1765 1766 1767 /** 1768 * Save class data after editing 1769 * 1770 * @param array $classrec - class data 1771 * @return boolean TRUE on success, FALSE on failure 1772 * 1773 * Note - only updates those fields which are present in the passed array, and ignores unexpected fields. 1774 */ 1775 public function save_edited_class($classrec) 1776 { 1777 if (!$classrec['userclass_id']) 1778 { 1779 e107::getMessage()->addDebug('Programming bungle on save - no ID field'); 1780 // echo 'Programming bungle on save - no ID field<br />'; 1781 return FALSE; 1782 } 1783 $qry = ''; 1784 $spacer = ''; 1785 if (isset($classrec['userclass_type']) && ($classrec['userclass_type'] == UC_TYPE_GROUP)) 1786 { // Need to make sure our ID is in the accumulation array 1787 $temp = explode(',',$classrec['userclass_accum']); 1788 if (!in_array($classrec['userclass_id'], $temp)) 1789 { 1790 $temp[] = $classrec['userclass_id']; 1791 $classrec['userclass_accum'] = implode(',',$temp); 1792 } 1793 } 1794 1795 foreach ($this->field_list as $fl => $val) 1796 { 1797 if (isset($classrec[$fl])) 1798 { 1799 $qry .= $spacer."`".$fl."` = '".$classrec[$fl]."'"; 1800 $spacer = ", "; 1801 } 1802 } 1803 if ($this->sql_r->db_Update('userclass_classes', $qry." WHERE `userclass_id`='{$classrec['userclass_id']}'") === FALSE) 1804 { 1805 return FALSE; 1806 } 1807 $this->clearCache(); 1808 return TRUE; 1809 } 1810 1811 1812 1813 /** 1814 * Check if a user may edit a user class. 1815 * @param integer $classID > 0 1816 * @param string $classList - comma-separated list of class IDs; defaults to those of current user 1817 * 1818 * @return integer: 1819 * 0 - if editing not allowed at all 1820 * 1 - if restricted editing allowed (usually because its a fixed class) 1821 * 2 - All editing rights allowed 1822 */ 1823 public function queryCanEditClass($classID, $classList = USERCLASS_LIST) 1824 { 1825 if (!isset($this->class_tree[$classID])) return 0; // Class doesn't exist - no hope of editing! 1826 1827 $blockers = array(e_UC_PUBLIC => 1, e_UC_READONLY => 1, e_UC_NOBODY => 1, e_UC_GUEST => 1); 1828 if (isset($blockers[$classID])) return 0; // Don't allow edit of some fixed classes 1829 1830 $canEdit = $this->isAdmin; 1831 $possibles = array_flip(explode(',',$classList)); 1832 if (isset($possibles[$this->class_tree[$classID]['userclass_editclass']])) $canEdit = TRUE; 1833 1834 if (!$canEdit) return 0; 1835 1836 if (($classID >= e_UC_SPECIAL_BASE) && ($classID <= e_UC_SPECIAL_END)) return 1; // Restricted edit of fixed classes 1837 if (isset($this->fixed_classes[$classID])) return 1; // This picks up fixed classes such as e_UC_PUBLIC outside the main range 1838 1839 return 2; // Full edit rights - a 'normal' class 1840 } 1841 1842 1843 1844 1845 /** 1846 * Check if a class may be deleted. (Fixed classes, classes with descendants cannot be deleted) 1847 * @param integer $classID > 0 1848 * @return boolean TRUE if deletion permissible; false otherwise 1849 */ 1850 public function queryCanDeleteClass($classID) 1851 { 1852 if (($classID >= e_UC_SPECIAL_BASE) && ($classID <= e_UC_SPECIAL_END)) return FALSE; // Don't allow deletion of fixed classes 1853 if (isset($this->fixed_classes[$classID])) return FALSE; // This picks up fixed classes such as e_UC_PUBLIC outside the main range which can't be deleted 1854 if (!isset($this->class_tree[$classID])) return FALSE; 1855 if (count($this->class_tree[$classID]['class_children'])) return FALSE; // Can't delete class with descendants 1856 foreach ($this->class_tree as $c) 1857 { 1858 if ($c['userclass_editclass'] == $classID) return FALSE; // Class specified as managing or using another class 1859 if ($c['userclass_visibility'] == $classID) return FALSE; 1860 } 1861 return TRUE; 1862 } 1863 1864 1865 1866 /** 1867 * Delete a class 1868 * @param integer $classID > 0 1869 * @return boolean TRUE on success, FALSE on error 1870 */ 1871 public function delete_class($classID) 1872 { 1873 if (self::queryCanDeleteClass($classID) === FALSE) return FALSE; 1874 1875 if ($this->sql_r->db_Delete('userclass_classes', "`userclass_id`='{$classID}'") === FALSE) return FALSE; 1876 $this->clearCache(); 1877 $this->readTree(TRUE); // Re-read the class tree 1878 return TRUE; 1879 } 1880 1881 1882 1883 /** 1884 * Delete a class, and update class membership for all users who are members of that class. 1885 * @param integer $classID > 0 1886 * @return boolean TRUE on success, FALSE on error 1887 */ 1888 public function deleteClassAndUsers($classID) 1889 { 1890 if (self::delete_class($classID) === TRUE) 1891 { 1892 if ($this->sql_r->select('user', 'user_id, user_class', "user_class REGEXP '(^|,){$classID}(,|$)'")) 1893 { 1894 $sql2 = e107::getDb('sql2'); 1895 while ($row = $this->sql_r->fetch()) 1896 { 1897 $newClass = self::ucRemove($classID, $row['user_class']); 1898 $sql2->update('user', "user_class = '{$newClass}' WHERE user_id = {$row['user_id']} LIMIT 1"); 1899 } 1900 } 1901 return TRUE; 1902 } 1903 return FALSE; 1904 } 1905 1906 1907 1908 /** 1909 * Adds all users in list to a specified user class 1910 * (Moved in from e_userclass class) 1911 * @param integer $cid - user class ID 1912 * @param $uinfoArray is array(uid=>user_class) - list of affected users 1913 * @return none 1914 */ 1915 public function class_add($cid, $uinfoArray) 1916 { 1917 $e107 = e107::getInstance(); 1918 $uc_sql = new db; 1919 foreach($uinfoArray as $uid => $curclass) 1920 { 1921 if ($curclass) 1922 { 1923 $newarray = array_unique(array_merge(explode(',', $curclass), array($cid))); 1924 $new_userclass = implode(',', $newarray); 1925 } 1926 else 1927 { 1928 $new_userclass = $cid; 1929 } 1930 $uc_sql->update('user', "user_class='".e107::getParser()->toDB($new_userclass, true)."' WHERE user_id=".intval($uid)." LIMIT 1"); 1931 } 1932 } 1933 1934 1935 1936 /** 1937 * Removes all users in list from a specified user class 1938 * (Moved in from e_userclass class) 1939 * @param integer $cid - user class ID 1940 * @param $uinfoArray is array(uid=>user_class) - list of affected users 1941 * @return none 1942 */ 1943 public function class_remove($cid, $uinfoArray) 1944 { 1945 $uc_sql = e107::getDb(); 1946 foreach($uinfoArray as $uid => $curclass) 1947 { 1948 $newarray = array_diff(explode(',', $curclass), array('', $cid)); 1949 $new_userclass = implode(',', $newarray); 1950 $uc_sql->update('user', "user_class='".e107::getParser()->toDB($new_userclass, true)."' WHERE user_id=".intval($uid)." LIMIT 1"); 1951 } 1952 } 1953 1954 1955 1956 /** 1957 * Check class data record for a fixed class - certain fields have constraints on their values. 1958 * updates any values which are unacceptable. 1959 * @param array $data - user class record (passed by reference) 1960 * @param integer $id - class id 1961 * @return boolean Returns TRUE if nothing changed, FALSE if changes made 1962 */ 1963 public function checkAdminInfo(&$data, $id) 1964 { 1965 $ret = TRUE; 1966 if (($id < e_UC_SPECIAL_BASE) || ($id > e_UC_SPECIAL_END)) return TRUE; 1967 if (isset($data['userclass_parent'])) 1968 { 1969 if (($data['userclass_parent'] < e_UC_SPECIAL_BASE) || ($data['userclass_parent'] > e_UC_SPECIAL_END)) 1970 { 1971 $data['userclass_parent'] = e_UC_NOBODY; 1972 $ret = FALSE; 1973 } 1974 } 1975 if (isset($data['userclass_editclass'])) 1976 { 1977 if ($id == e_UC_MAINADMIN) 1978 { 1979 if ($data['userclass_editclass'] < e_UC_MAINADMIN) 1980 { 1981 $data['userclass_editclass'] = e_UC_MAINADMIN; 1982 $ret = FALSE; 1983 } 1984 } 1985 elseif (($data['userclass_editclass'] < e_UC_SPECIAL_BASE) || ($data['userclass_editclass'] > e_UC_SPECIAL_END)) 1986 { 1987 $data['userclass_editclass'] = e_UC_MAINADMIN; 1988 $ret = FALSE; 1989 } 1990 } 1991 return $ret; 1992 } 1993 1994 1995 1996 /** 1997 * Set a simple default tree structure of classes 1998 * @return none 1999 */ 2000 public function set_default_structure() 2001 { 2002 // If they don't exist, we need to create class records for the 'standard' user classes 2003 $init_list = array( 2004 array('userclass_id' => e_UC_MEMBER, 'userclass_name' => UC_LAN_3, 2005 'userclass_description' => UCSLAN_75, 2006 'userclass_editclass' => e_UC_MAINADMIN, 2007 'userclass_parent' => e_UC_PUBLIC, 2008 'userclass_visibility' => e_UC_MEMBER 2009 ), 2010 array('userclass_id' => e_UC_ADMINMOD, 'userclass_name' => UC_LAN_8, 2011 'userclass_description' => UCSLAN_74, 2012 'userclass_editclass' => e_UC_MAINADMIN, 2013 'userclass_parent' => e_UC_MAINADMIN, 2014 'userclass_visibility' => e_UC_MEMBER 2015 ), 2016 array('userclass_id' => e_UC_ADMIN, 'userclass_name' => UC_LAN_5, 2017 'userclass_description' => UCSLAN_76, 2018 'userclass_editclass' => e_UC_MAINADMIN, 2019 'userclass_parent' => e_UC_ADMINMOD, 2020 'userclass_visibility' => e_UC_MEMBER 2021 ), 2022 array('userclass_id' => e_UC_MAINADMIN, 'userclass_name' => UC_LAN_6, 2023 'userclass_description' => UCSLAN_77, 2024 'userclass_editclass' => e_UC_MAINADMIN, 2025 'userclass_parent' => e_UC_NOBODY, 2026 'userclass_visibility' => e_UC_MEMBER 2027 ), 2028 array('userclass_id' => e_UC_MODS, 'userclass_name' => UC_LAN_7, 2029 'userclass_description' => UCSLAN_78, 2030 'userclass_editclass' => e_UC_MAINADMIN, 2031 'userclass_parent' => e_UC_ADMINMOD, 2032 'userclass_visibility' => e_UC_MEMBER 2033 ), 2034 array('userclass_id' => e_UC_NEWUSER, 'userclass_name' => UC_LAN_9, 2035 'userclass_description' => UCSLAN_87, 2036 'userclass_editclass' => e_UC_MAINADMIN, 2037 'userclass_parent' => e_UC_MEMBER, 2038 'userclass_visibility' => e_UC_ADMIN 2039 ), 2040 array('userclass_id' => e_UC_BOTS, 'userclass_name' => UC_LAN_10, 2041 'userclass_description' => UCSLAN_88, 2042 'userclass_editclass' => e_UC_MAINADMIN, 2043 'userclass_parent' => e_UC_PUBLIC, 2044 'userclass_visibility' => e_UC_ADMIN 2045 ) 2046 ); 2047 2048 foreach ($init_list as $entry) 2049 { 2050 if ($this->sql_r->select('userclass_classes','*',"userclass_id='".$entry['userclass_id']."' ")) 2051 { 2052 $this->sql_r->update('userclass_classes', "userclass_parent='".$entry['userclass_parent']."', userclass_visibility='".$entry['userclass_visibility']."' WHERE userclass_id='".$entry['userclass_id']."'"); 2053 } 2054 else 2055 { 2056 $this->add_new_class($entry); 2057 } 2058 } 2059 } 2060 2061 2062 2063 /** 2064 * Write the current userclass tree to the file e_TEMP.'userclasses.xml' 2065 * 2066 * @return TRUE on success, FALSE on fail. 2067 */ 2068 public function makeXMLFile() 2069 { 2070 $xml = "<dbTable name=\"userclass_classes\">\n"; 2071 foreach ($this->class_tree as $uc => $d) 2072 { 2073 $xml .= "\t<item>\n"; 2074 $xml .= "\t\t<field name=\"userclass_id\">{$uc}</field>\n"; 2075 foreach ($this->field_list as $f => $v) 2076 { 2077 $xml .= "\t\t<field name=\"{$f}\">{$d[$f]}</field>\n"; 2078 } 2079 $xml .= "\t</item>\n"; 2080 } 2081 $xml .= "</dbTable>\n"; 2082 return (file_put_contents(e_TEMP.'userclasses.xml', $xml) === FALSE) ? FALSE : TRUE; 2083 } 2084 2085 2086 2087 /** 2088 * Clear user class cache 2089 * @return none 2090 */ 2091 public function clearCache() 2092 { 2093 e107::getCache()->clear_sys(UC_CACHE_TAG); 2094 } 2095} 2096 2097 2098 2099 2100