Textpattern | PHP Cross Reference | Content Management Systems |
Description: Users panel.
1 <?php 2 3 /* 4 * Textpattern Content Management System 5 * http://textpattern.com 6 * 7 * Copyright (C) 2005 Dean Allen 8 * Copyright (C) 2016 The Textpattern Development Team 9 * 10 * This file is part of Textpattern. 11 * 12 * Textpattern is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU General Public License 14 * as published by the Free Software Foundation, version 2. 15 * 16 * Textpattern is distributed in the hope that it will be useful, 17 * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 * GNU General Public License for more details. 20 * 21 * You should have received a copy of the GNU General Public License 22 * along with Textpattern. If not, see <http://www.gnu.org/licenses/>. 23 */ 24 25 /** 26 * Users panel. 27 * 28 * @package Admin\Admin 29 */ 30 31 use Textpattern\Search\Filter; 32 33 if (!defined('txpinterface')) { 34 die('txpinterface is undefined.'); 35 } 36 37 $levels = get_groups(); 38 39 if ($event == 'admin') { 40 require_privs('admin'); 41 42 include_once txpath.'/lib/txplib_admin.php'; 43 44 $available_steps = array( 45 'admin_multi_edit' => true, 46 'admin_change_pageby' => true, 47 'author_list' => false, 48 'author_edit' => false, 49 'author_save' => true, 50 'author_save_new' => true, 51 'change_email' => true, 52 'change_email_form' => false, 53 'change_pass' => true, 54 'new_pass_form' => false, 55 ); 56 57 if ($step && bouncer($step, $available_steps)) { 58 $step(); 59 } else { 60 author_list(); 61 } 62 } 63 64 /** 65 * Changes an email address. 66 */ 67 68 function change_email() 69 { 70 global $txp_user; 71 72 $new_email = ps('new_email'); 73 74 if (!is_valid_email($new_email)) { 75 change_email_form(array(gTxt('email_required'), E_ERROR)); 76 77 return; 78 } 79 80 $rs = update_user($txp_user, $new_email); 81 82 if ($rs) { 83 author_list(gTxt('email_changed', array('{email}' => $new_email))); 84 85 return; 86 } 87 88 change_email_form(array(gTxt('author_save_failed'), E_ERROR)); 89 } 90 91 /** 92 * Updates a user. 93 */ 94 95 function author_save() 96 { 97 global $txp_user; 98 99 require_privs('admin.edit'); 100 101 extract(psa(array( 102 'privs', 103 'name', 104 'RealName', 105 'email', 106 ))); 107 108 $privs = assert_int($privs); 109 110 if (!is_valid_email($email)) { 111 author_edit(array(gTxt('email_required'), E_ERROR)); 112 113 return; 114 } 115 116 $rs = update_user($name, $email, $RealName); 117 118 if ($rs && ($txp_user === $name || change_user_group($name, $privs))) { 119 author_list(gTxt('author_updated', array('{name}' => $RealName))); 120 121 return; 122 } 123 124 author_edit(array(gTxt('author_save_failed'), E_ERROR)); 125 } 126 127 /** 128 * Changes current user's password. 129 */ 130 131 function change_pass() 132 { 133 global $txp_user; 134 135 extract(psa(array('current_pass', 'new_pass'))); 136 137 if (empty($new_pass)) { 138 new_pass_form(array(gTxt('password_required'), E_ERROR)); 139 140 return; 141 } 142 143 if (txp_validate($txp_user, $current_pass)) { 144 $rs = change_user_password($txp_user, $new_pass); 145 146 if ($rs) { 147 $message = gTxt('password_changed'); 148 author_list($message); 149 } 150 } else { 151 new_pass_form(array(gTxt('password_invalid'), E_ERROR)); 152 } 153 } 154 155 /** 156 * Creates a new user. 157 */ 158 159 function author_save_new() 160 { 161 require_privs('admin.edit'); 162 163 extract(psa(array( 164 'privs', 165 'name', 166 'email', 167 'RealName', 168 ))); 169 170 $privs = assert_int($privs); 171 172 if (is_valid_username($name) && is_valid_email($email)) { 173 if (user_exists($name)) { 174 author_edit(array(gTxt('author_already_exists', array('{name}' => $name)), E_ERROR)); 175 176 return; 177 } 178 179 $password = Txp::get('\Textpattern\Password\Random')->generate(PASSWORD_LENGTH); 180 181 $rs = create_user($name, $email, $password, $RealName, $privs); 182 183 if ($rs) { 184 $message = send_account_activation($name); 185 186 author_list($message); 187 188 return; 189 } 190 } 191 192 author_edit(array(gTxt('error_adding_new_author'), E_ERROR)); 193 } 194 195 /** 196 * Lists user groups as a <select> input. 197 * 198 * @param int $priv Selected option 199 * @return string HTML 200 */ 201 202 function privs($priv = '') 203 { 204 global $levels; 205 206 return selectInput('privs', $levels, $priv, '', '', 'privileges'); 207 } 208 209 /** 210 * Translates a numeric ID to a human-readable user group. 211 * 212 * @param int $priv The group 213 * @return string 214 */ 215 216 function get_priv_level($priv) 217 { 218 global $levels; 219 220 return $levels[$priv]; 221 } 222 223 /** 224 * Password changing form. 225 * 226 * @param string|array $message The activity message 227 */ 228 229 function new_pass_form($message = '') 230 { 231 pagetop(gTxt('tab_site_admin'), $message); 232 233 echo form( 234 hed(gTxt('change_password'), 2). 235 inputLabel( 236 'current_pass', 237 fInput('password', 'current_pass', '', '', '', '', INPUT_REGULAR, '', 'current_pass'), 238 'current_password', '', array('class' => 'txp-form-field edit-admin-current-password') 239 ). 240 inputLabel( 241 'new_pass', 242 fInput('password', 'new_pass', '', 'txp-maskable txp-strength-hint', '', '', INPUT_REGULAR, '', 'new_pass'). 243 n.tag(null, 'div', array('class' => 'strength-meter')). 244 n.tag( 245 checkbox('unmask', 1, false, 0, 'show_password'). 246 n.tag(gTxt('show_password'), 'label', array('for' => 'show_password')), 247 'div', array('class' => 'edit-admin-show-password')), 248 'new_password', '', array('class' => 'txp-form-field edit-admin-new-password') 249 ). 250 graf( 251 sLink('admin', '', gTxt('cancel'), 'txp-button'). 252 fInput('submit', 'change_pass', gTxt('submit'), 'publish'), 253 array('class' => 'txp-edit-actions') 254 ). 255 eInput('admin'). 256 sInput('change_pass'), 257 '', '', 'post', 'txp-edit', '', 'change_password'); 258 } 259 260 /** 261 * Email changing form. 262 * 263 * @param string|array $message The activity message 264 */ 265 266 function change_email_form($message = '') 267 { 268 global $txp_user; 269 270 pagetop(gTxt('tab_site_admin'), $message); 271 272 $email = fetch('email', 'txp_users', 'name', $txp_user); 273 274 echo form( 275 hed(gTxt('change_email_address'), 2). 276 inputLabel( 277 'new_email', 278 fInput('text', 'new_email', $email, '', '', '', INPUT_REGULAR, '', 'new_email'), 279 'new_email', '', array('class' => 'txp-form-field edit-admin-new-email') 280 ). 281 graf( 282 sLink('admin', '', gTxt('cancel'), 'txp-button'). 283 fInput('submit', 'change_email', gTxt('submit'), 'publish'), 284 array('class' => 'txp-edit-actions') 285 ). 286 eInput('admin'). 287 sInput('change_email'), 288 '', '', 'post', 'txp-edit', '', 'change_email'); 289 } 290 291 /** 292 * The main panel listing all authors. 293 * 294 * @param string|array $message The activity message 295 */ 296 297 function author_list($message = '') 298 { 299 global $event, $txp_user, $author_list_pageby, $levels; 300 301 pagetop(gTxt('tab_site_admin'), $message); 302 303 if (is_disabled('mail')) { 304 echo graf( 305 span(null, array('class' => 'ui-icon ui-icon-alert')).' '. 306 gTxt('warn_mail_unavailable'), 307 array('class' => 'alert-block warning') 308 ); 309 } 310 311 $buttons = array(); 312 313 // Change password button. 314 $buttons[] = sLink('admin', 'new_pass_form', gTxt('change_password'), 'txp-button'); 315 316 if (!has_privs('admin.edit')) { 317 // Change email address button. 318 $buttons[] = sLink('admin', 'change_email_form', gTxt('change_email_address'), 'txp-button'); 319 } else { 320 // New author button. 321 $buttons[] = sLink('admin', 'author_edit', gTxt('add_new_author'), 'txp-button'); 322 } 323 324 // User list. 325 if (has_privs('admin.list')) { 326 extract(gpsa(array( 327 'page', 328 'sort', 329 'dir', 330 'crit', 331 'search_method', 332 ))); 333 334 if ($sort === '') { 335 $sort = get_pref('admin_sort_column', 'name'); 336 } else { 337 if (!in_array($sort, array('name', 'RealName', 'email', 'privs', 'last_login'))) { 338 $sort = 'name'; 339 } 340 341 set_pref('admin_sort_column', $sort, 'admin', 2, '', 0, PREF_PRIVATE); 342 } 343 344 if ($dir === '') { 345 $dir = get_pref('admin_sort_dir', 'asc'); 346 } else { 347 $dir = ($dir == 'desc') ? "desc" : "asc"; 348 set_pref('admin_sort_dir', $dir, 'admin', 2, '', 0, PREF_PRIVATE); 349 } 350 351 $sort_sql = $sort.' '.$dir; 352 353 $switch_dir = ($dir == 'desc') ? 'asc' : 'desc'; 354 355 $search = new Filter($event, 356 array( 357 'login' => array( 358 'column' => 'txp_users.name', 359 'label' => gTxt('login_name'), 360 ), 361 'RealName' => array( 362 'column' => 'txp_users.RealName', 363 'label' => gTxt('real_name'), 364 ), 365 'email' => array( 366 'column' => 'txp_users.email', 367 'label' => gTxt('email'), 368 ), 369 'privs' => array( 370 'column' => array('txp_users.privs'), 371 'label' => gTxt('privileges'), 372 'type' => 'boolean', 373 ), 374 ) 375 ); 376 377 $search->setAliases('privs', $levels); 378 379 list($criteria, $crit, $search_method) = $search->getFilter(); 380 381 $search_render_options = array( 382 'placeholder' => 'search_users', 383 ); 384 385 $total = getCount('txp_users', $criteria); 386 387 echo n.'<div class="txp-layout">'. 388 n.tag( 389 hed(gTxt('tab_site_admin'), 1, array('class' => 'txp-heading')), 390 'div', array('class' => 'txp-layout-4col-alt') 391 ); 392 393 $searchBlock = 394 n.tag( 395 $search->renderForm('author_list', $search_render_options), 396 'div', array( 397 'class' => 'txp-layout-4col-3span', 398 'id' => 'users_control', 399 ) 400 ); 401 402 $createBlock = array(); 403 404 $createBlock[] = n.tag(implode(n, $buttons), 'div', array('class' => 'txp-control-panel')); 405 406 $contentBlockStart = n.tag_start('div', array( 407 'class' => 'txp-layout-1col', 408 'id' => 'users_container', 409 )); 410 411 $createBlock = implode(n, $createBlock); 412 413 if ($total < 1) { 414 if ($criteria != 1) { 415 echo $searchBlock. 416 $contentBlockStart. 417 $createBlock. 418 graf( 419 span(null, array('class' => 'ui-icon ui-icon-info')).' '. 420 gTxt('no_results_found'), 421 array('class' => 'alert-block information') 422 ). 423 n.tag_end('div'). // End of .txp-layout-1col. 424 n.'</div>'; // End of .txp-layout. 425 } 426 427 return; 428 } 429 430 $limit = max($author_list_pageby, 15); 431 432 list($page, $offset, $numPages) = pager($total, $limit, $page); 433 434 $use_multi_edit = (has_privs('admin.edit') && ($total > 1 or safe_count('txp_users', "1 = 1") > 1)); 435 436 echo $searchBlock.$contentBlockStart.$createBlock; 437 438 $rs = safe_rows_start( 439 "*, UNIX_TIMESTAMP(last_access) AS last_login", 440 'txp_users', 441 "$criteria ORDER BY $sort_sql LIMIT $offset, $limit" 442 ); 443 444 if ($rs) { 445 echo 446 n.tag_start('form', array( 447 'class' => 'multi_edit_form', 448 'id' => 'users_form', 449 'name' => 'longform', 450 'method' => 'post', 451 'action' => 'index.php', 452 )). 453 n.tag_start('div', array('class' => 'txp-listtables')). 454 n.tag_start('table', array('class' => 'txp-list')). 455 n.tag_start('thead'). 456 tr( 457 ( 458 ($use_multi_edit) 459 ? hCell( 460 fInput('checkbox', 'select_all', 0, '', '', '', '', '', 'select_all'), 461 '', ' class="txp-list-col-multi-edit" scope="col" title="'.gTxt('toggle_all_selected').'"' 462 ) 463 : hCell('', '', ' class="txp-list-col-multi-edit" scope="col"') 464 ). 465 column_head( 466 'login_name', 'name', 'admin', true, $switch_dir, '', '', 467 (('name' == $sort) ? "$dir " : '').'txp-list-col-login-name name' 468 ). 469 column_head( 470 'real_name', 'RealName', 'admin', true, $switch_dir, '', '', 471 (('RealName' == $sort) ? "$dir " : '').'txp-list-col-real-name name' 472 ). 473 column_head( 474 'email', 'email', 'admin', true, $switch_dir, '', '', 475 (('email' == $sort) ? "$dir " : '').'txp-list-col-email' 476 ). 477 column_head( 478 'privileges', 'privs', 'admin', true, $switch_dir, '', '', 479 (('privs' == $sort) ? "$dir " : '').'txp-list-col-privs' 480 ). 481 column_head( 482 'last_login', 'last_login', 'admin', true, $switch_dir, '', '', 483 (('last_login' == $sort) ? "$dir " : '').'txp-list-col-last-login date' 484 ) 485 ). 486 n.tag_end('thead'). 487 n.tag_start('tbody'); 488 489 while ($a = nextRow($rs)) { 490 extract(doSpecial($a)); 491 492 echo tr( 493 td( 494 ((has_privs('admin.edit') and $txp_user != $a['name']) ? fInput('checkbox', 'selected[]', $a['name'], 'checkbox') : ''), '', 'txp-list-col-multi-edit' 495 ). 496 hCell( 497 ((has_privs('admin.edit')) ? eLink('admin', 'author_edit', 'user_id', $user_id, $name) : $name), '', ' class="txp-list-col-login-name name" scope="row"' 498 ). 499 td( 500 $RealName, '', 'txp-list-col-real-name name' 501 ). 502 td( 503 href($email, 'mailto:'.$email), '', 'txp-list-col-email' 504 ). 505 td( 506 get_priv_level($privs), '', 'txp-list-col-privs' 507 ). 508 td( 509 ($last_login ? safe_strftime('%b %Y', $last_login) : ''), '', 'txp-list-col-last-login date' 510 ) 511 ); 512 } 513 514 echo 515 n.tag_end('tbody'). 516 n.tag_end('table'). 517 n.tag_end('div'). // End of .txp-listtables. 518 ( 519 ($use_multi_edit) 520 ? author_multiedit_form($page, $sort, $dir, $crit, $search_method) 521 : '' 522 ). 523 tInput(). 524 n.tag_end('form'). 525 n.tag_start('div', array( 526 'class' => 'txp-navigation', 527 'id' => 'users_navigation', 528 )). 529 pageby_form('admin', $author_list_pageby). 530 nav_form('admin', $page, $numPages, $sort, $dir, $crit, $search_method). 531 n.tag_end('div'); 532 } 533 534 echo n.tag_end('div'); // End of .txp-layout-1col. 535 } else { 536 echo 537 n.tag_start('div', array( 538 'class' => 'txp-layout-1col', 539 'id' => 'users_container', 540 )). 541 n.tag(implode(n, $buttons), 'div', array('class' => 'txp-control-panel')). 542 n.tag_end('div'); // End of .txp-layout-1col. 543 } 544 545 echo n.'</div>'; // End of .txp-layout. 546 } 547 548 /** 549 * Renders and outputs the user editor panel. 550 * 551 * Accessing requires 'admin.edit' privileges. 552 * 553 * @param string|array $message The activity message 554 */ 555 556 function author_edit($message = '') 557 { 558 global $step, $txp_user; 559 560 require_privs('admin.edit'); 561 562 pagetop(gTxt('tab_site_admin'), $message); 563 564 $vars = array('user_id', 'name', 'RealName', 'email', 'privs'); 565 $rs = array(); 566 $out = array(); 567 568 extract(gpsa($vars)); 569 570 $is_edit = ($user_id && $step == 'author_edit'); 571 572 if ($is_edit) { 573 $user_id = assert_int($user_id); 574 $rs = safe_row("*", 'txp_users', "user_id = $user_id"); 575 extract($rs); 576 } 577 578 if ($is_edit) { 579 $out[] = hed(gTxt('edit_author'), 2); 580 } else { 581 $out[] = hed(gTxt('add_new_author'), 2); 582 } 583 584 if ($is_edit) { 585 $out[] = inputLabel( 586 'login_name', 587 strong(txpspecialchars($name)), 588 '', '', array('class' => 'txp-form-field edit-admin-login-name') 589 ); 590 } else { 591 $out[] = inputLabel( 592 'login_name', 593 fInput('text', 'name', $name, '', '', '', INPUT_REGULAR, '', 'login_name'), 594 'login_name', 'add_new_author', array('class' => 'txp-form-field edit-admin-login-name') 595 ); 596 } 597 598 $out[] = inputLabel( 599 'real_name', 600 fInput('text', 'RealName', $RealName, '', '', '', INPUT_REGULAR, '', 'real_name'), 601 'real_name', '', array('class' => 'txp-form-field edit-admin-name') 602 ). 603 inputLabel( 604 'login_email', 605 fInput('email', 'email', $email, '', '', '', INPUT_REGULAR, '', 'login_email'), 606 'email', '', array('class' => 'txp-form-field edit-admin-email') 607 ); 608 609 if ($txp_user != $name) { 610 $out[] = inputLabel( 611 'privileges', 612 privs($privs), 613 'privileges', 'about_privileges', array('class' => 'txp-form-field edit-admin-privileges') 614 ); 615 } else { 616 $out[] = inputLabel( 617 'privileges', 618 strong(get_priv_level($privs)), 619 '', '', array('class' => 'txp-form-field edit-admin-privileges') 620 ). 621 hInput('privs', $privs); 622 } 623 624 $out[] = pluggable_ui('author_ui', 'extend_detail_form', '', $rs). 625 graf( 626 sLink('admin', '', gTxt('cancel'), 'txp-button'). 627 fInput('submit', '', gTxt('save'), 'publish'), 628 array('class' => 'txp-edit-actions') 629 ). 630 eInput('admin'); 631 632 if ($user_id) { 633 $out[] = hInput('user_id', $user_id). 634 hInput('name', $name). 635 sInput('author_save'); 636 } else { 637 $out[] = sInput('author_save_new'); 638 } 639 640 echo form(join('', $out), '', '', 'post', 'txp-edit', '', 'user_edit'); 641 } 642 643 /** 644 * Updates pageby value. 645 */ 646 647 function admin_change_pageby() 648 { 649 event_change_pageby('author'); 650 author_list(); 651 } 652 653 /** 654 * Renders multi-edit form. 655 * 656 * @param int $page The page 657 * @param string $sort The sorting value 658 * @param string $dir The sorting direction 659 * @param string $crit The search string 660 * @param string $search_method The search method 661 * @return string HTML 662 */ 663 664 function author_multiedit_form($page, $sort, $dir, $crit, $search_method) 665 { 666 $privileges = privs(); 667 $users = safe_column("name", 'txp_users', "1 = 1"); 668 669 $methods = array( 670 'changeprivilege' => array('label' => gTxt('changeprivilege'), 'html' => $privileges), 671 'resetpassword' => gTxt('resetpassword'), 672 'resendactivation' => gTxt('resend_activation'), 673 ); 674 675 if (count($users) > 1) { 676 $methods['delete'] = array( 677 'label' => gTxt('delete'), 678 'html' => tag(gTxt('assign_assets_to'), 'label', array('for' => 'assign_assets')). 679 selectInput('assign_assets', $users, '', true, '', 'assign_assets'), 680 ); 681 } 682 683 return multi_edit($methods, 'admin', 'admin_multi_edit', $page, $sort, $dir, $crit, $search_method); 684 } 685 686 /** 687 * Processes multi-edit actions. 688 * 689 * Accessing requires 'admin.edit' privileges. 690 */ 691 692 function admin_multi_edit() 693 { 694 global $txp_user; 695 696 require_privs('admin.edit'); 697 698 $selected = ps('selected'); 699 $method = ps('edit_method'); 700 $changed = array(); 701 $msg = ''; 702 703 if (!$selected or !is_array($selected)) { 704 return author_list(); 705 } 706 707 $clause = ''; 708 709 if ($method === 'resetpassword') { 710 $clause = " AND last_access IS NOT NULL"; 711 } elseif ($method === 'resendactivation') { 712 $clause = " AND last_access IS NULL"; 713 } 714 715 $names = safe_column( 716 "name", 717 'txp_users', 718 "name IN (".join(',', quote_list($selected)).") AND name != '".doSlash($txp_user)."'".$clause 719 ); 720 721 if (!$names) { 722 return author_list(); 723 } 724 725 switch ($method) { 726 case 'delete': 727 728 $assign_assets = ps('assign_assets'); 729 730 if (!$assign_assets) { 731 $msg = array('must_reassign_assets', E_ERROR); 732 } elseif (in_array($assign_assets, $names)) { 733 $msg = array('cannot_assign_assets_to_deletee', E_ERROR); 734 } elseif (remove_user($names, $assign_assets)) { 735 $changed = $names; 736 callback_event('authors_deleted', '', 0, $changed); 737 $msg = 'author_deleted'; 738 } 739 740 break; 741 742 case 'changeprivilege': 743 744 if (change_user_group($names, ps('privs'))) { 745 $changed = $names; 746 $msg = 'author_updated'; 747 } 748 749 break; 750 751 case 'resetpassword': 752 753 foreach ($names as $name) { 754 send_reset_confirmation_request($name); 755 $changed[] = $name; 756 } 757 758 $msg = 'password_reset_confirmation_request_sent'; 759 break; 760 761 case 'resendactivation': 762 763 foreach ($names as $name) { 764 send_account_activation($name); 765 $changed[] = $name; 766 } 767 768 $msg = 'resend_activation_request_sent'; 769 break; 770 } 771 772 if ($changed) { 773 return author_list(gTxt($msg, array('{name}' => txpspecialchars(join(', ', $changed))))); 774 } 775 776 author_list($msg); 777 } 778 779 /** 780 * Legacy panel. 781 * 782 * @param string|array $message 783 * @deprecated in 4.2.0 784 */ 785 786 function admin($message = '') 787 { 788 author_list($message); 789 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title