1<?php 2/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */ 3 4/** 5 * A framework for authentication and authorization in PHP applications 6 * 7 * LiveUser_Admin is meant to be used with the LiveUser package. 8 * It is composed of all the classes necessary to administrate 9 * data used by LiveUser. 10 * 11 * You'll be able to add/edit/delete/get things like: 12 * * Rights 13 * * Users 14 * * Groups 15 * * Areas 16 * * Applications 17 * * Subgroups 18 * * ImpliedRights 19 * 20 * And all other entities within LiveUser. 21 * 22 * At the moment we support the following storage containers: 23 * * DB 24 * * MDB 25 * * MDB2 26 * 27 * But it takes no time to write up your own storage container, 28 * so if you like to use native mysql functions straight, then it's possible 29 * to do so in under a hour! 30 * 31 * PHP version 4 and 5 32 * 33 * LICENSE: This library is free software; you can redistribute it and/or 34 * modify it under the terms of the GNU Lesser General Public 35 * License as published by the Free Software Foundation; either 36 * version 2.1 of the License, or (at your option) any later version. 37 * 38 * This library is distributed in the hope that it will be useful, 39 * but WITHOUT ANY WARRANTY; without even the implied warranty of 40 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 41 * Lesser General Public License for more details. 42 * 43 * You should have received a copy of the GNU Lesser General Public 44 * License along with this library; if not, write to the Free Software 45 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 46 * MA 02111-1307 USA 47 * 48 * 49 * @category authentication 50 * @package LiveUser_Admin 51 * @author Markus Wolff <wolff@21st.de> 52 * @author Helgi �ormar �orbj�rnsson <dufuz@php.net> 53 * @author Lukas Smith <smith@pooteeweet.org> 54 * @author Arnaud Limbourg <arnaud@php.net> 55 * @author Christian Dickmann <dickmann@php.net> 56 * @author Matt Scifo <mscifo@php.net> 57 * @author Bjoern Kraus <krausbn@php.net> 58 * @copyright 2002-2006 Markus Wolff 59 * @license http://www.gnu.org/licenses/lgpl.txt 60 * @version CVS: $Id: Simple.php 211320 2006-04-13 13:41:47Z lsmith $ 61 * @link http://pear.php.net/LiveUser_Admin 62 */ 63 64require_once 'LiveUser/Perm/Simple.php'; 65 66/** 67 * Simple permission administration class that features support for 68 * creating, updating, removing and assigning: 69 * - users 70 * - rights 71 * - areas (categorize rights) 72 * - applications (categorize areas) 73 * - translations (for rights, areas, applications and groups) 74 * 75 * This class provides a set of functions for implementing a user 76 * permission management system on live websites. All authorisation 77 * backends/containers must be extensions of this base class. 78 * 79 * @category authentication 80 * @package LiveUser_Admin 81 * @author Markus Wolff <wolff@21st.de> 82 * @author Bjoern Kraus <krausbn@php.net> 83 * @author Helgi �ormar �orbj�rnsson <dufuz@php.net> 84 * @copyright 2002-2006 Markus Wolff 85 * @license http://www.gnu.org/licenses/lgpl.txt 86 * @version Release: @package_version@ 87 * @link http://pear.php.net/LiveUser_Admin 88 */ 89class LiveUser_Admin_Perm_Simple 90{ 91 /** 92 * Error stack 93 * 94 * @var object PEAR_ErrorStack 95 * @access public 96 */ 97 var $stack = null; 98 99 /** 100 * Storage Container 101 * 102 * @var object 103 * @access private 104 */ 105 var $_storage = null; 106 107 /** 108 * Key (method names), with array lists of selectable tables for the given method 109 * 110 * @var array 111 * @access public 112 */ 113 var $selectable_tables = array( 114 'getUsers' => array('perm_users', 'userrights', 'rights'), 115 'getRights' => array('rights', 'userrights', 'areas', 'applications', 'translations'), 116 'getAreas' => array('areas', 'applications', 'translations'), 117 'getApplications' => array('applications', 'translations'), 118 'getTranslations' => array('translations'), 119 ); 120 121 /** 122 * Key (field name), with method names as values to determine what method 123 * should be called to get data when the 'with' option is used in a get*() method 124 * 125 * @var array 126 * @access public 127 */ 128 var $withFieldMethodMap = array( 129 'perm_user_id' => 'getUsers', 130 'right_id' => 'getRights', 131 'area_id' => 'getAreas', 132 'application_id' => 'getApplications', 133 ); 134 135 /** 136 * Constructor 137 * 138 * @return void 139 * 140 * @access protected 141 */ 142 function LiveUser_Admin_Perm_Simple() 143 { 144 // Create the error stack, retrieve the errors using LiveUser_Admin->getErrors(). 145 $this->stack = &PEAR_ErrorStack::singleton('LiveUser_Admin'); 146 } 147 148 /** 149 * Initialize the storage container 150 * 151 * @param array array containing the configuration. 152 * @return bool true on success or false on failure 153 * 154 * @access public 155 */ 156 function init(&$conf) 157 { 158 // Sanity check, is there a storage container defined in the configuration. 159 if (!array_key_exists('storage', $conf)) { 160 $this->stack->push(LIVEUSER_ADMIN_ERROR, 'exception', 161 array('msg' => 'Missing storage configuration array')); 162 return false; 163 } 164 165 // Set the config to class vars. 166 if (is_array($conf)) { 167 $keys = array_keys($conf); 168 foreach ($keys as $key) { 169 if (isset($this->$key)) { 170 $this->$key =& $conf[$key]; 171 } 172 } 173 } 174 175 // Create the storage class, if and error occures, add it to the stack and return false. 176 $this->_storage =& LiveUser::storageFactory($conf['storage'], 'LiveUser_Admin_Perm_'); 177 if ($this->_storage === false) { 178 end($conf['storage']); 179 $key = key($conf['storage']); 180 $this->stack->push(LIVEUSER_ERROR, 'exception', 181 array('msg' => 'Could not instanciate perm storage container: '.$key)); 182 return false; 183 } 184 185 return true; 186 } 187 188 /** 189 * Add a user 190 * 191 * @param array containing atleast the key-value-pairs of all required 192 * columns in the perm_users table 193 * @return int|bool false on error, true (or new id) on success 194 * 195 * @access public 196 */ 197 function addUser($data) 198 { 199 // Sanity check. If not present, set the perm_type to the default value. 200 if (!array_key_exists('perm_type', $data)) { 201 $data['perm_type'] = LIVEUSER_USER_TYPE_ID; 202 } 203 204 $result = $this->_storage->insert('perm_users', $data); 205 // todo: notify observer 206 return $result; 207 } 208 209 /** 210 * Update users 211 * 212 * @param array containing the key value pairs of columns to update 213 * @param array key values pairs (value may be a string or an array) 214 * This will construct the WHERE clause of your update 215 * Be careful, if you leave this blank no WHERE clause 216 * will be used and all users will be affected by the update 217 * @return int|bool false on error, the affected rows on success 218 * 219 * @access public 220 */ 221 function updateUser($data, $filters) 222 { 223 $result = $this->_storage->update('perm_users', $data, $filters); 224 // todo: notify observer 225 return $result; 226 } 227 228 /** 229 * Remove users and all their relevant relations 230 * 231 * @param array key values pairs (value may be a string or an array) 232 * This will construct the WHERE clause of your update 233 * Be careful, if you leave this blank no WHERE clause 234 * will be used and all users will be affected by the removed 235 * @return int|bool false on error, the affected rows on success 236 * 237 * @access public 238 */ 239 function removeUser($filters) 240 { 241 // Prepare the filters. Based on the provided filters a new array will be 242 // created with the corresponding perm_user_id's. If the filters are empty, 243 // cause an error or just have no result 0 or false will be returned 244 $filters = $this->_makeRemoveFilter($filters, 'perm_user_id', 'getUsers'); 245 if (!$filters) { 246 return $filters; 247 } 248 249 // Revoke all the rights this user might have (clean up the database). 250 $result = $this->revokeUserRight($filters); 251 if ($result === false) { 252 return false; 253 } 254 255 $result = $this->_storage->delete('perm_users', $filters); 256 // todo: notify observer 257 return $result; 258 } 259 260 /** 261 * Add a right 262 * 263 * @param array containing atleast the key-value-pairs of all required 264 * columns in the rights table 265 * @return int|bool false on error, true (or new id) on success 266 * 267 * @access public 268 */ 269 function addRight($data) 270 { 271 $result = $this->_storage->insert('rights', $data); 272 // todo: notify observer 273 return $result; 274 } 275 276 /** 277 * Update rights 278 * 279 * @param array containing the key value pairs of columns to update 280 * @param array key values pairs (value may be a string or an array) 281 * This will construct the WHERE clause of your update 282 * Be careful, if you leave this blank no WHERE clause 283 * will be used and all rights will be affected by the update 284 * @return int|bool false on error, the affected rows on success 285 * 286 * @access public 287 */ 288 function updateRight($data, $filters) 289 { 290 $result = $this->_storage->update('rights', $data, $filters); 291 // todo: notify observer 292 return $result; 293 } 294 295 /** 296 * Remove rights and all their relevant relations 297 * 298 * @param array key values pairs (value may be a string or an array) 299 * This will construct the WHERE clause of your update 300 * Be careful, if you leave this blank no WHERE clause 301 * will be used and all rights will be affected by the remove 302 * @return int|bool false on error, the affected rows on success 303 * 304 * @access public 305 */ 306 function removeRight($filters) 307 { 308 // Prepare the filters. Based on the provided filters a new array will be 309 // created with the corresponding right_id's. If the filters are empty, 310 // cause an error or just have no result 0 or false will be returned 311 $filters = $this->_makeRemoveFilter($filters, 'right_id', 'getRights'); 312 if (!$filters) { 313 return $filters; 314 } 315 316 // Revoke this right from any user it might have been assigned to (clean up database) 317 $result = $this->revokeUserRight($filters); 318 if ($result === false) { 319 return false; 320 } 321 322 $result = $this->_storage->delete('rights', $filters); 323 // todo: notify observer 324 return $result; 325 } 326 327 /** 328 * Add an area 329 * 330 * @param array containing atleast the key-value-pairs of all required 331 * columns in the areas table 332 * @return int|bool false on error, true (or new id) on success 333 * 334 * @access public 335 */ 336 function addArea($data) 337 { 338 $result = $this->_storage->insert('areas', $data); 339 // todo: notify observer 340 return $result; 341 } 342 343 /** 344 * Update areas 345 * 346 * @param array associative array in the form of $fieldname => $data 347 * @param array associative array in the form of $fieldname => $data 348 * This will construct the WHERE clause of your update 349 * Be careful, if you leave this blank no WHERE clause 350 * will be used and all areas will be affected by the update 351 * @return int|bool false on error, the affected rows on success 352 * 353 * @access public 354 */ 355 function updateArea($data, $filters) 356 { 357 $result = $this->_storage->update('areas', $data, $filters); 358 // todo: notify observer 359 return $result; 360 } 361 362 /** 363 * Remove areas and all their relevant relations 364 * 365 * @param array key values pairs (value may be a string or an array) 366 * This will construct the WHERE clause of your update 367 * Be careful, if you leave this blank no WHERE clause 368 * will be used and all areas will be affected by the remove 369 * @return int|bool false on error, the affected rows on success 370 * 371 * @access public 372 */ 373 function removeArea($filters) 374 { 375 // Prepare the filters. Based on the provided filters a new array will be 376 // created with the corresponding area_id's. If the filters are empty, 377 // cause an error or just have no result 0 or false will be returned 378 $filters = $this->_makeRemoveFilter($filters, 'area_id', 'getAreas'); 379 if (!$filters) { 380 return $filters; 381 } 382 383 // Remove all the rights that are part of this area. 384 $result = $this->removeRight($filters); 385 if ($result === false) { 386 return false; 387 } 388 389 $result = $this->_storage->delete('areas', $filters); 390 // todo: notify observer 391 return $result; 392 } 393 394 /** 395 * Add an application 396 * 397 * @param array containing atleast the key-value-pairs of all required 398 * columns in the applications table 399 * @return int|bool false on error, true (or new id) on success 400 * 401 * @access public 402 */ 403 function addApplication($data) 404 { 405 $result = $this->_storage->insert('applications', $data); 406 // todo: notify observer 407 return $result; 408 } 409 410 /** 411 * Update applications 412 * 413 * @param array containing the key value pairs of columns to update 414 * @param array key values pairs (value may be a string or an array) 415 * This will construct the WHERE clause of your update 416 * Be careful, if you leave this blank no WHERE clause 417 * will be used and all applictions will be affected by the update 418 * @return int|bool false on error, the affected rows on success 419 * 420 * @access public 421 */ 422 function updateApplication($data, $filters) 423 { 424 $result = $this->_storage->update('applications', $data, $filters); 425 // todo: notify observer 426 return $result; 427 } 428 429 /** 430 * Remove applications and all their relevant relations 431 * 432 * @param array key values pairs (value may be a string or an array) 433 * This will construct the WHERE clause of your update 434 * Be careful, if you leave this blank no WHERE clause 435 * will be used and all applications will be affected by the remove 436 * @return int|bool false on error, the affected rows on success 437 * 438 * @access public 439 */ 440 function removeApplication($filters) 441 { 442 // Prepare the filters. Based on the provided filters a new array will be 443 // created with the corresponding application_id's. If the filters are empty, 444 // cause an error or just have no result 0 or false will be returned 445 $filters = $this->_makeRemoveFilter($filters, 'application_id', 'getApplications'); 446 if (!$filters) { 447 return $filters; 448 } 449 450 // Remove all the area's that are part of this application 451 $result = $this->removeArea($filters); 452 if ($result === false) { 453 return false; 454 } 455 456 $result = $this->_storage->delete('applications', $filters); 457 // todo: notify observer 458 return $result; 459 } 460 461 /** 462 * Grant user a right 463 * 464 * <code> 465 * // grant user id 13 the right NEWS_CHANGE 466 * $data = array( 467 * 'right_id' => NEWS_CHANGE, 468 * 'perm_user_id' => 13 469 * ); 470 * $lua->perm->grantUserRight($data); 471 * </code> 472 * 473 * @param array containing the perm_user_id and right_id and optionally a right_level 474 * @return 475 * 476 * @access public 477 */ 478 function grantUserRight($data) 479 { 480 // Sanity check. Set the right_level to it's default value if it's not set. 481 if (!array_key_exists('right_level', $data)) { 482 $data['right_level'] = LIVEUSER_MAX_LEVEL; 483 } 484 485 // check if already exists 486 $filters = array( 487 'perm_user_id' => $data['perm_user_id'], 488 'right_id' => $data['right_id'], 489 ); 490 491 $count = $this->_storage->selectCount('userrights', 'right_id', $filters); 492 493 // The user already has this right, adding an error to the stack and return false. 494 if ($count > 0) { 495 $this->stack->push( 496 LIVEUSER_ADMIN_ERROR, 'exception', 497 array('msg' => 'This user with perm id '.$data['perm_user_id']. 498 ' has already been granted the right id '.$data['right_id']) 499 ); 500 return false; 501 } 502 503 $result = $this->_storage->insert('userrights', $data); 504 // todo: notify observer 505 return $result; 506 } 507 508 /** 509 * Update right(s) for the given user(s) 510 * 511 * @param array containing the key value pairs of columns to update 512 * @param array key values pairs (value may be a string or an array) 513 * This will construct the WHERE clause of your update 514 * Be careful, if you leave this blank no WHERE clause 515 * will be used and all users will be affected by the update 516 * @return int|bool false on error, the affected rows on success 517 * 518 * @access public 519 */ 520 function updateUserRight($data, $filters) 521 { 522 $result = $this->_storage->update('userrights', $data, $filters); 523 // todo: notify observer 524 return $result; 525 } 526 527 /** 528 * Revoke (remove) right(s) from the user(s) 529 * 530 * @param array key values pairs (value may be a string or an array) 531 * This will construct the WHERE clause of your update 532 * Be careful, if you leave this blank no WHERE clause 533 * will be used and all users will be affected by the remove 534 * @return int|bool false on error, the affected rows on success 535 * 536 * @access public 537 */ 538 function revokeUserRight($filters) 539 { 540 $result = $this->_storage->delete('userrights', $filters); 541 // todo: notify observer 542 return $result; 543 } 544 545 /** 546 * Add a translation 547 * 548 * @param array containing atleast the key-value-pairs of all required 549 * columns in the users table 550 * @return int|bool false on error, true (or new id) on success 551 * 552 * @access public 553 */ 554 function addTranslation($data) 555 { 556 $result = $this->_storage->insert('translations', $data); 557 // todo: notify observer 558 return $result; 559 } 560 561 /** 562 * Update translations 563 * 564 * @param array containing the key value pairs of columns to update 565 * @param array key values pairs (value may be a string or an array) 566 * This will construct the WHERE clause of your update 567 * Be careful, if you leave this blank no WHERE clause 568 * will be used and all translations will be affected by the update 569 * @return int|bool false on error, the affected rows on success 570 * 571 * @access public 572 */ 573 function updateTranslation($data, $filters) 574 { 575 $result = $this->_storage->update('translations', $data, $filters); 576 // todo: notify observer 577 return $result; 578 } 579 580 /** 581 * Remove translations and all their relevant relations 582 * 583 * @param array key values pairs (value may be a string or an array) 584 * This will construct the WHERE clause of your update 585 * Be careful, if you leave this blank no WHERE clause 586 * will be used and all translations will be affected by the remove 587 * @return int|bool false on error, the affected rows on success 588 * 589 * @access public 590 */ 591 function removeTranslation($filters) 592 { 593 // Prepare the filters. Based on the provided filters a new array will be 594 // created with the corresponding translation_id's. If the filters are empty, 595 // cause an error or just have no result 0 or false will be returned 596 $filters = $this->_makeRemoveFilter($filters, 'translation_id', 'getTranslations'); 597 if (!$filters) { 598 return $filters; 599 } 600 601 $result = $this->_storage->delete('translations', $filters); 602 // todo: notify observer 603 return $result; 604 } 605 606 /** 607 * Makes the filters used by the remove functions and also 608 * checks if there is actually something that needs removing. 609 * 610 * @param array key values pairs (value may be a string or an array) 611 * This will construct the WHERE clause of your update 612 * Be careful, if you leave this blank no WHERE clause 613 * will be used and all users will be affected by the update 614 * @param string name of the column for which we require a filter to be set 615 * @param string name of the method that should be used to determine the filter 616 * @return int|array|bool 0, an array containing the filter for the key 617 * or false on error 618 * 619 * @access private 620 */ 621 function _makeRemoveFilter($filters, $key, $method) 622 { 623 // Do not allow people to delete the entire contents of a given table 624 if (empty($filters) || !is_array($filters)) { 625 return 0; 626 } 627 628 // todo: if all filters apply to the given table only then we can probably skip running the select .. 629 630 // Rewrite filter to only include the provided key, since we cannot 631 // rely on joins in delete for all backends 632 if (!isset($filters[$key]) || count($filters) > 1) { 633 // Prepare the params for fetching the column provided. It should 634 // return an array with only the keys. 635 $params = array( 636 'fields' => array($key), 637 'filters' => $filters, 638 'select' => 'col', 639 ); 640 $result = $this->$method($params); 641 if ($result === false) { 642 return false; 643 } 644 645 if (empty($result)) { 646 return 0; 647 } 648 649 // Rebuild the filters array. 650 $filters = array($key => $result); 651 } 652 return $filters; 653 } 654 655 /** 656 * This function finds the list of selectable tables either from the params 657 * or from the selectable_tables property using the method parameter 658 * 659 * @param string name of the method 660 * @param array containing the parameters passed to a get*() method 661 * @return array contains the selectable tables 662 * 663 * @access private 664 */ 665 function _findSelectableTables($method, $params = array()) 666 { 667 $selectable_tables = array(); 668 // Check if the provided params might already have the selectable tables. 669 // If so, return them, else fetch them through this->selectable_tables. 670 if (array_key_exists('selectable_tables', $params)) { 671 $selectable_tables = $params['selectable_tables']; 672 } elseif (array_key_exists($method, $this->selectable_tables)) { 673 $selectable_tables = $this->selectable_tables[$method]; 674 } 675 return $selectable_tables; 676 } 677 678 /** 679 * This function holds up most of the heat for all the get* functions. 680 * 681 * @param array containing key-value pairs for: 682 * 'fields' - ordered array containing the fields to fetch 683 * if empty all fields from the user table are fetched 684 * 'filters' - key values pairs (value may be a string or an array) 685 * 'orders' - key value pairs (values 'ASC' or 'DESC') 686 * 'rekey' - if set to true, returned array will have the 687 * first column as its first dimension 688 * 'group' - if set to true and $rekey is set to true, then 689 * all values with the same first column will be 690 * wrapped in an array 691 * 'limit' - number of rows to select 692 * 'offset' - first row to select 693 * 'select' - determines what query method to use: 694 * 'one' -> queryOne, 'row' -> queryRow, 695 * 'col' -> queryCol, 'all' ->queryAll (default) 696 * @param string name of the table from which to start looking 697 * for join points 698 * @param array list of tables that may be joined to 699 * @return bool|array false on failure or array with selected data 700 * 701 * @access private 702 */ 703 function _makeGet($params, $root_table, $selectable_tables) 704 { 705 // Ensure that default params are set 706 $params = LiveUser_Admin_Storage::setSelectDefaultParams($params); 707 708 $data = $this->_storage->select($params['select'], $params['fields'], 709 $params['filters'], $params['orders'], $params['rekey'], $params['group'], 710 $params['limit'], $params['offset'], $root_table, $selectable_tables); 711 712 // If 'with' is set and the result data is not empty 713 if (!empty($params['with']) && !empty($data)) { 714 if ($params['select'] != 'all') { 715 $this->stack->push( 716 LIVEUSER_ADMIN_ERROR, 'exception', 717 array('msg' => 'Using "with" requires "select" to be set to "all"') 718 ); 719 return false; 720 } 721 // Check if all with keys were fetched 722 $missing = array_diff(array_keys($params['with']), array_keys(reset($data))); 723 if (!empty($missing)) { 724 $this->stack->push( 725 LIVEUSER_ADMIN_ERROR, 'exception', 726 array('msg' => 'The following "with" elements are not included in the result: '.implode(', ', $missing)) 727 ); 728 return false; 729 } 730 foreach ($data as $key => $row) { 731 foreach ($params['with'] as $field => $with_params) { 732 $with_params['filters'][$field] = $row[$field]; 733 $method = $this->withFieldMethodMap[$field]; 734 // remove "_id" from the field name (group_id => group) 735 $data_key = preg_replace('/(.+)_id/', '\\1s', $field); 736 $data[$key][$data_key] = $this->$method($with_params); 737 } 738 } 739 } 740 741 return $data; 742 } 743 744 /** 745 * Fetches users 746 * 747 * @param array containing key-value pairs for: 748 * 'fields' - ordered array containing the fields to fetch 749 * if empty all fields from the user table are fetched 750 * 'filters' - key values pairs (value may be a string or an array) 751 * 'orders' - key value pairs (values 'ASC' or 'DESC') 752 * 'rekey' - if set to true, returned array will have the 753 * first column as its first dimension 754 * 'group' - if set to true and $rekey is set to true, then 755 * all values with the same first column will be 756 * wrapped in an array 757 * 'limit' - number of rows to select 758 * 'offset' - first row to select 759 * 'select' - determines what query method to use: 760 * 'one' -> queryOne, 'row' -> queryRow, 761 * 'col' -> queryCol, 'all' ->queryAll (default) 762 * 'selectable_tables' - array list of tables that may be 763 * joined to in this query, the first element is 764 * the root table from which the joins are done 765 * @return bool|array false on failure or array with selected data 766 * 767 * @access public 768 */ 769 function getUsers($params = array()) 770 { 771 $selectable_tables = $this->_findSelectableTables('getUsers', $params); 772 $root_table = reset($selectable_tables); 773 774 return $this->_makeGet($params, $root_table, $selectable_tables); 775 } 776 777 /** 778 * Fetches rights 779 * 780 * @param array containing key-value pairs for: 781 * 'fields' - ordered array containing the fields to fetch 782 * if empty all fields from the user table are fetched 783 * 'filters' - key values pairs (value may be a string or an array) 784 * 'orders' - key value pairs (values 'ASC' or 'DESC') 785 * 'rekey' - if set to true, returned array will have the 786 * first column as its first dimension 787 * 'group' - if set to true and $rekey is set to true, then 788 * all values with the same first column will be 789 * wrapped in an array 790 * 'limit' - number of rows to select 791 * 'offset' - first row to select 792 * 'select' - determines what query method to use: 793 * 'one' -> queryOne, 'row' -> queryRow, 794 * 'col' -> queryCol, 'all' ->queryAll (default) 795 * 'selectable_tables' - array list of tables that may be 796 * joined to in this query, the first element is 797 * the root table from which the joins are done 798 * @return bool|array false on failure or array with selected data 799 * 800 * @access public 801 */ 802 function getRights($params = array()) 803 { 804 $selectable_tables = $this->_findSelectableTables('getRights' , $params); 805 $root_table = reset($selectable_tables); 806 807 return $this->_makeGet($params, $root_table, $selectable_tables); 808 } 809 810 /** 811 * Fetches areas 812 * 813 * @param array containing key-value pairs for: 814 * 'fields' - ordered array containing the fields to fetch 815 * if empty all fields from the user table are fetched 816 * 'filters' - key values pairs (value may be a string or an array) 817 * 'orders' - key value pairs (values 'ASC' or 'DESC') 818 * 'rekey' - if set to true, returned array will have the 819 * first column as its first dimension 820 * 'group' - if set to true and $rekey is set to true, then 821 * all values with the same first column will be 822 * wrapped in an array 823 * 'limit' - number of rows to select 824 * 'offset' - first row to select 825 * 'select' - determines what query method to use: 826 * 'one' -> queryOne, 'row' -> queryRow, 827 * 'col' -> queryCol, 'all' ->queryAll (default) 828 * 'selectable_tables' - array list of tables that may be 829 * joined to in this query, the first element is 830 * the root table from which the joins are done 831 * @return bool|array false on failure or array with selected data 832 * 833 * @access public 834 */ 835 function getAreas($params = array()) 836 { 837 $selectable_tables = $this->_findSelectableTables('getAreas' , $params); 838 $root_table = reset($selectable_tables); 839 840 return $this->_makeGet($params, $root_table, $selectable_tables); 841 } 842 843 /** 844 * Fetches applications 845 * 846 * @param array containing key-value pairs for: 847 * 'fields' - ordered array containing the fields to fetch 848 * if empty all fields from the user table are fetched 849 * 'filters' - key values pairs (value may be a string or an array) 850 * 'orders' - key value pairs (values 'ASC' or 'DESC') 851 * 'rekey' - if set to true, returned array will have the 852 * first column as its first dimension 853 * 'group' - if set to true and $rekey is set to true, then 854 * all values with the same first column will be 855 * wrapped in an array 856 * 'limit' - number of rows to select 857 * 'offset' - first row to select 858 * 'select' - determines what query method to use: 859 * 'one' -> queryOne, 'row' -> queryRow, 860 * 'col' -> queryCol, 'all' ->queryAll (default) 861 * 'selectable_tables' - array list of tables that may be 862 * joined to in this query, the first element is 863 * the root table from which the joins are done 864 * @return bool|array false on failure or array with selected data 865 * 866 * @access public 867 */ 868 function getApplications($params = array()) 869 { 870 $selectable_tables = $this->_findSelectableTables('getApplications' , $params); 871 $root_table = reset($selectable_tables); 872 873 return $this->_makeGet($params, $root_table, $selectable_tables); 874 } 875 876 /** 877 * Fetches translations 878 * 879 * @param array containing key-value pairs for: 880 * 'fields' - ordered array containing the fields to fetch 881 * if empty all fields from the user table are fetched 882 * 'filters' - key values pairs (value may be a string or an array) 883 * 'orders' - key value pairs (values 'ASC' or 'DESC') 884 * 'rekey' - if set to true, returned array will have the 885 * first column as its first dimension 886 * 'group' - if set to true and $rekey is set to true, then 887 * all values with the same first column will be 888 * wrapped in an array 889 * 'limit' - number of rows to select 890 * 'offset' - first row to select 891 * 'select' - determines what query method to use: 892 * 'one' -> queryOne, 'row' -> queryRow, 893 * 'col' -> queryCol, 'all' ->queryAll (default) 894 * 'selectable_tables' - array list of tables that may be 895 * joined to in this query, the first element is 896 * the root table from which the joins are done 897 * @return bool|array false on failure or array with selected data 898 * 899 * @access public 900 */ 901 function getTranslations($params = array()) 902 { 903 $selectable_tables = $this->_findSelectableTables('getTranslations' , $params); 904 $root_table = reset($selectable_tables); 905 906 return $this->_makeGet($params, $root_table, $selectable_tables); 907 } 908 909 /** 910 * Generate the constants to a file or define them directly. 911 * 912 * $type can be either 'constant' or 'array'. Constant will result in 913 * defining constants while array results in defining an array. 914 * 915 * $options can contain 916 * 'prefix' => prefix for the generated (qualified) names 917 * 'area' => specific area id to grab rights from 918 * 'application' => specific application id to grab rights from 919 * 'filters' => specific set of filters to use (overwrites area/application) 920 * 'by_group' => if joins should be done using the 'userrights' (false default) 921 * or through the 'grouprights' and 'groupusers' tables (true) 922 * 'inherited' => filter array to fetch all rights from (sub)group membership 923 * 'implied' => filter array for fetching implied rights 924 * 'naming' => LIVEUSER_SECTION_RIGHT for PREFIX_RIGHTNAME <- DEFAULT 925 * LIVEUSER_SECTION_AREA for PREFIX_AREANAME_RIGHTNAME 926 * LIVEUSER_SECTION_APPLICATION for PREFIX_APPLICATIONNAME_AREANAME_RIGHTNAME 927 * 'filename' => if $mode is 'file' you must give the full path for the 928 * output file 929 * 'varname' => if $mode is 'file' and $type is 'array' you must give 930 * the name of the variable to define 931 * 932 * If no prefix is given it will not be used to generate the constants/arrays 933 * 934 * $mode can either be 'file' or 'direct' and will determine of the 935 * constants/arrays will be written to a file, or returned/defined. 936 * returned as an array when $type is set to 'array' and defined when $type 937 * is set to 'constant' 938 * 939 * @param string type of output ('constant' or 'array') 940 * @param array options for constants generation 941 * @param string output mode desired ('file' or 'direct') 942 * @return bool|array depending on the type an array with the data or 943 * a boolean denoting success or failure 944 * 945 * @access public 946 */ 947 function outputRightsConstants($type, $options = array(), $mode = null) 948 { 949 $params = array(); 950 951 // Prepare the fields to fetch. 952 $params['fields'] = array('right_id', 'right_define_name'); 953 954 $naming = LIVEUSER_SECTION_RIGHT; 955 if (array_key_exists('naming', $options)) { 956 $naming = $options['naming']; 957 switch ($naming) { 958 case LIVEUSER_SECTION_AREA: 959 $params['fields'][] = 'area_define_name'; 960 break; 961 case LIVEUSER_SECTION_APPLICATION: 962 $params['fields'][] = 'application_define_name'; 963 $params['fields'][] = 'area_define_name'; 964 break; 965 } 966 } 967 968 // Prepare the filters. 969 if (array_key_exists('by_group', $options)) { 970 $params['by_group'] = $options['by_group']; 971 } 972 973 if (array_key_exists('inherited', $options)) { 974 $params['inherited'] = $options['inherited']; 975 } 976 977 if (array_key_exists('implied', $options)) { 978 $params['implied'] = $options['implied']; 979 } 980 981 if (array_key_exists('filters', $options)) { 982 $params['filters'] = $options['filters']; 983 } else { 984 if (array_key_exists('area', $options)) { 985 $params['filters']['area_id'] = $options['area']; 986 } 987 988 if (array_key_exists('application', $options)) { 989 $params['filters']['application_id'] = $options['application']; 990 } 991 } 992 993 $prefix = ''; 994 if (array_key_exists('prefix', $options)) { 995 $prefix = $options['prefix'] . '_'; 996 } 997 998 $rekey = false; 999 if ($type == 'array' && array_key_exists('rekey', $options)) { 1000 $rekey = $options['rekey']; 1001 } 1002 1003 $rights = $this->getRights($params); 1004 1005 if ($rights === false) { 1006 return false; 1007 } 1008 1009 $generate = array(); 1010 1011 // Prepare an array containing all the rights to be defined. The stucture of 1012 // this array is dependent on the value of naming and if the rekey is set. 1013 switch ($naming) { 1014 case LIVEUSER_SECTION_APPLICATION: 1015 if ($rekey) { 1016 foreach ($rights as $r) { 1017 $app_name = $prefix . $r['application_define_name']; 1018 $area_name = $r['area_define_name']; 1019 $generate[$app_name][$area_name][$r['right_define_name']] = $r['right_id']; 1020 } 1021 } else { 1022 foreach ($rights as $r) { 1023 $key = $prefix . $r['application_define_name'] . '_' 1024 . $r['area_define_name'] . '_' . $r['right_define_name']; 1025 $generate[$key] = $r['right_id']; 1026 } 1027 } 1028 break; 1029 case LIVEUSER_SECTION_AREA: 1030 if ($rekey) { 1031 foreach ($rights as $r) { 1032 $area_name = $prefix . $r['area_define_name']; 1033 $generate[$area_name][$r['right_define_name']] = $r['right_id']; 1034 } 1035 } else { 1036 foreach ($rights as $r) { 1037 $key = $prefix . $r['area_define_name'] . '_' . $r['right_define_name']; 1038 $generate[$key] = $r['right_id']; 1039 } 1040 } 1041 break; 1042 case LIVEUSER_SECTION_RIGHT: 1043 default: 1044 foreach ($rights as $r) { 1045 $generate[$prefix . $r['right_define_name']] = $r['right_id']; 1046 } 1047 break; 1048 } 1049 1050 if ($type == 'array' && $mode != 'file') { 1051 return $generate; 1052 } 1053 1054 // Define the rights, either as an array or defines. 1055 // Add an error to the stack if the provided variable name is not valid. 1056 if ($type == 'array') { 1057 if (!array_key_exists('varname', $options) 1058 || !preg_match('/^[a-zA-Z_0-9]+$/', $options['varname']) 1059 ) { 1060 $this->stack->push( 1061 LIVEUSER_ADMIN_ERROR_FILTER, 'exception', 1062 array('msg' => 'varname is not a valid variable name in PHP: '.$options['varname']) 1063 ); 1064 return false; 1065 } 1066 $strDef = sprintf("\$%s = %s;\n", $options['varname'], var_export($generate, true)); 1067 } else { 1068 if ($mode == 'file') { 1069 $strDef = ''; 1070 } 1071 foreach ($generate as $v => $k) { 1072 if (!preg_match('/^[a-zA-Z_0-9]+$/', $v)) { 1073 $this->stack->push( 1074 LIVEUSER_ADMIN_ERROR_FILTER, 'exception', 1075 array('msg' => 'definename is not a valid define name in PHP: '.$v) 1076 ); 1077 return false; 1078 } 1079 $v = strtoupper($v); 1080 if ($mode == 'file') { 1081 $strDef .= sprintf("define('%s', %s);\n", $v, $k); 1082 } elseif (!defined($v)) { 1083 define($v, $k); 1084 } 1085 } 1086 } 1087 1088 if ($mode != 'file') { 1089 return true; 1090 } 1091 1092 // The results should be written to a file. 1093 // If the filename doesn't exist or the file cannot be opened, add an error to the stack. 1094 if (!array_key_exists('filename', $options) || !$options['filename']) { 1095 $this->stack->push( 1096 LIVEUSER_ADMIN_ERROR_FILTER, 'exception', 1097 array('msg' => 'no filename is set for output mode file') 1098 ); 1099 return false; 1100 } 1101 1102 $fp = @fopen($options['filename'], 'wb'); 1103 1104 if (!$fp) { 1105 $this->stack->push( 1106 LIVEUSER_ADMIN_ERROR_FILTER, 'exception', 1107 array('msg' => 'file could not be opened: '.$options['filename']) 1108 ); 1109 return false; 1110 } 1111 1112 @fputs($fp, "<?php\n".$strDef.'?>'); 1113 @fclose($fp); 1114 1115 return true; 1116 } 1117 1118 /** 1119 * properly disconnect from resources 1120 * 1121 * @access public 1122 */ 1123 function disconnect() 1124 { 1125 $this->_storage->disconnect(); 1126 } 1127} 1128?> 1129