1<?php 2/* 3* e107 website system 4* 5* Copyright (C) 2008-2012 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* Extended user field handler 10* 11*/ 12 13if (!defined('e107_INIT')) { exit; } 14 15/** 16 * Extended user field handler 17 * 18 * @todo: - change some routines to access the cached variables rather than DB 19 * @todo: Remove setting up of _FIELD_TYPES array (may be necessary, since UEF data structure not fixed) 20 * @todo: Consider changing field type constants to class constants 21 * @todo - cache field structure (already done in a different way in e107::user() in class2.php line 1387 or so) 22 * @todo - class variables - confirm whether public/protected assignments are correct 23 * @todo - consider whether to split system and non-system fields 24 25Code uses two tables: 26 user_extended_struct - individual field definitions, one record per field 27 user_extended - actual field data, one record per user 28 29@todo: Should user_extended_validate_entry() check DB for DB-type fields? 30 31*/ 32 33e107::coreLan('user_extended'); 34 35class e107_user_extended 36{ 37 public $user_extended_types; // Text description corresponding to each field type 38 private $extended_xml = FALSE; 39 public $typeArray; // Cross-reference between names of field types, and numeric ID (must be public) 40 private $reserved_names; // List of field names used in main user DB - not allowed in extended DB 41 public $fieldDefinitions = array(); // Array initialised from DB by constructor - currently all fields 42 public $catDefinitions; // Categories 43 private $nameIndex = array(); // Array for field name lookup - initialised by constructor 44 public $systemCount = 0; // Count of system fields - always zero ATM 45 public $userCount = 0; // Count of non-system fields 46 private $fieldAttributes = array(); // Field Permissionss with field name as key. 47 48 public function __construct() 49 { 50 @define('EUF_CATEGORY', 0); 51 @define('EUF_TEXT',1); 52 @define('EUF_RADIO',2); 53 @define('EUF_DROPDOWN',3); 54 @define('EUF_DB_FIELD',4); 55 @define('EUF_TEXTAREA',5); 56 @define('EUF_INTEGER',6); 57 @define('EUF_DATE',7); 58 @define('EUF_LANGUAGE',8); 59 @define('EUF_PREDEFINED',9); // should be EUF_LIST IMO 60 @define('EUF_CHECKBOX',10); 61 @define('EUF_PREFIELD',11); // should be EUF_PREDEFINED, useful when creating fields from e.g. plugin XML 62 @define('EUF_ADDON', 12); // defined within e_user.php addon @todo 63 @define('EUF_COUNTRY', 13); // $frm->country() 64 @define('EUF_RICHTEXTAREA', 14); // $frm->bbarea() 65 66 $this->typeArray = array( 67 'text' => EUF_TEXT, 68 'radio' => EUF_RADIO, 69 'dropdown' => EUF_DROPDOWN, 70 'db field' => EUF_DB_FIELD, 71 'textarea' => EUF_TEXTAREA, 72 'integer' => EUF_INTEGER, 73 'date' => EUF_DATE, 74 'language' => EUF_LANGUAGE, 75 'list' => EUF_PREDEFINED, 76 'checkbox' => EUF_CHECKBOX, 77 'predefined' => EUF_PREFIELD, // DON'T USE IT IN PREDEFINED FIELD XML!!! Used in plugin installation routine. 78 'addon' => EUF_ADDON, 79 'country' => EUF_COUNTRY, 80 'richtextarea' => EUF_RICHTEXTAREA, 81 ); 82 83 $this->user_extended_types = array( 84 1 => UE_LAN_1, 85 2 => UE_LAN_2, 86 3 => UE_LAN_3, 87 4 => UE_LAN_4, 88 5 => UE_LAN_5, 89 14 => UE_LAN_14, 90 6 => UE_LAN_6, 91 7 => LAN_DATE, 92 8 => UE_LAN_8, 93 9 => UE_LAN_9, 94 10 => UE_LAN_10, 95 13 => UE_LAN_13, 96 // 12=> UE_LAN_10 97 98 ); 99 100 //load array with field names from main user table, so we can disallow these 101 // user_new, user_timezone deleted for 0.8 102 $this->reserved_names = array ( 103 'id', 'name', 'loginname', 'customtitle', 'password', 104 'sess', 'email', 'signature', 'image', 'hideemail', 105 'join', 'lastvisit', 'currentvisit', 'chats', 106 'comments', 'forums', 'ip', 'ban', 'prefs', 'viewed', 107 'visits', 'admin', 'login', 'class', 'baseclasslist', 'perms', 'pwchange', 108 'xup' 109 ); 110 111 $this->init(); 112 113 } 114 115 public function init() 116 { 117 $sql = e107::getDb(); 118 119 // Read in all the field and category fields 120 // At present we load all fields into common array - may want to split system and non-system 121 $this->catDefinitions = array(); // Categories array 122 $this->nameIndex = array(); // Index of names => field IDs 123 $this->systemCount = 0; 124 $this->userCount = 0; 125 126 if($sql->select('user_extended_struct', '*', "user_extended_struct_text != '_system_' ORDER BY user_extended_struct_order ASC")) 127 { 128 while($row = $sql->fetch()) 129 { 130 if ($row['user_extended_struct_type'] == 0) 131 { // Its a category 132 $this->catDefinitions[$row['user_extended_struct_id']] = $row; 133 } 134 else 135 { // Its a field definition 136 $this->fieldDefinitions[$row['user_extended_struct_id']] = $row; 137 $id = 'user_' . $row['user_extended_struct_name']; 138 139 $this->fieldAttributes[$id] = array( 140 'read' => $row['user_extended_struct_read'], 141 'write' => $row['user_extended_struct_write'], 142 'type' => $row['user_extended_struct_type'] 143 ); 144 $this->nameIndex['user_' . $row['user_extended_struct_name']] = $row['user_extended_struct_id']; // Create name to ID index 145 146 if($row['user_extended_struct_text'] == '_system_') 147 { 148 $this->systemCount++; 149 } 150 else 151 { 152 $this->userCount++; 153 } 154 } 155 } 156 } 157 158 return null; 159 } 160 161 162 163 164 165 /** 166 * Check read/write access on extended user-fields 167 * @param string $field eg. user_something 168 * @param string $type read|write 169 * @return boolean true if 170 */ 171 public function hasPermission($field, $type='read') 172 { 173 $class = ($type == 'read') ? $this->fieldAttributes[$field]['read'] : $this->fieldAttributes[$field]['write']; 174 return check_class($class); 175 } 176 177 178 179 /** 180 * Check for reserved field names. 181 * (Names which clash with the 'normal' user table aren't allowed) 182 * @param array $name - name of field bweing checked (no 'user_' prefix) 183 * @return boolean TRUE if disallowed name 184 */ 185 public function user_extended_reserved($name) 186 { 187 return (in_array($name, $this->reserved_names)); 188 } 189 190 191 192 // Adds the _FIELD_TYPES array to the data, ready for saving in the DB. 193 function addFieldTypes(&$target) 194 { 195 $target['_FIELD_TYPES'] = array(); // We should always want to recreate the array, even if it exists 196 foreach ($target['data'] as $k => $v) 197 { 198 if (isset($this->nameIndex[$k])) 199 { 200 switch ($this->fieldDefinitions[$this->nameIndex[$k]]['user_extended_struct_type']) 201 { 202 case EUF_TEXT : 203 case EUF_DB_FIELD : 204 case EUF_TEXTAREA : 205 case EUF_RICHTEXTAREA : 206 case EUF_DROPDOWN : 207 case EUF_DATE : 208 case EUF_LANGUAGE : 209 case EUF_PREDEFINED : 210 211 case EUF_RADIO : 212 $target['_FIELD_TYPES'][$k] = 'todb'; 213 break; 214 215 case EUF_CHECKBOX : 216 $target['_FIELD_TYPES'][$k] = 'array'; 217 break; 218 219 220 case EUF_INTEGER : 221 $target['_FIELD_TYPES'][$k] = 'int'; 222 break; 223 } 224 } 225 } 226 } 227 228 229 230 /** 231 * For all UEFs not in the target array, adds the default value 232 * Also updates the _FIELD_TYPES array, so call this last thing before writing to the DB 233 * 234 * @param $target - pointer to data array 235 */ 236 public function addDefaultFields(&$target) 237 { 238 //$target['_FIELD_TYPES'] = array(); // We should always want to recreate the array, even if it exists 239 foreach ($this->fieldDefinitions as $k => $defs) 240 { 241 $f = 'user_'.$defs['user_extended_struct_name']; 242 if (!isset($target['data'][$f]) && $this->fieldDefinitions[$k]['user_extended_struct_default']) 243 { 244 switch ($this->fieldDefinitions[$k]['user_extended_struct_type']) 245 { 246 case EUF_TEXT : 247 case EUF_DB_FIELD : 248 case EUF_TEXTAREA : 249 case EUF_RICHTEXTAREA : 250 case EUF_DROPDOWN : 251 case EUF_DATE : 252 case EUF_LANGUAGE : 253 case EUF_PREDEFINED : 254 255 $target['data'][$f] = $this->fieldDefinitions[$k]['user_extended_struct_default']; 256 $target['_FIELD_TYPES'][$f] = 'todb'; 257 break; 258 case EUF_RADIO : 259 case EUF_INTEGER : 260 $target['data'][$f] = $this->fieldDefinitions[$k]['user_extended_struct_default']; 261 $target['_FIELD_TYPES'][$f] = 'int'; 262 break; 263 case EUF_CHECKBOX : 264 $target['data'][$f] = $this->fieldDefinitions[$k]['user_extended_struct_default']; 265 $target['_FIELD_TYPES'][$f] = 'array'; 266 break; 267 } 268 } 269 } 270 } 271 272 273 274 // Validate a single extended user field 275 // $val is whatever the user entered. 276 // $params is the field definition 277 // Return FALSE if acceptable, TRUE if fail , error message on regex fail if the message is defined 278 function user_extended_validate_entry($val, $params) 279 { 280 $tp = e107::getParser(); 281 282 $parms = explode('^,^', $params['user_extended_struct_parms']); 283 $requiredField = $params['user_extended_struct_required'] == 1; 284 $regex = $tp->toText($parms[1]); 285 $regexfail = $tp->toText($parms[2]); 286 if(defined($regexfail)) 287 { 288 $regexfail = constant($regexfail); 289 } 290 if($val == '' && $requiredField) 291 { 292 return true; 293 } 294 295 $type = $params['user_extended_struct_type']; 296 297 switch($type) 298 { 299 case EUF_DATE : 300 if($requiredField && ($val == '0000-00-00')) 301 { 302 return true; 303 } 304 break; 305 } 306 if($regex != "" && $val != "") 307 { 308 if(!preg_match($regex, $val)) 309 { 310 return $regexfail ? $regexfail : true; 311 } 312 } 313 314 return false; // Pass by default here 315 } 316 317 318 319 /** 320 * Validate all user-modifable extended user fields which are presented. 321 * Primarily intended to validate data entered by a user or admin 322 * 323 * @param array $inArray is the input data (usually from $_POST or $_POST['ue'], although doesn't have to be) - may have 'surplus' values 324 * @param array $hideArray is a set of possible 'hide' flags 325 * @param boolean $isSignup TRUE causes required fields to be specifically checked, else only data passed is checked 326 * 327 * @return array with three potential subkeys: 328 * 'data' - valid data values (key is field name) 329 * ['data']['user_hidden_fields'] is the hidden fields 330 * 'errors' - data values in error 331 * 'errortext' - error message corresponding to erroneous value 332 * 333 * @todo - does $hidden_fields need to be merged with values for fields not processed? (Probably not - should only relate to fields current user can see) 334 * @todo - make sure admin can edit fields of other users 335 */ 336 public function userExtendedValidateAll($inArray, $hideArray, $isSignup=FALSE) 337 { 338 $tp = e107::getParser(); 339 340 $eufVals = array(); // 'Answer' array 341 $hideFlags = array(); 342 343 foreach ($this->fieldDefinitions as $k => $defs) 344 { 345 $category = $defs['user_extended_struct_parent']; 346 if (($category == 0) || ($isSignup && (int) $this->catDefinitions[$category]['user_extended_struct_applicable'] === (int) e_UC_MEMBER && (int) $this->catDefinitions[$category]['user_extended_struct_write'] === (int) e_UC_MEMBER) || (check_class($this->catDefinitions[$category]['user_extended_struct_applicable']) && check_class($this->catDefinitions[$category]['user_extended_struct_write']))) 347 { // Category applicable to user 348 349 if (($isSignup && (int) $defs['user_extended_struct_applicable'] === (int) e_UC_MEMBER && (int) $defs['user_extended_struct_write'] === (int) e_UC_MEMBER) || (check_class($defs['user_extended_struct_applicable']) && check_class($defs['user_extended_struct_write']))) 350 { // User can also update field 351 $f = 'user_'.$defs['user_extended_struct_name']; 352 if (isset($inArray[$f]) || ($isSignup && ($defs['user_extended_struct_required'] == 1))) 353 { // Only allow valid keys 354 $val = varset($inArray[$f], FALSE); 355 $err = $this->user_extended_validate_entry($val, $defs); 356 if ($err === true) 357 { // General error - usually empty field; could be unacceptable value, or regex fail and no error message defined 358 $eufVals['errortext'][$f] = str_replace('[x]',$tp->toHTML(defset($defs['user_extended_struct_text'], $defs['user_extended_struct_text']),FALSE,'defs'),LAN_USER_75); 359 $eufVals['errors'][$f] = ERR_GENERIC; 360 } 361 elseif ($err) 362 { // Specific error message returned - usually regex fail 363 $eufVals['errortext'][$f] = $err; 364 $eufVals['errors'][$f] = ERR_GENERIC; 365 } 366 elseif (!$err) 367 { 368 $eufVals['data'][$f] = $tp->toDB($val); 369 } 370 if (isset($hideArray[$f])) 371 { 372 $hideFlags[] = $f; 373 } 374 } 375 } 376 } 377 } 378 $hidden_fields = implode('^', $hideFlags); 379 if ($hidden_fields != '') 380 { 381 $hidden_fields = '^'.$hidden_fields.'^'; 382 } 383 $eufVals['data']['user_hidden_fields'] = $hidden_fields; 384 385 return $eufVals; 386 } 387 388 389 /** 390 * Sanitize User submitted user-extended fields. 391 * @param $posted 392 * @return array 393 */ 394 function sanitizeAll($posted) 395 { 396 397 $arr = array(); 398 399 foreach($posted as $field => $value) 400 { 401 $type = $this->getFieldType($field); 402 403 switch($type) 404 { 405 406 case EUF_INTEGER : //integer 407 $arr[$field] = (int) $value; 408 break; 409 410 case EUF_TEXT : //textbox 411 case EUF_COUNTRY: 412 case EUF_RADIO : //radio 413 case EUF_CHECKBOX : //checkboxes 414 case EUF_DROPDOWN : //dropdown 415 case EUF_PREDEFINED : // predefined list, shown in dropdown 416 case EUF_DB_FIELD : //db_field 417 case EUF_DATE : //date 418 case EUF_LANGUAGE : // language 419 case EUF_TEXTAREA : //textarea 420 case EUF_PREFIELD: 421 case EUF_ADDON: 422 423 $arr[$field] = filter_var($value,FILTER_SANITIZE_STRING); 424 break; 425 426 case EUF_RICHTEXTAREA : // rich textarea (using WYSIWYG editor) 427 $arr[$field] = e107::getParser()->cleanHtml($value); 428 break; 429 430 default: 431 e107::getDebug()->log("User extended field: ".$field." is missing a valid field-type."); 432 433 } 434 435 436 } 437 438 439 return $arr; 440 441 442 } 443 444 445 446 /** 447 * alias of user_extended_get_categories(); 448 * 449 * @param bool $byID 450 * @return array 451 */ 452 function getCategories($byID = TRUE) 453 { 454 return $this->user_extended_get_categories($byID); 455 } 456 457 458 function user_extended_get_categories($byID = TRUE) 459 { 460 $ret = array(); 461 $sql = e107::getDb('ue'); 462 463 if($sql->select("user_extended_struct", "*", "user_extended_struct_type = 0 ORDER BY user_extended_struct_order ASC")) 464 { 465 if($byID == TRUE) 466 { 467 while($row = $sql->fetch()) 468 { 469 $ret[$row['user_extended_struct_id']][] = $row; 470 } 471 } 472 else 473 { 474 $ret = $sql->db_getList(); 475 } 476 } 477 return $ret; 478 } 479 480 481 /** 482 * BC Alias of getFields(); 483 * @param string $cat 484 * @return mixed 485 */ 486 public function getFields($cat = "") 487 { 488 return $this->user_extended_get_fieldList($cat); 489 } 490 491 492 // Get the definition of all fields, or those in a specific category, grouped by category ID 493 // Reads non-system fields only 494 function user_extended_get_fields($cat = "") 495 { 496 $sql = e107::getDb('ue'); 497 $ret = array(); 498 $more = ($cat) ? " AND user_extended_struct_parent = ".intval($cat)." " : ""; 499 if($sql->select("user_extended_struct", "*", "user_extended_struct_type > 0 AND user_extended_struct_text != '_system_' {$more} ORDER BY user_extended_struct_order ASC")) 500 { 501 while($row = $sql->fetch()) 502 { 503 $ret[$row['user_extended_struct_parent']][] = $row; 504 } 505 } 506 return $ret; 507 } 508 509 510 /** 511 * Alias of user_extended_get_fieldList(). 512 * @param string $cat 513 * @param string $indexField 514 * @return mixed 515 */ 516 function getFieldList($cat = "", $indexField = 'user_extended_struct_id') 517 { 518 return $this->user_extended_get_fieldList($cat, $indexField); 519 } 520 521 522 /** 523 * Get the definition of all fields, or those in a specific category, indexed by field ID (or some other field by specifying $indexField) 524 * @param $cat 525 * @param $indexField; 526 * @param $system - include system fields. 527 * @return array 528 */ 529 function user_extended_get_fieldList($cat = "", $indexField = 'user_extended_struct_id', $system = false) 530 { 531 if(!$indexField) 532 { 533 $indexField = 'user_extended_struct_id'; 534 } 535 536 $sql = e107::getDb('ue'); 537 538 $ret = array(); 539 540 $more = ($cat != '') ? " AND user_extended_struct_parent = ".intval($cat)." " : ""; 541 $sys = ($system == false) ? " AND user_extended_struct_text != '_system_' " : ""; 542 543 if($sql->select("user_extended_struct", "*", "user_extended_struct_type > 0 {$sys} {$more} ORDER BY user_extended_struct_order ASC")) 544 { 545 while($row = $sql->fetch()) 546 { 547 $ret[$row[$indexField]] = $row; 548 } 549 } 550 551 return $ret; 552 } 553 554 555 /** 556 * Return the list of user_extended fields. 557 * @return array 558 */ 559 function getFieldNames() 560 { 561 $ret = array(); 562 563 $sql = e107::getDb('ue'); 564 565 if($sql->select("user_extended_struct", "*", "user_extended_struct_type > 0 ORDER BY user_extended_struct_order ASC")) 566 { 567 while($row = $sql->fetch()) 568 { 569 $ret[] = 'user_'.$row['user_extended_struct_name']; 570 } 571 } 572 return $ret; 573 574 } 575 576 577 /** 578 * Get the field-type of a given field-name. 579 * @param $field 580 * @return bool|int 581 */ 582 public function getFieldType($field) 583 { 584 585 if(!empty($this->fieldAttributes[$field]['type'])) 586 { 587 return (int) $this->fieldAttributes[$field]['type']; 588 } 589 590 return false; 591 } 592 593 594 /** 595 * Return a list of all field types. 596 * @return array 597 */ 598 public function getFieldTypes() 599 { 600 return $this->user_extended_types; 601 602 } 603 604 605 // Return the field creation text for a definition 606 /** 607 * @param $type 608 * @param $default 609 * @return bool|string 610 */ 611 function user_extended_type_text($type, $default) 612 { 613 $tp = e107::getParser(); 614 615 if(!is_numeric($type)) 616 { 617 return false; 618 } 619 620 switch ($type) 621 { 622 case EUF_COUNTRY : 623 $db_type = 'VARCHAR(2)'; 624 break; 625 626 case EUF_INTEGER : 627 $db_type = 'INT(11)'; 628 break; 629 630 case EUF_DATE : 631 $db_type = 'DATE'; 632 break; 633 634 case EUF_TEXTAREA: 635 case EUF_RICHTEXTAREA : 636 case EUF_CHECKBOX : 637 $db_type = 'TEXT'; 638 break; 639 640 case EUF_TEXT : 641 case EUF_RADIO : 642 case EUF_DROPDOWN : 643 case EUF_DB_FIELD : 644 case EUF_LANGUAGE : 645 case EUF_PREDEFINED : 646 647 648 $db_type = 'VARCHAR(255)'; 649 break; 650 651 case EUF_PREFIELD: // FIXME Predefined field - this should be assignable from XML typically. 652 $db_type = 'VARCHAR(255)'; 653 break; 654 655 case EUF_CATEGORY: 656 return ''; 657 break; 658 659 case EUF_ADDON: 660 return 'JSON'; 661 break; 662 663 default: 664 e107::getMessage()->addDebug("<strong>Unknown type '{$type}' for user extended field.</strong>"); 665 return false; 666 break; 667 668 } 669 if($type != EUF_DB_FIELD && ($type != EUF_TEXTAREA) && ($type != EUF_RICHTEXTAREA) && ($type != EUF_CHECKBOX) && !empty($default)) 670 { 671 $default_text = " DEFAULT '".$tp -> toDB($default, true)."'"; 672 } 673 else 674 { 675 $default_text = ''; 676 } 677 678 679 return $db_type.$default_text; 680 } 681 682 683 function user_extended_field_exist($name) 684 { 685 $sql = e107::getDb('sql2'); 686 $tp = e107::getParser(); 687 return $sql->count('user_extended_struct','(*)', "WHERE user_extended_struct_name = '".$tp -> toDB($name, true)."'"); 688 } 689 690 function clear_cache() 691 { 692 e107::getCache()->clear_sys('nomd5_extended_struct'); 693 } 694 695 // For use by plugins to add extended user fields and won't be visible anywhere else 696 function user_extended_add_system($name, $type, $default = '', $source = '_system_') 697 { 698 return $this->user_extended_add($name, '_system_', $type, $source, '', $default, 0, 255, 255, 255, 0, 0); 699 } 700 701 function user_extended_add($name, $text='', $type='', $parms='', $values='', $default='', $required='', $read='', $write='', $applicable='', $order='', $parent='') 702 { 703 704 $sql = e107::getDb('ue'); 705 $tp = e107::getParser(); 706 707 $this->clear_cache(); 708 709 if(is_array($name)) 710 { 711 extract($name); 712 } 713 714 if(!is_numeric($type)) 715 { 716 $type = $this->typeArray[$type]; 717 } 718 719 if($this->user_extended_field_exist($name) && $sql->field('user_extended', 'user_'.$name)!==false) 720 { 721 return true; 722 } 723 724 if ($this->user_extended_reserved($name)) 725 { 726 e107::getMessage()->addDebug("Reserved Field"); 727 return false; 728 } 729 730 $field_info = $this->user_extended_type_text($type, $default); 731 732 // wrong type 733 if(false === $field_info) 734 { 735 e107::getMessage()->addDebug("\$field_info is false ".__METHOD__); 736 return false; 737 } 738 739 if($order === '' && $field_info) 740 { 741 if($sql->select('user_extended_struct','MAX(user_extended_struct_order) as maxorder','1')) 742 { 743 $row = $sql->fetch(); 744 if(is_numeric($row['maxorder'])) 745 { 746 $order = $row['maxorder']+1; 747 } 748 } 749 } 750 // field of type category 751 if($field_info) 752 { 753 $sql->gen('ALTER TABLE #user_extended ADD user_'.$tp -> toDB($name, true).' '.$field_info); 754 } 755 756 /* TODO 757 $extStructInsert = array( 758 'user_extended_struct_id' => '_NULL_', 759 'user_extended_struct_name' => '', 760 'user_extended_struct_text' => '', 761 'user_extended_struct_type' => '', 762 'user_extended_struct_parms' => '', 763 'user_extended_struct_values' => '', 764 'user_extended_struct_default' => '', 765 'user_extended_struct_read' => '', 766 'user_extended_struct_write' => '', 767 'user_extended_struct_required' => '', 768 'user_extended_struct_signup' => '', 769 'user_extended_struct_applicable' => '', 770 'user_extended_struct_order' => '', 771 'user_extended_struct_parent' => '' 772 ); 773 */ 774 775 if(!$this->user_extended_field_exist($name)) 776 { 777 $sql->insert('user_extended_struct',"null,'".$tp -> toDB($name, true)."','".$tp -> toDB($text, true)."','".intval($type)."','".$tp -> toDB($parms, true)."','".$tp -> toDB($values, true)."', '".$tp -> toDB($default, true)."', '".intval($read)."', '".intval($write)."', '".intval($required)."', '0', '".intval($applicable)."', '".intval($order)."', '".intval($parent)."'"); 778 } 779 780 if($this->user_extended_field_exist($name)) 781 { 782 return true; 783 } 784 785 return false; 786 } 787 788 789 790 function user_extended_modify($id, $name, $text, $type, $parms, $values, $default, $required, $read, $write, $applicable, $parent) 791 { 792 $sql = e107::getDb('ue'); 793 $tp = e107::getParser(); 794 795 if ($this->user_extended_field_exist($name)) 796 { 797 $field_info = $this->user_extended_type_text($type, $default); 798 // wrong type 799 if(false === $field_info) return false; 800 801 // field of type category 802 if($field_info) 803 { 804 $sql->gen("ALTER TABLE #user_extended MODIFY user_".$tp -> toDB($name, true)." ".$field_info); 805 } 806 807 $newfield_info = " 808 user_extended_struct_text = '".$tp -> toDB($text, true)."', 809 user_extended_struct_type = '".intval($type)."', 810 user_extended_struct_parms = '".$tp -> toDB($parms, true)."', 811 user_extended_struct_values = '".$tp -> toDB($values, true)."', 812 user_extended_struct_default = '".$tp -> toDB($default, true)."', 813 user_extended_struct_required = '".intval($required)."', 814 user_extended_struct_read = '".intval($read)."', 815 user_extended_struct_write = '".intval($write)."', 816 user_extended_struct_applicable = '".intval($applicable)."', 817 user_extended_struct_parent = '".intval($parent)."' 818 WHERE user_extended_struct_id = '".intval($id)."' 819 "; 820 return $sql->update("user_extended_struct", $newfield_info); 821 } 822 823 return false; 824 } 825 826 function user_extended_remove($id, $name) 827 { 828 $sql = e107::getDb('ue'); 829 $tp = e107::getParser(); 830 831 $this->clear_cache(); 832 if ($this->user_extended_field_exist($name)) 833 { 834 // FIXME - no table structure changes for categories 835 // but no good way to detect it right now - ignore the sql error for now, fix it asap 836 $sql->gen("ALTER TABLE #user_extended DROP user_".$tp -> toDB($name, true)); 837 838 if(is_numeric($id)) 839 { 840 $sql->delete("user_extended_struct", "user_extended_struct_id = '".intval($id)."' "); 841 } 842 else 843 { 844 $sql->delete("user_extended_struct", "user_extended_struct_name = '".$tp -> toDB($id, true)."' "); 845 } 846 return !($this->user_extended_field_exist($name)); 847 } 848 849 return false; 850 } 851 852 function user_extended_hide($struct, $curval) 853 { 854 $chk = ($curval) ? " checked='checked' " : ""; 855 $name = "hide[user_".$struct['user_extended_struct_name']."]"; 856 return "<input type='checkbox' {$chk} value='1' name='{$name}' /> ".UE_LAN_HIDE; 857 } 858 859 860 /** 861 * BC alias of renderElement 862 * 863 * @param array $struct 864 * @param mixed $curval 865 * @return array|string 866 */ 867 function user_extended_edit($struct, $curval) 868 { 869 return $this->renderElement($struct, $curval); 870 } 871 872 873 /** 874 * @param array $struct 875 * @param mixed $curval 876 * @param array $opts 877 * @return array|string 878 */ 879 public function renderElement($struct, $curval, $opts=array()) 880 { 881 $tp = e107::getParser(); 882 $frm = e107::getForm(); 883 884 if(trim($curval) == "" && $struct['user_extended_struct_default'] != "") 885 { 886 $curval = $struct['user_extended_struct_default']; 887 } 888 889 $choices = explode(",",$struct['user_extended_struct_values']); 890 891 foreach($choices as $k => $v) 892 { 893 $choices[$k] = str_replace("[E_COMMA]", ",", $v); 894 } 895 896 $parms = explode("^,^",$struct['user_extended_struct_parms']); 897 $include = preg_replace("/\n/", " ", $tp->toHTML($parms[0])); 898 // $regex = $tp->toText(varset($parms[1])); 899 // $regexfail = $tp->toText(varset($parms[2])); 900 $fname = "ue[user_".$struct['user_extended_struct_name']."]"; 901 $required = vartrue($struct['user_extended_struct_required']) == 1 ? "required" : ""; 902 $fid = $frm->name2id($fname); 903 $placeholder = (!empty($parms[4])) ? "placeholder=\"".$tp->toAttribute($parms[4])."\"" : ""; 904 905 $class = !empty($opts['class']) ? $opts['class'] : "form-control tbox"; 906 $placeholder = !empty($opts['placeholder']) ? "placeholder=\"".$tp->toAttribute($opts['placeholder'])."\"" : $placeholder; 907 908 if(!empty($parms[5])) 909 { 910 $class .= " e-tip"; 911 $title = "title=\"".$tp->toAttribute($parms[5])."\""; 912 } 913 else 914 { 915 $title = ''; 916 } 917 918 if(strpos($include, 'class') === FALSE) 919 { 920 $include .= " class='".$class."' "; 921 } 922 923 $ret = null; 924 925 switch($struct['user_extended_struct_type']) 926 { 927 928 case EUF_COUNTRY: 929 return e107::getForm()->country($fname,$curval, $opts); 930 break; 931 932 933 case EUF_TEXT : //textbox 934 case EUF_INTEGER : //integer 935 $ret = "<input id='{$fid}' type='text' name='{$fname}' {$title} value='{$curval}' {$include} {$required} {$placeholder} />"; 936 937 return $ret; 938 break; 939 940 case EUF_RADIO : //radio 941 942 $ret = ''; 943 944 foreach($choices as $choice) 945 { 946 $choice = trim($choice); 947 $choice = $tp->toHTML($choice); 948 949 if(strpos($choice,"|")!==FALSE) 950 { 951 list($val,$label) = explode("|",$choice); 952 } 953 elseif(strpos($choice," => ")!==FALSE) // new in v2.x 954 { 955 list($val,$label) = explode(" => ",$choice); 956 } 957 else 958 { 959 $val = $choice; 960 $label = $choice; 961 } 962 963 $label = deftrue($label, $label); 964 965 if(deftrue('BOOTSTRAP')) 966 { 967 $ret .= $frm->radio($fname,$val,($curval == $val),array('label'=>$label, 'required'=> !empty($required))); 968 } 969 else 970 { 971 $chk = ($curval == $val)? " checked='checked' " : ""; 972 $ret .= "<input id='{$fid}' {$include} type='radio' name='{$fname}' value='{$val}' {$chk} {$required} /> {$label}"; 973 } 974 975 } 976 977 return $ret; 978 979 break; 980 981 case EUF_CHECKBOX : //checkboxes 982 983 if(!is_array($curval)) 984 { 985 $curval = e107::unserialize($curval); 986 } 987 988 return e107::getForm()->checkboxes($fname.'[]',$choices, $curval, array('useLabelValues'=>1)); 989 990 break; 991 992 case EUF_DROPDOWN : //dropdown 993 $ret = "<select {$include} id='{$fid}' name='{$fname}' {$required} {$title} >\n"; 994 $ret .= "<option value=''> </option>\n"; // ensures that the user chose it. 995 foreach($choices as $choice) 996 { 997 $choice = trim($choice); 998 $choice = deftrue($choice, $choice); 999 $sel = ($curval == $choice) ? " selected='selected' " : ""; 1000 $ret .= "<option value='{$choice}' {$sel}>{$choice}</option>\n"; 1001 } 1002 $ret .= "</select>\n"; 1003 return $ret; 1004 break; 1005 1006 case EUF_PREDEFINED : // predefined list, shown in dropdown 1007 $listRoot = trim($struct['user_extended_struct_values']); // Base list name 1008 $filename = e_CORE.'sql/extended_'.$listRoot.'.php'; 1009 if (!is_readable($filename)) return 'No file: '.$filename; 1010 require_once($filename); 1011 $className = 'extended_'.$listRoot; 1012 if (!class_exists($className)) return '?????'; 1013 /** @var extended_timezones $temp */ 1014 $temp = new $className(); 1015 if (!method_exists($className, 'getValue')) return '???-???'; 1016 $temp->pointerReset(); 1017 1018 $ret = "<select id='{$fid}' {$include} name='{$fname}' {$required} >\n"; 1019 $ret .= "<option value=''> </option>\n"; // ensures that the user chooses it. 1020 while (FALSE !== ($row = $temp->getValue(0, 'next'))) 1021 { 1022 $val = key($row); 1023 $choice = $temp->getValue($val, 'display'); 1024 $sel = ($curval == $val) ? " selected='selected' " : ''; 1025 $ret .= "<option value='{$val}' {$sel}>{$choice}</option>\n"; 1026 } 1027 $ret .= "</select>\n"; 1028 return $ret; 1029 1030 case EUF_DB_FIELD : //db_field 1031 1032 if(empty($choices)) 1033 { 1034 e107::getDebug()->log("DB Field Choices is empty"); 1035 } 1036 1037 $sql = e107::getDb('ue'); 1038 1039 $order = ($choices[3]) ? "ORDER BY " . $tp->toDB($choices[3], true) : ""; 1040 1041 if($sql->select($tp->toDB($choices[0], true), $tp->toDB($choices[1], true) . "," . $tp->toDB($choices[2], true), "1 $order")) 1042 { 1043 $choiceList = $sql->db_getList('ALL', false); 1044 $ret = "<select id='{$fid}' {$include} name='{$fname}' {$required} {$title}>\n"; 1045 $ret .= "<option value=''> </option>\n"; // ensures that the user chose it. 1046 1047 foreach($choiceList as $cArray) 1048 { 1049 $cID = trim($cArray[$choices[1]]); 1050 $cText = trim($cArray[$choices[2]]); 1051 $sel = ($curval == $cID) ? " selected='selected' " : ""; 1052 $ret .= "<option value='{$cID}' {$sel}>{$cText}</option>\n"; 1053 } 1054 1055 $ret .= "</select>\n"; 1056 1057 return $ret; 1058 } 1059 else 1060 { 1061 return "<span class='label label-danger'>Failed to load</span>"; 1062 } 1063 1064 break; 1065 1066 case EUF_TEXTAREA : //textarea 1067 return "<textarea id='{$fid}' {$include} name='{$fname}' {$required} {$title}>{$curval}</textarea>"; 1068 break; 1069 1070 case EUF_RICHTEXTAREA : // rich textarea (using WYSIWYG editor) 1071 return e107::getForm()->bbarea($fname, $curval); 1072 1073 case EUF_DATE : //date 1074 1075 if($curval == '0000-00-00') // Quick datepicker fix. 1076 { 1077 $curval = ''; 1078 } 1079 1080 if(THEME_LEGACY === true) 1081 { 1082 if(empty($opts['placeholder'])) 1083 { 1084 $opts['placeholder'] = 'yyyy-mm-dd'; 1085 } 1086 1087 return e107::getForm()->text($fname,$curval,10,$opts); 1088 } 1089 1090 $opts['format'] = 'yyyy-mm-dd'; 1091 $opts['return'] = 'string'; 1092 1093 if(!empty($required)) 1094 { 1095 $opts['required'] = true; 1096 } 1097 1098 return e107::getForm()->datepicker($fname,$curval,$opts); 1099 break; 1100 1101 case EUF_LANGUAGE : // language 1102 $lanlist = e107::getLanguage()->installed(); 1103 sort($lanlist); 1104 1105 $ret = "<select {$include} id='{$fid}' name='{$fname}' {$required} >\n"; 1106 $ret .= "<option value=''> </option>\n"; // ensures that the user chose it. 1107 foreach($lanlist as $choice) 1108 { 1109 $choice = trim($choice); 1110 $sel = ($curval == $choice || (!USER && $choice == e_LANGUAGE))? " selected='selected' " : ""; 1111 $ret .= "<option value='{$choice}' {$sel}>{$choice}</option>\n"; 1112 } 1113 $ret .= "</select>\n"; 1114 1115 break; 1116 1117 } 1118 1119 return $ret; 1120 } 1121 1122 1123 /** 1124 * BC Alias for getStructure() 1125 * @param string $orderby 1126 * @return mixed 1127 */ 1128 function user_extended_getStruct($orderby="user_extended_struct_order") 1129 { 1130 return $this->getStructure($orderby); 1131 } 1132 1133 1134 /** 1135 * Return all extended-field structure information 1136 * @param string $orderby 1137 * @return array|mixed 1138 */ 1139 function getStructure($orderby="user_extended_struct_order") 1140 { 1141 1142 $id = 'core/userextended/structure'; 1143 1144 if($ueStruct = e107::getRegistry($id)) 1145 { 1146 return $ueStruct; 1147 } 1148 1149 $tp = e107::getParser(); 1150 $sql_ue = e107::getDb('ue'); // new db; // Use our own db to avoid interference with other objects 1151 1152 $ret = array(); 1153 $parms = ""; 1154 1155 if($orderby != "") 1156 { 1157 $parms = "1 ORDER BY ".$tp -> toDB($orderby, true); 1158 } 1159 1160 if($sql_ue->select('user_extended_struct','*',$parms)) 1161 { 1162 while($row = $sql_ue->fetch()) 1163 { 1164 $ret['user_'.$row['user_extended_struct_name']] = $row; 1165 } 1166 } 1167 1168 e107::setRegistry($id, $ret); 1169 1170 return $ret; 1171 } 1172 1173 1174 /** 1175 * @param bool|false $no_cache 1176 * @return bool 1177 */ 1178 function parse_extended_xml($no_cache = false) 1179 { 1180 if($no_cache == FALSE && $this->extended_xml) 1181 { 1182 return $this->extended_xml; 1183 } 1184 1185 $xml = e107::getXml(); 1186 $data = $xml->loadXMLfile(e_CORE."xml/user_extended.xml", true); 1187 $ret['version'] = $data['@attributes']['version']; 1188 unset($info); 1189 foreach($data['item'] as $item) 1190 { 1191 if(is_array($item['include_text']) && !count($item['include_text'])) 1192 { 1193 $item['include_text'] = ''; 1194 } 1195 $info = array( 1196 "name" => $item['@attributes']['name'], 1197 "text" => "UE_LAN_".strtoupper($item['@attributes']['name']), 1198 "type" => $item['type'], 1199 "values" => $item['values'], 1200 "default" => $item['default'], 1201 "required" => $item['required'], 1202 "read" => $item['read'], 1203 "write" => $item['write'], 1204 "applicable" => $item['applicable'], 1205 "include_text" => $item['include_text'], 1206 "parms" => $item['include_text'], 1207 "regex" => $item['regex'] 1208 ); 1209 if(is_array($item['default']) && $item['default'] == '') 1210 { 1211 $info['default'] = 0; 1212 } 1213 if($item['regex']) 1214 { 1215 $info['parms'] .= $item['include_text']."^,^".$item['regex']."^,^LAN_UE_FAIL_".strtoupper($item['@attributes']['name']); 1216 } 1217 $ret[$item['@attributes']['name']] = $info; 1218 } 1219 $this->extended_xml = $ret; 1220 return $this->extended_xml; 1221 } 1222 1223 /** 1224 * Proxy Method to retrieve the value of an extended field 1225 * @param int $uid 1226 * @param string $field_name 1227 * @param mixed $ifnotset [optional] 1228 * @return mixed 1229 */ 1230 function get($uid, $field_name, $ifnotset=false) 1231 { 1232 return $this->user_extended_getvalue($uid, $field_name, $ifnotset); 1233 } 1234 1235 1236 1237 /** 1238 * Proxy method for setting the value of an extended field 1239 * (inserts or updates) 1240 * 1241 * @param integer $uid 1242 * @param string $field_name eg. location 1243 * @param string $newvalue eg. USA 1244 * @param string $fieldType [optional] default 'todb' | 1245 * @return boolean; 1246 */ 1247 function set($uid, $field_name, $newvalue, $fieldType = 'todb') 1248 { 1249 return $this->user_extended_setvalue($uid, $field_name, $newvalue, $fieldType); 1250 } 1251 1252 1253 /** 1254 * Set the value of an extended field 1255 * 1256 * $ue = new e107_user_extended; 1257 * $result = $ue->user_extended_setvalue(1, 'location', 'Pittsburgh'); 1258 * 1259 * @param int $uid 1260 * @param string $field_name 1261 * @param mixed $newvalue 1262 * @param string $fieldType 1263 * @return bool|int 1264 */ 1265 function user_extended_setvalue($uid, $field_name, $newvalue, $fieldType = 'todb') 1266 { 1267 $sql = e107::getDb(); 1268 $tp = e107::getParser(); 1269 1270 $uid = (int)$uid; 1271 switch($fieldType) 1272 { 1273 case 'int': 1274 $newvalue = (int)$newvalue; 1275 break; 1276 1277 case 'escape': 1278 $newvalue = "'".$sql->escape($newvalue)."'"; 1279 break; 1280 1281 default: 1282 $newvalue = "'".$tp->toDB($newvalue)."'"; 1283 break; 1284 } 1285 if(substr($field_name, 0, 5) != 'user_') 1286 { 1287 $field_name = 'user_'.$field_name; 1288 } 1289 $qry = " 1290 INSERT INTO `#user_extended` (user_extended_id, {$field_name}) 1291 VALUES ({$uid}, {$newvalue}) 1292 ON DUPLICATE KEY UPDATE {$field_name} = {$newvalue} 1293 "; 1294 return $sql->gen($qry); 1295 } 1296 1297 1298 /** 1299 * Retrieve the value of an extended field 1300 * 1301 * $ue = new e107_user_extended; 1302 * $value = $ue->user_extended_getvalue(2, 'location'); 1303 * 1304 * @param int $uid 1305 * @param string $field_name 1306 * @param bool $ifnotset 1307 * @return bool 1308 */ 1309 function user_extended_getvalue($uid, $field_name, $ifnotset=false) 1310 { 1311 $uid = intval($uid); 1312 if(substr($field_name, 0, 5) != 'user_') 1313 { 1314 $field_name = 'user_'.$field_name; 1315 } 1316 $uinfo = e107::user($uid); 1317 if (!isset($uinfo[$field_name])) return $ifnotset; 1318 return $uinfo[$field_name]; 1319 } 1320 1321 1322 /** 1323 * 1324 * Given a predefined list field, returns the display text corresponding to the passed value 1325 * 1326 * TODO: consider whether to cache the class object@param $table 1327 * @param $value 1328 * @return mixed|string 1329 */ 1330 function user_extended_display_text($table, $value) 1331 { 1332 $filename = e_CORE.'sql/extended_'.$table.'.php'; 1333 if (!is_readable($filename)) return 'No file: '.$filename; 1334 require_once($filename); 1335 $className = 'extended_'.$table; 1336 if (!class_exists($className)) return '?????'; 1337 /** @var extended_timezones $temp */ 1338 $temp = new $className(); 1339 if (!method_exists($className, 'getValue')) return '???-???'; 1340 return $temp->getValue($value); 1341 } 1342 1343 1344 /** 1345 * Render Extended User Field Data in a read-only fashion. 1346 * @param $value 1347 * @param $type 1348 * @return array|string 1349 */ 1350 public function renderValue($value, $type=null) 1351 { 1352 1353 1354 //TODO FIXME Add more types. 1355 1356 switch($type) 1357 { 1358 1359 case EUF_COUNTRY: 1360 if(!empty($value)) 1361 { 1362 return e107::getForm()->getCountry($value); 1363 } 1364 1365 return null; 1366 break; 1367 1368 1369 1370 case EUF_CHECKBOX: 1371 $value = e107::unserialize($value); 1372 1373 if(!empty($value)) 1374 { 1375 1376 sort($value); 1377 return implode('<br />',$value); 1378 1379 /* 1380 $text = '<ul>'; 1381 foreach($uVal as $v) 1382 { 1383 $text .= "<li>".$v."</li>"; 1384 1385 } 1386 $text .= "</ul>"; 1387 $ret_data = $text;*/ 1388 } 1389 break; 1390 1391 case EUF_DATE : //check for 0000-00-00 in date field 1392 if($value == '0000-00-00') { $value = ''; } 1393 return $value; 1394 break; 1395 1396 case EUF_RICHTEXTAREA: 1397 return e107::getParser()->toHTML($value, true); 1398 break; 1399 1400 default: 1401 return $value; 1402 // code to be executed if n is different from all labels; 1403 } 1404 1405 return null; 1406 } 1407 1408} 1409 1410 1411