Textpattern | PHP Cross Reference | Content Management Systems |
Description: Links 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 * Links panel. 27 * 28 * @package Admin\Link 29 */ 30 31 use Textpattern\Validator\CategoryConstraint; 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 == 'link') { 40 require_privs('link'); 41 42 global $vars; 43 $vars = array('category', 'url', 'linkname', 'linksort', 'description', 'id'); 44 45 global $all_link_cats, $all_link_authors; 46 $all_link_cats = getTree('root', 'link'); 47 $all_link_authors = the_privileged('link.edit.own'); 48 49 $available_steps = array( 50 'link_list' => false, 51 'link_edit' => false, 52 'link_save' => true, 53 'link_change_pageby' => true, 54 'link_multi_edit' => true, 55 ); 56 57 if ($step && bouncer($step, $available_steps)) { 58 $step(); 59 } else { 60 link_list(); 61 } 62 } 63 64 /** 65 * The main panel listing all links. 66 * 67 * @param string|array $message The activity message 68 */ 69 70 function link_list($message = '') 71 { 72 global $event, $step, $link_list_pageby, $txp_user; 73 74 pagetop(gTxt('tab_link'), $message); 75 76 extract(gpsa(array( 77 'page', 78 'sort', 79 'dir', 80 'crit', 81 'search_method', 82 ))); 83 84 if ($sort === '') { 85 $sort = get_pref('link_sort_column', 'name'); 86 } else { 87 if (!in_array($sort, array('id', 'description', 'url', 'category', 'date', 'author'))) { 88 $sort = 'name'; 89 } 90 91 set_pref('link_sort_column', $sort, 'link', 2, '', 0, PREF_PRIVATE); 92 } 93 94 if ($dir === '') { 95 $dir = get_pref('link_sort_dir', 'asc'); 96 } else { 97 $dir = ($dir == 'desc') ? "desc" : "asc"; 98 set_pref('link_sort_dir', $dir, 'link', 2, '', 0, PREF_PRIVATE); 99 } 100 101 switch ($sort) { 102 case 'id': 103 $sort_sql = "txp_link.id $dir"; 104 break; 105 case 'description': 106 $sort_sql = "txp_link.description $dir, txp_link.id ASC"; 107 break; 108 case 'url': 109 $sort_sql = "txp_link.url $dir, txp_link.id ASC"; 110 break; 111 case 'category': 112 $sort_sql = "txp_category.title $dir, txp_link.id ASC"; 113 break; 114 case 'date': 115 $sort_sql = "txp_link.date $dir, txp_link.id ASC"; 116 break; 117 case 'author': 118 $sort_sql = "txp_users.RealName $dir, txp_link.id ASC"; 119 break; 120 default: 121 $sort = 'name'; 122 $sort_sql = "txp_link.linksort $dir, txp_link.id ASC"; 123 break; 124 } 125 126 $switch_dir = ($dir == 'desc') ? 'asc' : 'desc'; 127 128 $search = new Filter($event, 129 array( 130 'id' => array( 131 'column' => 'txp_link.id', 132 'label' => gTxt('ID'), 133 'type' => 'integer', 134 ), 135 'name' => array( 136 'column' => 'txp_link.linkname', 137 'label' => gTxt('link_name'), 138 ), 139 'url' => array( 140 'column' => 'txp_link.url', 141 'label' => gTxt('url'), 142 ), 143 'description' => array( 144 'column' => 'txp_link.description', 145 'label' => gTxt('description'), 146 ), 147 'category' => array( 148 'column' => array('txp_link.category', 'txp_category.title'), 149 'label' => gTxt('link_category'), 150 ), 151 'author' => array( 152 'column' => array('txp_link.author', 'txp_users.RealName'), 153 'label' => gTxt('author'), 154 ), 155 'linksort' => array( 156 'column' => 'txp_link.linksort', 157 'label' => gTxt('sort_value'), 158 ), 159 ) 160 ); 161 162 list($criteria, $crit, $search_method) = $search->getFilter(array( 163 'id' => array('can_list' => true), 164 )); 165 166 $search_render_options = array( 167 'placeholder' => 'search_links', 168 ); 169 170 $sql_from = 171 safe_pfx_j('txp_link')." 172 LEFT JOIN ".safe_pfx_j('txp_category')." ON txp_category.name = txp_link.category AND txp_category.type = 'link' 173 LEFT JOIN ".safe_pfx_j('txp_users')." ON txp_users.name = txp_link.author"; 174 175 if ($criteria === 1) { 176 $total = safe_count('txp_link', $criteria); 177 } else { 178 $total = getThing("SELECT COUNT(*) FROM $sql_from WHERE $criteria"); 179 } 180 181 echo n.'<div class="txp-layout">'. 182 n.tag( 183 hed(gTxt('tab_link'), 1, array('class' => 'txp-heading')), 184 'div', array('class' => 'txp-layout-4col-alt') 185 ); 186 187 $searchBlock = 188 n.tag( 189 $search->renderForm('link_list', $search_render_options), 190 'div', array( 191 'class' => 'txp-layout-4col-3span', 192 'id' => $event.'_control', 193 ) 194 ); 195 196 $createBlock = array(); 197 198 if (has_privs('link.edit')) { 199 $createBlock[] = 200 n.tag( 201 sLink('link', 'link_edit', gTxt('add_new_link'), 'txp-button'), 202 'div', array('class' => 'txp-control-panel') 203 ); 204 } 205 206 $contentBlockStart = n.tag_start('div', array( 207 'class' => 'txp-layout-1col', 208 'id' => $event.'_container', 209 )); 210 211 $createBlock = implode(n, $createBlock); 212 213 if ($total < 1) { 214 if ($criteria != 1) { 215 echo $searchBlock. 216 $contentBlockStart. 217 $createBlock. 218 graf( 219 span(null, array('class' => 'ui-icon ui-icon-info')).' '. 220 gTxt('no_results_found'), 221 array('class' => 'alert-block information') 222 ); 223 } else { 224 echo $contentBlockStart. 225 $createBlock. 226 graf( 227 span(null, array('class' => 'ui-icon ui-icon-info')).' '. 228 gTxt('no_links_recorded'), 229 array('class' => 'alert-block information') 230 ); 231 } 232 233 echo n.tag_end('div'). // End of .txp-layout-1col. 234 n.'</div>'; // End of .txp-layout.; 235 236 return; 237 } 238 239 $limit = max($link_list_pageby, 15); 240 241 list($page, $offset, $numPages) = pager($total, $limit, $page); 242 243 echo $searchBlock.$contentBlockStart.$createBlock; 244 245 $rs = safe_query( 246 "SELECT 247 txp_link.id, 248 UNIX_TIMESTAMP(txp_link.date) AS uDate, 249 txp_link.category, 250 txp_link.url, 251 txp_link.linkname, 252 txp_link.description, 253 txp_link.author, 254 txp_users.RealName AS realname, 255 txp_category.Title AS category_title 256 FROM $sql_from WHERE $criteria ORDER BY $sort_sql LIMIT $offset, $limit" 257 ); 258 259 if ($rs && numRows($rs)) { 260 $show_authors = !has_single_author('txp_link'); 261 262 echo n.tag( 263 toggle_box('links_detail'), 'div', array('class' => 'txp-list-options')). 264 n.tag_start('form', array( 265 'class' => 'multi_edit_form', 266 'id' => 'links_form', 267 'name' => 'longform', 268 'method' => 'post', 269 'action' => 'index.php', 270 )). 271 n.tag_start('div', array('class' => 'txp-listtables')). 272 n.tag_start('table', array('class' => 'txp-list')). 273 n.tag_start('thead'). 274 tr( 275 hCell( 276 fInput('checkbox', 'select_all', 0, '', '', '', '', '', 'select_all'), 277 '', ' class="txp-list-col-multi-edit" scope="col" title="'.gTxt('toggle_all_selected').'"' 278 ). 279 column_head( 280 'ID', 'id', 'link', true, $switch_dir, $crit, $search_method, 281 (('id' == $sort) ? "$dir " : '').'txp-list-col-id' 282 ). 283 column_head( 284 'link_name', 'name', 'link', true, $switch_dir, $crit, $search_method, 285 (('name' == $sort) ? "$dir " : '').'txp-list-col-name' 286 ). 287 column_head( 288 'description', 'description', 'link', true, $switch_dir, $crit, $search_method, 289 (('description' == $sort) ? "$dir " : '').'txp-list-col-description links_detail' 290 ). 291 column_head( 292 'link_category', 'category', 'link', true, $switch_dir, $crit, $search_method, 293 (('category' == $sort) ? "$dir " : '').'txp-list-col-category category' 294 ). 295 column_head( 296 'url', 'url', 'link', true, $switch_dir, $crit, $search_method, 297 (('url' == $sort) ? "$dir " : '').'txp-list-col-url' 298 ). 299 column_head( 300 'date', 'date', 'link', true, $switch_dir, $crit, $search_method, 301 (('date' == $sort) ? "$dir " : '').'txp-list-col-created date links_detail' 302 ). 303 ( 304 $show_authors 305 ? column_head('author', 'author', 'link', true, $switch_dir, $crit, $search_method, 306 (('author' == $sort) ? "$dir " : '').'txp-list-col-author name') 307 : '' 308 ) 309 ). 310 n.tag_end('thead'). 311 n.tag_start('tbody'); 312 313 $validator = new Validator(); 314 315 while ($a = nextRow($rs)) { 316 extract($a, EXTR_PREFIX_ALL, 'link'); 317 318 $edit_url = array( 319 'event' => 'link', 320 'step' => 'link_edit', 321 'id' => $link_id, 322 'sort' => $sort, 323 'dir' => $dir, 324 'page' => $page, 325 'search_method' => $search_method, 326 'crit' => $crit, 327 ); 328 329 $validator->setConstraints(array(new CategoryConstraint($link_category, array('type' => 'link')))); 330 $vc = $validator->validate() ? '' : ' error'; 331 332 if ($link_category) { 333 $link_category = span(txpspecialchars($link_category_title), array('title' => $link_category)); 334 } 335 336 $can_edit = has_privs('link.edit') || ($link_author === $txp_user && has_privs('link.edit.own')); 337 $view_url = txpspecialchars($link_url); 338 339 echo tr( 340 td( 341 fInput('checkbox', 'selected[]', $link_id), '', 'txp-list-col-multi-edit' 342 ). 343 hCell( 344 ($can_edit ? href($link_id, $edit_url, ' title="'.gTxt('edit').'"') : $link_id), '', ' class="txp-list-col-id" scope="row"' 345 ). 346 td( 347 ($can_edit ? href(txpspecialchars($link_linkname), $edit_url, ' title="'.gTxt('edit').'"') : txpspecialchars($link_linkname)), '', 'txp-list-col-name' 348 ). 349 td( 350 txpspecialchars($link_description), '', 'txp-list-col-description links_detail' 351 ). 352 td( 353 $link_category, '', 'txp-list-col-category category'.$vc 354 ). 355 td( 356 href($view_url, $view_url, ' rel="external" target="_blank"'), '', 'txp-list-col-url' 357 ). 358 td( 359 gTime($link_uDate), '', 'txp-list-col-created date links_detail' 360 ). 361 ( 362 $show_authors 363 ? td(span(txpspecialchars($link_realname), array('title' => $link_author)), '', 'txp-list-col-author name') 364 : '' 365 ) 366 ); 367 } 368 369 echo n.tag_end('tbody'). 370 n.tag_end('table'). 371 n.tag_end('div'). 372 link_multiedit_form($page, $sort, $dir, $crit, $search_method). 373 tInput(). 374 n.tag_end('form'). 375 376 n.tag_start('div', array( 377 'class' => 'txp-navigation', 378 'id' => $event.'_navigation', 379 )). 380 pageby_form('link', $link_list_pageby). 381 nav_form('link', $page, $numPages, $sort, $dir, $crit, $search_method, $total, $limit). 382 n.tag_end('div'); 383 } 384 385 echo n.tag_end('div'). // End of .txp-layout-1col. 386 n.'</div>'; // End of .txp-layout. 387 } 388 389 /** 390 * Renders and outputs the link editor panel. 391 * 392 * @param string|array $message The activity message 393 */ 394 395 function link_edit($message = '') 396 { 397 global $vars, $event, $step, $txp_user; 398 399 pagetop(gTxt('tab_link'), $message); 400 401 extract(array_map('assert_string', gpsa($vars))); 402 403 $is_edit = ($id && $step == 'link_edit'); 404 405 $rs = array(); 406 407 if ($is_edit) { 408 $id = assert_int($id); 409 $rs = safe_row("*", 'txp_link', "id = $id"); 410 411 if ($rs) { 412 extract($rs); 413 414 if (!has_privs('link.edit') && !($author === $txp_user && has_privs('link.edit.own'))) { 415 link_list(gTxt('restricted_area')); 416 417 return; 418 } 419 } 420 } 421 422 if (has_privs('link.edit') || has_privs('link.edit.own')) { 423 $caption = gTxt(($is_edit) ? 'edit_link' : 'add_new_link'); 424 425 echo form( 426 hed($caption, 2). 427 inputLabel( 428 'link_name', 429 fInput('text', 'linkname', $linkname, '', '', '', INPUT_REGULAR, '', 'link_name'), 430 'title', '', array('class' => 'txp-form-field edit-link-name') 431 ). 432 inputLabel( 433 'link_sort', 434 fInput('text', 'linksort', $linksort, 'input-medium', '', '', INPUT_MEDIUM, '', 'link_sort'), 435 'sort_value', 'link_sort', array('class' => 'txp-form-field edit-link-sort') 436 ). 437 // TODO: maybe use type="url" once browsers are less strict. 438 inputLabel( 439 'link_url', 440 fInput('text', 'url', $url, '', '', '', INPUT_REGULAR, '', 'link_url'), 441 'url', 'link_url', array('class' => 'txp-form-field edit-link-url') 442 ). 443 inputLabel( 444 'link_category', 445 event_category_popup('link', $category, 'link_category'). 446 n.eLink('category', 'list', '', '', gTxt('edit'), '', '', '', 'txp-option-link'), 447 'link_category', 'link_category', array('class' => 'txp-form-field edit-link-category') 448 ). 449 inputLabel( 450 'link_description', 451 '<textarea id="link_description" name="description" cols="'.INPUT_LARGE.'" rows="'.TEXTAREA_HEIGHT_SMALL.'">'.txpspecialchars($description).'</textarea>', 452 'description', 'link_description', array('class' => 'txp-form-field txp-form-field-textarea edit-link-description') 453 ). 454 pluggable_ui('link_ui', 'extend_detail_form', '', $rs). 455 graf( 456 sLink('link', '', gTxt('cancel'), 'txp-button'). 457 fInput('submit', '', gTxt('save'), 'publish'), 458 array('class' => 'txp-edit-actions') 459 ). 460 eInput('link'). 461 sInput('link_save'). 462 hInput('id', $id). 463 hInput('search_method', gps('search_method')). 464 hInput('crit', gps('crit')), 465 '', '', 'post', 'txp-edit', '', 'link_details'); 466 } 467 } 468 469 /** 470 * Legacy link category HTML select field. 471 * 472 * @param string $cat 473 * @return string 474 * @deprecated in 4.6.0 475 */ 476 477 function linkcategory_popup($cat = '') 478 { 479 return event_category_popup('link', $cat, 'link_category'); 480 } 481 482 // ------------------------------------------------------------- 483 484 function link_save() 485 { 486 global $vars, $txp_user; 487 488 $varray = array_map('assert_string', gpsa($vars)); 489 extract(doSlash($varray)); 490 491 if ($id) { 492 $id = $varray['id'] = assert_int($id); 493 } 494 495 if ($linkname === '' && $url === '' && $description === '') { 496 link_list(array(gTxt('link_empty'), E_ERROR)); 497 498 return; 499 } 500 501 $author = fetch('author', 'txp_link', 'id', $id); 502 if (!has_privs('link.edit') && !($author === $txp_user && has_privs('link.edit.own'))) { 503 link_list(gTxt('restricted_area')); 504 505 return; 506 } 507 508 if (!$linksort) { 509 $linksort = $linkname; 510 } 511 512 $constraints = array( 513 'category' => new CategoryConstraint($varray['category'], array('type' => 'link')), 514 ); 515 516 callback_event_ref('link_ui', 'validate_save', 0, $varray, $constraints); 517 $validator = new Validator($constraints); 518 519 if ($validator->validate()) { 520 if ($id) { 521 $ok = safe_update('txp_link', 522 "category = '$category', 523 url = '".trim($url)."', 524 linkname = '$linkname', 525 linksort = '$linksort', 526 description = '$description', 527 author = '".doSlash($txp_user)."'", 528 "id = $id" 529 ); 530 } else { 531 $ok = safe_insert('txp_link', 532 "category = '$category', 533 date = NOW(), 534 url = '".trim($url)."', 535 linkname = '$linkname', 536 linksort = '$linksort', 537 description = '$description', 538 author = '".doSlash($txp_user)."'" 539 ); 540 if ($ok) { 541 $GLOBALS['ID'] = $_POST['id'] = $ok; 542 } 543 } 544 545 if ($ok) { 546 // update lastmod due to link feeds 547 update_lastmod('link_saved', compact('id', 'linkname', 'linksort', 'url', 'category', 'description')); 548 $message = gTxt(($id ? 'link_updated' : 'link_created'), array('{name}' => doStrip($linkname))); 549 } else { 550 $message = array(gTxt('link_save_failed'), E_ERROR); 551 } 552 } else { 553 $message = array(gTxt('link_save_failed'), E_ERROR); 554 } 555 556 link_list($message); 557 } 558 559 // ------------------------------------------------------------- 560 561 function link_change_pageby() 562 { 563 event_change_pageby('link'); 564 link_list(); 565 } 566 567 // ------------------------------------------------------------- 568 569 function link_multiedit_form($page, $sort, $dir, $crit, $search_method) 570 { 571 global $all_link_cats, $all_link_authors; 572 573 $categories = $all_link_cats ? treeSelectInput('category', $all_link_cats, '') : ''; 574 $authors = $all_link_authors ? selectInput('author', $all_link_authors, '', true) : ''; 575 576 $methods = array( 577 'changecategory' => array('label' => gTxt('changecategory'), 'html' => $categories), 578 'changeauthor' => array('label' => gTxt('changeauthor'), 'html' => $authors), 579 'delete' => gTxt('delete'), 580 ); 581 582 if (!$categories) { 583 unset($methods['changecategory']); 584 } 585 586 if (has_single_author('txp_link') || !has_privs('link.edit')) { 587 unset($methods['changeauthor']); 588 } 589 590 if (!has_privs('link.delete.own') && !has_privs('link.delete')) { 591 unset($methods['delete']); 592 } 593 594 return multi_edit($methods, 'link', 'link_multi_edit', $page, $sort, $dir, $crit, $search_method); 595 } 596 597 // ------------------------------------------------------------- 598 599 function link_multi_edit() 600 { 601 global $txp_user, $all_link_cats, $all_link_authors; 602 603 // Empty entry to permit clearing the category 604 $categories = array(''); 605 606 foreach ($all_link_cats as $row) { 607 $categories[] = $row['name']; 608 } 609 610 $selected = ps('selected'); 611 612 if (!$selected or !is_array($selected)) { 613 link_list(); 614 615 return; 616 } 617 618 $selected = array_map('assert_int', $selected); 619 $method = ps('edit_method'); 620 $changed = array(); 621 $key = ''; 622 623 switch ($method) { 624 case 'delete' : 625 if (!has_privs('link.delete')) { 626 if (has_privs('link.delete.own')) { 627 $selected = safe_column("id", 'txp_link', "id IN (".join(',', $selected).") AND author = '".doSlash($txp_user)."'"); 628 } else { 629 $selected = array(); 630 } 631 } 632 foreach ($selected as $id) { 633 if (safe_delete('txp_link', "id = $id")) { 634 $changed[] = $id; 635 } 636 } 637 638 if ($changed) { 639 callback_event('links_deleted', '', 0, $changed); 640 } 641 642 $key = ''; 643 break; 644 case 'changecategory': 645 $val = ps('category'); 646 if (in_array($val, $categories)) { 647 $key = 'category'; 648 } 649 break; 650 case 'changeauthor': 651 $val = ps('author'); 652 if (has_privs('link.edit') && in_array($val, $all_link_authors)) { 653 $key = 'author'; 654 } 655 break; 656 default: 657 $key = ''; 658 $val = ''; 659 break; 660 } 661 662 if (!has_privs('link.edit')) { 663 if (has_privs('link.edit.own')) { 664 $selected = safe_column("id", 'txp_link', "id IN (".join(',', $selected).") AND author = '".doSlash($txp_user)."'"); 665 } else { 666 $selected = array(); 667 } 668 } 669 670 if ($selected and $key) { 671 foreach ($selected as $id) { 672 if (safe_update('txp_link', "$key = '".doSlash($val)."'", "id = $id")) { 673 $changed[] = $id; 674 } 675 } 676 } 677 678 if ($changed) { 679 update_lastmod('link_updated', $changed); 680 681 link_list(gTxt( 682 ($method == 'delete' ? 'links_deleted' : 'link_updated'), 683 array(($method == 'delete' ? '{list}' : '{name}') => join(', ', $changed)))); 684 685 return; 686 } 687 688 link_list(); 689 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title