Textpattern | PHP Cross Reference | Content Management Systems |
Description: Comments 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 * Comments panel. 27 * 28 * @package Admin\Discuss 29 */ 30 31 use Textpattern\Validator\ChoiceConstraint; 32 use Textpattern\Validator\Validator; 33 use Textpattern\Search\Filter; 34 35 if (!defined('txpinterface')) { 36 die('txpinterface is undefined.'); 37 } 38 39 if ($event == 'discuss') { 40 require_privs('discuss'); 41 42 if (!get_pref('use_comments', 1)) { 43 require_privs(); 44 } 45 46 $available_steps = array( 47 'discuss_save' => true, 48 'discuss_list' => false, 49 'discuss_edit' => false, 50 'discuss_multi_edit' => true, 51 'discuss_change_pageby' => true, 52 ); 53 54 if ($step && bouncer($step, $available_steps)) { 55 $step(); 56 } else { 57 discuss_list(); 58 } 59 } 60 61 //------------------------------------------------------------- 62 63 function discuss_save() 64 { 65 $varray = array_map('assert_string', gpsa(array('email', 'name', 'web', 'message', 'ip'))); 66 $varray = $varray + array_map('assert_int', gpsa(array('discussid', 'visible', 'parentid'))); 67 extract(doSlash($varray)); 68 69 $message = $varray['message'] = preg_replace('#<(/?txp:.+?)>#', '<$1>', $message); 70 71 $constraints = array( 72 'status' => new ChoiceConstraint($visible, array( 73 'choices' => array(SPAM, MODERATE, VISIBLE), 74 'message' => 'invalid_status', 75 )), 76 ); 77 78 callback_event_ref('discuss_ui', 'validate_save', 0, $varray, $constraints); 79 $validator = new Validator($constraints); 80 81 if ($validator->validate() && safe_update('txp_discuss', 82 "email = '$email', 83 name = '$name', 84 web = '$web', 85 message = '$message', 86 visible = $visible", 87 "discussid = $discussid" 88 )) { 89 update_comments_count($parentid); 90 update_lastmod('discuss_saved', compact('discussid', 'email', 'name', 'web', 'message', 'ip', 'visible', 'parentid')); 91 $message = gTxt('comment_updated', array('{id}' => $discussid)); 92 } else { 93 $message = array(gTxt('comment_save_failed'), E_ERROR); 94 } 95 96 discuss_list($message); 97 } 98 99 //------------------------------------------------------------- 100 101 function short_preview($message) 102 { 103 $message = strip_tags($message); 104 $offset = min(120, strlen($message)); 105 106 if (strpos($message, ' ', $offset) !== false) { 107 $maxpos = strpos($message, ' ', $offset); 108 $message = substr($message, 0, $maxpos).'…'; 109 } 110 111 return $message; 112 } 113 114 /** 115 * Outputs the main panel listing all comments. 116 * 117 * @param string|array $message The activity message 118 */ 119 120 function discuss_list($message = '') 121 { 122 global $event, $comment_list_pageby; 123 124 pagetop(gTxt('list_discussions'), $message); 125 126 extract(gpsa(array( 127 'sort', 128 'dir', 129 'page', 130 'crit', 131 'search_method', 132 ))); 133 134 if ($sort === '') { 135 $sort = get_pref('discuss_sort_column', 'date'); 136 } else { 137 if (!in_array($sort, array('id', 'ip', 'name', 'email', 'website', 'message', 'status', 'parent'))) { 138 $sort = 'date'; 139 } 140 141 set_pref('discuss_sort_column', $sort, 'discuss', 2, '', 0, PREF_PRIVATE); 142 } 143 144 if ($dir === '') { 145 $dir = get_pref('discuss_sort_dir', 'desc'); 146 } else { 147 $dir = ($dir == 'asc') ? "asc" : "desc"; 148 set_pref('discuss_sort_dir', $dir, 'discuss', 2, '', 0, PREF_PRIVATE); 149 } 150 151 switch ($sort) { 152 case 'id': 153 $sort_sql = "txp_discuss.discussid $dir"; 154 break; 155 case 'ip': 156 $sort_sql = "txp_discuss.ip $dir"; 157 break; 158 case 'name': 159 $sort_sql = "txp_discuss.name $dir"; 160 break; 161 case 'email': 162 $sort_sql = "txp_discuss.email $dir"; 163 break; 164 case 'website': 165 $sort_sql = "txp_discuss.web $dir"; 166 break; 167 case 'message': 168 $sort_sql = "txp_discuss.message $dir"; 169 break; 170 case 'status': 171 $sort_sql = "txp_discuss.visible $dir"; 172 break; 173 case 'parent': 174 $sort_sql = "txp_discuss.parentid $dir"; 175 break; 176 default: 177 $sort = 'date'; 178 $sort_sql = "txp_discuss.posted $dir"; 179 break; 180 } 181 182 if ($sort != 'date') { 183 $sort_sql .= ", txp_discuss.posted ASC"; 184 } 185 186 $switch_dir = ($dir == 'desc') ? 'asc' : 'desc'; 187 188 $search = new Filter($event, 189 array( 190 'id' => array( 191 'column' => 'txp_discuss.discussid', 192 'label' => gTxt('ID'), 193 'type' => 'integer', 194 ), 195 'parent' => array( 196 'column' => array('txp_discuss.parentid', 'textpattern.Title'), 197 'label' => gTxt('parent'), 198 ), 199 'name' => array( 200 'column' => 'txp_discuss.name', 201 'label' => gTxt('name'), 202 ), 203 'message' => array( 204 'column' => 'txp_discuss.message', 205 'label' => gTxt('message'), 206 ), 207 'email' => array( 208 'column' => 'txp_discuss.email', 209 'label' => gTxt('email'), 210 ), 211 'website' => array( 212 'column' => 'txp_discuss.web', 213 'label' => gTxt('website'), 214 ), 215 'ip' => array( 216 'column' => 'txp_discuss.ip', 217 'label' => gTxt('IP'), 218 ), 219 'visible' => array( 220 'column' => 'txp_discuss.visible', 221 'label' => gTxt('visible'), 222 'type' => 'numeric', 223 ), 224 ) 225 ); 226 227 $alias_yes = VISIBLE.', Yes'; 228 $alias_no = MODERATE.', No, Unmoderated, Pending'; 229 $alias_spam = SPAM.', Spam'; 230 231 $search->setAliases('visible', array( 232 VISIBLE => $alias_yes, 233 MODERATE => $alias_no, 234 SPAM => $alias_spam, 235 )); 236 237 list($criteria, $crit, $search_method) = $search->getFilter(array( 238 'id' => array('can_list' => true), 239 )); 240 241 $search_render_options = array( 242 'placeholder' => 'search_comments', 243 ); 244 245 $sql_from = 246 safe_pfx_j('txp_discuss')." 247 left join ".safe_pfx_j('textpattern')." on txp_discuss.parentid = textpattern.ID"; 248 249 $counts = getRows( 250 "SELECT txp_discuss.visible, COUNT(*) AS c 251 FROM ".safe_pfx_j('txp_discuss')." 252 LEFT JOIN ".safe_pfx_j('textpattern')." 253 ON txp_discuss.parentid = textpattern.ID 254 WHERE $criteria GROUP BY txp_discuss.visible" 255 ); 256 257 $count[SPAM] = $count[MODERATE] = $count[VISIBLE] = 0; 258 259 if ($counts) { 260 foreach ($counts as $c) { 261 $count[$c['visible']] = $c['c']; 262 } 263 } 264 265 // Grand total comment count. 266 $total = $count[SPAM] + $count[MODERATE] + $count[VISIBLE]; 267 268 echo n.'<div class="txp-layout">'. 269 n.tag( 270 hed(gTxt('list_discussions'), 1, array('class' => 'txp-heading')), 271 'div', array('class' => 'txp-layout-4col-alt') 272 ); 273 274 $searchBlock = 275 n.tag( 276 $search->renderForm('discuss_list', $search_render_options), 277 'div', array( 278 'class' => 'txp-layout-4col-3span', 279 'id' => $event.'_control', 280 ) 281 ); 282 283 $contentBlockStart = n.tag_start('div', array( 284 'class' => 'txp-layout-1col', 285 'id' => $event.'_container', 286 )); 287 288 if ($total < 1) { 289 if ($criteria != 1) { 290 echo $searchBlock. 291 $contentBlockStart. 292 graf( 293 span(null, array('class' => 'ui-icon ui-icon-info')).' '. 294 gTxt('no_results_found'), 295 array('class' => 'alert-block information') 296 ); 297 } else { 298 echo $contentBlockStart. 299 graf( 300 span(null, array('class' => 'ui-icon ui-icon-info')).' '. 301 gTxt('no_comments_recorded'), 302 array('class' => 'alert-block information') 303 ); 304 } 305 306 echo n.tag_end('div'). // End of .txp-layout-1col. 307 n.'</div>'; // End of .txp-layout. 308 309 return; 310 } 311 312 if (!cs('toggle_show_spam')) { 313 $total = $count[MODERATE] + $count[VISIBLE]; 314 $criteria = 'visible != '.intval(SPAM).' and '.$criteria; 315 } 316 317 $limit = max($comment_list_pageby, 15); 318 319 list($page, $offset, $numPages) = pager($total, $limit, $page); 320 321 echo $searchBlock.$contentBlockStart; 322 323 $rs = safe_query( 324 "SELECT 325 txp_discuss.discussid, 326 txp_discuss.parentid, 327 txp_discuss.name, 328 txp_discuss.email, 329 txp_discuss.web, 330 txp_discuss.ip, 331 txp_discuss.message, 332 txp_discuss.visible, 333 UNIX_TIMESTAMP(txp_discuss.posted) AS uPosted, 334 textpattern.ID AS thisid, 335 textpattern.Section AS section, 336 textpattern.url_title, 337 textpattern.Title AS title, 338 textpattern.Status, 339 UNIX_TIMESTAMP(textpattern.Posted) AS posted 340 FROM ".safe_pfx_j('txp_discuss')." 341 LEFT JOIN ".safe_pfx_j('textpattern')." ON txp_discuss.parentid = textpattern.ID 342 WHERE $criteria ORDER BY $sort_sql LIMIT $offset, $limit" 343 ); 344 345 if ($rs) { 346 echo n.tag( 347 cookie_box('show_spam'). 348 toggle_box('discuss_detail'), 349 'div', array('class' => 'txp-list-options')). 350 n.tag_start('form', array( 351 'class' => 'multi_edit_form', 352 'id' => 'discuss_form', 353 'name' => 'longform', 354 'method' => 'post', 355 'action' => 'index.php', 356 )). 357 n.tag_start('div', array('class' => 'txp-listtables')). 358 n.tag_start('table', array('class' => 'txp-list')). 359 n.tag_start('thead'). 360 tr( 361 hCell( 362 fInput('checkbox', 'select_all', 0, '', '', '', '', '', 'select_all'), 363 '', ' class="txp-list-col-multi-edit" scope="col" title="'.gTxt('toggle_all_selected').'"' 364 ). 365 column_head( 366 'ID', 'id', 'discuss', true, $switch_dir, $crit, $search_method, 367 (('id' == $sort) ? "$dir " : '').'txp-list-col-id' 368 ). 369 column_head( 370 'date', 'date', 'discuss', true, $switch_dir, $crit, $search_method, 371 (('date' == $sort) ? "$dir " : '').'txp-list-col-created date' 372 ). 373 column_head( 374 'name', 'name', 'discuss', true, $switch_dir, $crit, $search_method, 375 (('name' == $sort) ? "$dir " : '').'txp-list-col-name' 376 ). 377 column_head( 378 'message', 'message', 'discuss', true, $switch_dir, $crit, $search_method, 379 (('message' == $sort) ? "$dir " : 'txp-list-col-message') 380 ). 381 column_head( 382 'email', 'email', 'discuss', true, $switch_dir, $crit, $search_method, 383 (('email' == $sort) ? "$dir " : '').'txp-list-col-email discuss_detail' 384 ). 385 column_head( 386 'website', 'website', 'discuss', true, $switch_dir, $crit, $search_method, 387 (('website' == $sort) ? "$dir " : '').'txp-list-col-website discuss_detail' 388 ). 389 column_head( 390 'IP', 'ip', 'discuss', true, $switch_dir, $crit, $search_method, 391 (('ip' == $sort) ? "$dir " : '').'txp-list-col-ip discuss_detail' 392 ). 393 column_head( 394 'status', 'status', 'discuss', true, $switch_dir, $crit, $search_method, 395 (('status' == $sort) ? "$dir " : '').'txp-list-col-status' 396 ). 397 column_head( 398 'parent', 'parent', 'discuss', true, $switch_dir, $crit, $search_method, 399 (('parent' == $sort) ? "$dir " : '').'txp-list-col-parent' 400 ) 401 ). 402 n.tag_end('thead'); 403 404 include_once txpath.'/publish/taghandlers.php'; 405 406 echo n.tag_start('tbody'); 407 408 while ($a = nextRow($rs)) { 409 extract($a); 410 $parentid = assert_int($parentid); 411 412 $edit_url = array( 413 'event' => 'discuss', 414 'step' => 'discuss_edit', 415 'discussid' => $discussid, 416 'sort' => $sort, 417 'dir' => $dir, 418 'page' => $page, 419 'search_method' => $search_method, 420 'crit' => $crit, 421 ); 422 423 $dmessage = ($visible == SPAM) ? short_preview($message) : $message; 424 425 switch ($visible) { 426 case VISIBLE: 427 $comment_status = gTxt('visible'); 428 $row_class = 'visible'; 429 break; 430 case SPAM: 431 $comment_status = gTxt('spam'); 432 $row_class = 'spam'; 433 break; 434 case MODERATE: 435 $comment_status = gTxt('unmoderated'); 436 $row_class = 'moderate'; 437 break; 438 default: 439 break; 440 } 441 442 if (empty($thisid)) { 443 $parent = gTxt('article_deleted').' ('.$parentid.')'; 444 $view = ''; 445 } else { 446 $parent_title = empty($title) ? '<em>'.gTxt('untitled').'</em>' : escape_title($title); 447 448 $parent = href($parent_title, '?event=article'.a.'step=edit'.a.'ID='.$parentid); 449 450 $view = $comment_status; 451 452 if ($visible == VISIBLE and in_array($Status, array(4, 5))) { 453 $view = href($comment_status, permlinkurl($a).'#c'.$discussid, ' title="'.gTxt('view').'"'); 454 } 455 } 456 457 echo tr( 458 td( 459 fInput('checkbox', 'selected[]', $discussid), '', 'txp-list-col-multi-edit' 460 ). 461 hCell( 462 href($discussid, $edit_url, ' title="'.gTxt('edit').'"'), '', ' class="txp-list-col-id" scope="row"' 463 ). 464 td( 465 gTime($uPosted), '', 'txp-list-col-created date' 466 ). 467 td( 468 txpspecialchars(soft_wrap($name, 15)), '', 'txp-list-col-name' 469 ). 470 td( 471 short_preview($dmessage), '', 'txp-list-col-message' 472 ). 473 td( 474 txpspecialchars(soft_wrap($email, 15)), '', 'txp-list-col-email discuss_detail' 475 ). 476 td( 477 txpspecialchars(soft_wrap($web, 15)), '', 'txp-list-col-website discuss_detail' 478 ). 479 td( 480 href(txpspecialchars($ip), 'https://whois.domaintools.com/'.rawurlencode($ip), array( 481 'rel' => 'external', 482 'target' => '_blank', 483 )), '', 'txp-list-col-ip discuss_detail' 484 ). 485 td( 486 $view, '', 'txp-list-col-status' 487 ). 488 td( 489 $parent, '', 'txp-list-col-parent' 490 ), ' class="'.$row_class.'"' 491 ); 492 } 493 494 if (empty($message)) { 495 echo n.tr(tda(gTxt('just_spam_results_found'), ' colspan="10"')); 496 } 497 498 echo n.tag_end('tbody'). 499 n.tag_end('table'). 500 n.tag_end('div'). // End of .txp-listtables. 501 discuss_multiedit_form($page, $sort, $dir, $crit, $search_method). 502 tInput(). 503 n.tag_end('form'). 504 n.tag_start('div', array( 505 'class' => 'txp-navigation', 506 'id' => $event.'_navigation', 507 )). 508 pageby_form('discuss', $comment_list_pageby). 509 nav_form('discuss', $page, $numPages, $sort, $dir, $crit, $search_method, $total, $limit). 510 n.tag_end('div'); 511 } 512 513 echo n.tag_end('div'). // End of .txp-layout-1col. 514 n.'</div>'; // End of .txp-layout. 515 } 516 517 /** 518 * Renders and outputs the comment editor panel. 519 */ 520 521 function discuss_edit() 522 { 523 global $event; 524 525 pagetop(gTxt('edit_comment')); 526 527 extract(gpsa(array( 528 'discussid', 529 'sort', 530 'dir', 531 'page', 532 'crit', 533 'search_method', 534 ))); 535 536 $discussid = assert_int($discussid); 537 538 $rs = safe_row("*, UNIX_TIMESTAMP(posted) AS uPosted", 'txp_discuss', "discussid = $discussid"); 539 540 if ($rs) { 541 extract($rs); 542 543 $message = txpspecialchars($message); 544 545 $status_list = selectInput( 546 'visible', 547 array( 548 VISIBLE => gTxt('visible'), 549 SPAM => gTxt('spam'), 550 MODERATE => gTxt('unmoderated'), 551 ), 552 $visible, 553 false, 554 '', 555 'status'); 556 557 echo form( 558 hed(gTxt('edit_comment'), 2). 559 inputLabel( 560 'status', 561 $status_list, 562 'status', '', array('class' => 'txp-form-field edit-comment-status') 563 ). 564 inputLabel( 565 'name', 566 fInput('text', 'name', $name, '', '', '', INPUT_REGULAR, '', 'name'), 567 'name', '', array('class' => 'txp-form-field edit-comment-name') 568 ). 569 inputLabel( 570 'IP', 571 href(txpspecialchars($ip), 'https://whois.domaintools.com/'.rawurlencode($ip), array( 572 'rel' => 'external', 573 'target' => '_blank', 574 )), 575 '', '', array('class' => 'txp-form-field edit-comment-ip') 576 ). 577 inputLabel( 578 'email', 579 fInput('email', 'email', $email, '', '', '', INPUT_REGULAR, '', 'email'), 580 'email', '', array('class' => 'txp-form-field edit-comment-email') 581 ). 582 inputLabel( 583 'website', 584 fInput('text', 'web', $web, '', '', '', INPUT_REGULAR, '', 'website'), 585 'website', '', array('class' => 'txp-form-field edit-comment-website') 586 ). 587 inputLabel( 588 'date', 589 safe_strftime('%d %b %Y %X', 590 $uPosted), 591 '', '', array('class' => 'txp-form-field edit-comment-date') 592 ). 593 inputLabel( 594 'commentmessage', 595 '<textarea id="commentmessage" name="message" cols="'.INPUT_LARGE.'" rows="'.TEXTAREA_HEIGHT_MEDIUM.'">'.$message.'</textarea>', 596 'message', '', array('class' => 'txp-form-field txp-form-field-textarea edit-comment-message') 597 ). 598 graf( 599 sLink('discuss', '', gTxt('cancel'), 'txp-button'). 600 fInput('submit', 'step', gTxt('save'), 'publish'), 601 array('class' => 'txp-edit-actions') 602 ). 603 hInput('sort', $sort). 604 hInput('dir', $dir). 605 hInput('page', $page). 606 hInput('crit', $crit). 607 hInput('search_method', $search_method). 608 hInput('discussid', $discussid). 609 hInput('parentid', $parentid). 610 hInput('ip', $ip). 611 eInput('discuss'). 612 sInput('discuss_save'), 613 '', '', 'post', 'txp-edit', '', 'discuss_edit_form'); 614 } else { 615 echo graf( 616 span(null, array('class' => 'ui-icon ui-icon-info')).' '. 617 gTxt('comment_not_found'), 618 array('class' => 'alert-block information') 619 ); 620 } 621 } 622 623 // ------------------------------------------------------------- 624 625 function discuss_change_pageby() 626 { 627 event_change_pageby('comment'); 628 discuss_list(); 629 } 630 631 // ------------------------------------------------------------- 632 633 function discuss_multiedit_form($page, $sort, $dir, $crit, $search_method) 634 { 635 $methods = array( 636 'visible' => gTxt('show'), 637 'unmoderated' => gTxt('hide_unmoderated'), 638 'spam' => gTxt('hide_spam'), 639 'delete' => gTxt('delete'), 640 ); 641 642 return multi_edit($methods, 'discuss', 'discuss_multi_edit', $page, $sort, $dir, $crit, $search_method); 643 } 644 645 // ------------------------------------------------------------- 646 647 function discuss_multi_edit() 648 { 649 // FIXME: this method needs some refactoring. 650 $selected = ps('selected'); 651 $method = ps('edit_method'); 652 $done = array(); 653 654 if ($selected and is_array($selected)) { 655 // Get all articles for which we have to update the count. 656 foreach ($selected as $id) { 657 $ids[] = assert_int($id); 658 } 659 $parentids = safe_column("DISTINCT parentid", 'txp_discuss', "discussid IN (".implode(',', $ids).")"); 660 661 $rs = safe_rows_start("*", 'txp_discuss', "discussid IN (".implode(',', $ids).")"); 662 663 while ($row = nextRow($rs)) { 664 extract($row); 665 $id = assert_int($discussid); 666 $parentids[] = $parentid; 667 668 if ($method == 'delete') { 669 // Delete and, if successful, update comment count. 670 if (safe_delete('txp_discuss', "discussid = $id")) { 671 $done[] = $id; 672 } 673 674 callback_event('discuss_deleted', '', 0, $done); 675 } elseif ($method == 'spam') { 676 if (safe_update('txp_discuss', 677 "visible = ".SPAM, 678 "discussid = $id" 679 )) { 680 $done[] = $id; 681 } 682 } elseif ($method == 'unmoderated') { 683 if (safe_update('txp_discuss', 684 "visible = ".MODERATE, 685 "discussid = $id" 686 )) { 687 $done[] = $id; 688 } 689 } elseif ($method == 'visible') { 690 if (safe_update('txp_discuss', 691 "visible = ".VISIBLE, 692 "discussid = $id" 693 )) { 694 $done[] = $id; 695 } 696 } 697 } 698 699 $doneStr = join(', ', $done); 700 701 if ($doneStr) { 702 // Might as well clean up all comment counts while we're here. 703 clean_comment_counts($parentids); 704 705 $messages = array( 706 'delete' => gTxt('comments_deleted', array('{list}' => $doneStr)), 707 'spam' => gTxt('comments_marked_spam', array('{list}' => $doneStr)), 708 'unmoderated' => gTxt('comments_marked_unmoderated', array('{list}' => $doneStr)), 709 'visible' => gTxt('comments_marked_visible', array('{list}' => $doneStr)), 710 ); 711 712 update_lastmod('discuss_updated', $done); 713 714 return discuss_list($messages[$method]); 715 } 716 } 717 718 return discuss_list(); 719 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title