Textpattern | PHP Cross Reference | Content Management Systems |
Description: Category 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 * Category panel. 27 * 28 * @package Admin\Category 29 */ 30 31 if (!defined('txpinterface')) { 32 die('txpinterface is undefined.'); 33 } 34 35 if ($event == 'category') { 36 require_privs('category'); 37 38 $available_steps = array( 39 'cat_category_list' => false, 40 'cat_category_multiedit' => true, 41 'cat_article_create' => true, 42 'cat_image_create' => true, 43 'cat_file_create' => true, 44 'cat_link_create' => true, 45 'cat_article_save' => true, 46 'cat_image_save' => true, 47 'cat_file_save' => true, 48 'cat_link_save' => true, 49 'cat_article_edit' => false, 50 'cat_image_edit' => false, 51 'cat_file_edit' => false, 52 'cat_link_edit' => false, 53 ); 54 55 if ($step && bouncer($step, $available_steps)) { 56 $step(); 57 } else { 58 cat_category_list(); 59 } 60 } 61 62 /** 63 * Outputs the main panel listing all categories. 64 * 65 * @param string|array $message The activity message 66 */ 67 68 function cat_category_list($message = "") 69 { 70 pagetop(gTxt('categories'), $message); 71 $out = array(n.'<div class="txp-layout">'. 72 n.tag( 73 hed(gTxt('tab_organise'), 1, array('class' => 'txp-heading')), 74 'div', array('class' => 'txp-layout-1col') 75 ), 76 n.tag(cat_article_list(), 'section', array( 77 'class' => 'txp-layout-4col', 78 'id' => 'categories_article', 79 ) 80 ), 81 n.tag(cat_image_list(), 'section', array( 82 'class' => 'txp-layout-4col', 83 'id' => 'categories_image', 84 ) 85 ), 86 n.tag(cat_file_list(), 'section', array( 87 'class' => 'txp-layout-4col', 88 'id' => 'categories_file', 89 ) 90 ), 91 n.tag(cat_link_list(), 'section', array( 92 'class' => 'txp-layout-4col', 93 'id' => 'categories_link', 94 ) 95 ), 96 n.'</div>', // End of .txp-layout. 97 script_js(<<<EOS 98 $(document).ready(function () 99 { 100 $('.category-tree').txpMultiEditForm({ 101 'row' : 'p', 102 'highlighted' : 'p' 103 }); 104 }); 105 EOS 106 ), 107 ); 108 echo join(n, $out); 109 } 110 111 /** 112 * Renders a list of article categories. 113 */ 114 115 function cat_article_list() 116 { 117 return cat_event_category_list('article'); 118 } 119 120 /** 121 * Processes a saved editor form and creates an article category. 122 */ 123 124 function cat_article_create() 125 { 126 return cat_event_category_create('article'); 127 } 128 129 /** 130 * Renders an editor form for article categories. 131 */ 132 133 function cat_article_edit() 134 { 135 return cat_event_category_edit('article'); 136 } 137 138 /** 139 * Saves an article category. 140 */ 141 142 function cat_article_save() 143 { 144 return cat_event_category_save('article', 'textpattern'); 145 } 146 147 /** 148 * Renders a list of parent category options. 149 * 150 * @return string HTML <select> input 151 */ 152 153 function cat_parent_pop($name, $type, $id) 154 { 155 if ($id) { 156 $id = assert_int($id); 157 list($lft, $rgt) = array_values(safe_row("lft, rgt", 'txp_category', "id = $id")); 158 159 $rs = getTree('root', $type, "lft NOT BETWEEN $lft AND $rgt"); 160 } else { 161 $rs = getTree('root', $type); 162 } 163 164 if ($rs) { 165 return array(treeSelectInput('parent', $rs, $name, 'category_parent'), true); 166 } 167 168 return array(gTxt('no_other_categories_exist'), false); 169 } 170 171 /** 172 * Renders a list of link categories. 173 */ 174 175 function cat_link_list() 176 { 177 return cat_event_category_list('link'); 178 } 179 180 /** 181 * Processes a saved editor form and creates a link category. 182 */ 183 184 function cat_link_create() 185 { 186 return cat_event_category_create('link'); 187 } 188 189 /** 190 * Renders an editor form for link categories. 191 */ 192 193 function cat_link_edit() 194 { 195 return cat_event_category_edit('link'); 196 } 197 198 /** 199 * Saves a link category. 200 */ 201 202 function cat_link_save() 203 { 204 return cat_event_category_save('link', 'txp_link'); 205 } 206 207 /** 208 * Renders a list of image categories. 209 */ 210 211 function cat_image_list() 212 { 213 return cat_event_category_list('image'); 214 } 215 216 /** 217 * Processes a saved editor form and creates an image category. 218 */ 219 220 function cat_image_create() 221 { 222 return cat_event_category_create('image'); 223 } 224 225 /** 226 * Renders an editor form for image categories. 227 */ 228 229 function cat_image_edit() 230 { 231 return cat_event_category_edit('image'); 232 } 233 234 /** 235 * Saves an image category. 236 */ 237 238 function cat_image_save() 239 { 240 return cat_event_category_save('image', 'txp_image'); 241 } 242 243 /** 244 * Renders a multi-edit form. 245 * 246 * @param string $area Type of category 247 * @param array $array Additional HTML added to the form 248 * @return string HTML 249 */ 250 251 function cat_article_multiedit_form($area, $array) 252 { 253 $rs = getTree('root', $area); 254 $categories = $rs ? treeSelectInput('new_parent', $rs, '') : ''; 255 256 $methods = array( 257 'changeparent' => array('label' => gTxt('changeparent'), 'html' => $categories), 258 'deleteforce' => gTxt('deleteforce'), 259 'delete' => gTxt('delete'), 260 ); 261 262 if ($array) { 263 return 264 form( 265 join('', $array). 266 hInput('type', $area). 267 multi_edit($methods, 'category', 'cat_category_multiedit', '', '', '', '', '', $area), '', '', 'post', 'category-tree', '', 'category_'.$area.'_form' 268 ); 269 } 270 271 return; 272 } 273 274 /** 275 * Processes multi-edit actions. 276 */ 277 278 function cat_category_multiedit() 279 { 280 $type = ps('type'); 281 $method = ps('edit_method'); 282 $things = ps('selected'); 283 284 if (is_array($things) and $things and in_array($type, array('article', 'image', 'link', 'file'))) { 285 $things = array_map('assert_int', $things); 286 287 if ($method == 'delete' || $method == 'deleteforce') { 288 if ($type === 'article') { 289 $used = "name NOT IN (SELECT category1 FROM ".safe_pfx('textpattern').") 290 AND name NOT IN (SELECT category2 FROM ".safe_pfx('textpattern').")"; 291 } else { 292 $used = "name NOT IN (SELECT category FROM ".safe_pfx('txp_'.$type).")"; 293 } 294 295 $rs = safe_rows("id, name", 'txp_category', "id IN (".join(',', $things).") AND type = '".$type."'".(($method == 'deleteforce') ? '' : " AND rgt - lft = 1 AND ".$used)); 296 297 if ($rs) { 298 foreach ($rs as $cat) { 299 $catid[] = $cat['id']; 300 $names[] = doSlash($cat['name']); 301 } 302 303 if (safe_delete('txp_category', "id IN (".join(',', $catid).")")) { 304 if ($method == 'deleteforce') { 305 // Clear the deleted category names from assets. 306 $affected = join("','", $names); 307 if ($type === 'article') { 308 safe_update('textpattern', "category1 = ''", "category1 IN ('$affected')"); 309 safe_update('textpattern', "category2 = ''", "category2 IN ('$affected')"); 310 } else { 311 safe_update('txp_'.$type, "category = ''", "category IN ('$affected')"); 312 } 313 314 // Promote subcatagories of deleted catagories to root. 315 safe_update('txp_category', "parent = 'root'", "parent IN ('$affected')"); 316 } 317 318 rebuild_tree_full($type); 319 callback_event('categories_deleted', $type, 0, $catid); 320 321 $message = gTxt($type.'_categories_deleted', array('{list}' => join(', ', $catid))); 322 323 return cat_category_list($message); 324 } 325 } 326 } elseif ($method == 'changeparent') { 327 $new_parent = ps('new_parent'); 328 329 $rs = safe_rows("id, name", 'txp_category', "id IN (".join(',', $things).") AND type = '".$type."'"); 330 331 if ($rs) { 332 $exists = safe_field("name", 'txp_category', "name = '".doSlash($new_parent)."' AND type = '$type'"); 333 $parent = ($exists === false) ? 'root' : $exists; 334 $to_change = $affected = array(); 335 336 foreach ($rs as $cat) { 337 // Cannot assign parent to itself. 338 if ($cat['name'] != $new_parent) { 339 $to_change[] = doSlash($cat['name']); 340 $affected[] = $cat['name']; 341 } 342 } 343 344 $ret = safe_update('txp_category', "parent = '".doSlash($parent)."'", "name IN ('".join("','", $to_change)."') AND type = '".$type."'"); 345 346 if ($ret) { 347 rebuild_tree_full($type); 348 349 $message = gTxt('categories_set_parent', array('{type}' => gTxt($type), '{parent}' => $parent, '{list}' => join(', ', $affected))); 350 351 return cat_category_list($message); 352 } 353 } 354 } 355 } 356 357 return cat_category_list(); 358 } 359 360 /** 361 * Renders a list of categories. 362 * 363 * @param string $event Type of category 364 * @return string HTML 365 */ 366 367 function cat_event_category_list($event) 368 { 369 $rs = getTree('root', $event); 370 371 $parent = ps('parent_cat'); 372 373 $heading = 'tab_'.($event == 'article' ? 'list' : $event); 374 $for = $rs ? ' for="'.$event.'_category_parent"' : ''; 375 376 $out = hed(gTxt($heading).popHelp($event.'_category'), 2). 377 form( 378 graf( 379 tag(gTxt('create_new_category'), 'label', array('for' => $event.'_category_new')).br. 380 fInput('text', 'title', '', '', '', '', INPUT_REGULAR, '', $event.'_category_new') 381 ). 382 (($rs) 383 ? graf('<label'.$for.'>'.gTxt('parent').'</label>'.br. 384 treeSelectInput('parent_cat', $rs, $parent, $event.'_category_parent'), array('class' => 'parent')) 385 : '' 386 ). 387 graf( 388 fInput('submit', '', gTxt('Create')). 389 eInput('category'). 390 sInput('cat_'.$event.'_create') 391 ), '', '', 'post', $event); 392 393 if ($rs) { 394 $total_count = array(); 395 396 if ($event == 'article') { 397 // Count distinct articles for both categories, avoid duplicates. 398 $rs2 = getRows( 399 "SELECT category, COUNT(*) AS num FROM ( 400 SELECT ID, Category1 AS category FROM ".safe_pfx('textpattern')." 401 UNION 402 SELECT ID, Category2 AS category FROM ".safe_pfx('textpattern')." 403 ) AS t WHERE category != '' GROUP BY category"); 404 405 if ($rs2 !== false) { 406 foreach ($rs2 as $a) { 407 $total_count[$a['category']] = $a['num']; 408 } 409 } 410 } else { 411 switch ($event) { 412 case 'link': 413 $rs2 = safe_rows_start("category, COUNT(*) AS num", 'txp_link', "1 = 1 GROUP BY category"); 414 break; 415 case 'image': 416 $rs2 = safe_rows_start("category, COUNT(*) AS num", 'txp_image', "1 = 1 GROUP BY category"); 417 break; 418 case 'file': 419 $rs2 = safe_rows_start("category, COUNT(*) AS num", 'txp_file', "1 = 1 GROUP BY category"); 420 break; 421 } 422 423 while ($a = nextRow($rs2)) { 424 $name = $a['category']; 425 $num = $a['num']; 426 427 $total_count[$name] = $num; 428 } 429 } 430 431 $items = array(); 432 433 foreach ($rs as $a) { 434 extract($a); 435 436 // Format count. 437 switch ($event) { 438 case 'article': 439 $url = 'index.php?event=list'.a.'search_method=categories'.a.'crit='.$name; 440 break; 441 case 'link': 442 $url = 'index.php?event=link'.a.'search_method=category'.a.'crit='.$name; 443 break; 444 case 'image': 445 $url = 'index.php?event=image'.a.'search_method=category'.a.'crit='.$name; 446 break; 447 case 'file': 448 $url = 'index.php?event=file'.a.'search_method=category'.a.'crit='.$name; 449 break; 450 } 451 452 $count = isset($total_count[$name]) ? href('('.$total_count[$name].')', $url) : '(0)'; 453 454 if (empty($title)) { 455 $edit_link = '<em>'.eLink('category', 'cat_'.$event.'_edit', 'id', $id, gTxt('untitled')).'</em>'; 456 } else { 457 $edit_link = eLink('category', 'cat_'.$event.'_edit', 'id', $id, $title); 458 } 459 460 $items[] = graf( 461 checkbox('selected[]', $id, 0).sp.str_repeat(sp.sp, $level * 2).$edit_link.sp.$count, ' class="level-'.$level.'"'); 462 } 463 464 if ($items) { 465 $out .= cat_article_multiedit_form($event, $items); 466 } 467 } else { 468 $out .= graf( 469 span(null, array('class' => 'ui-icon ui-icon-info')).' '. 470 gTxt('no_categories_exist'), 471 array('class' => 'alert-block information') 472 ); 473 } 474 475 return $out; 476 } 477 478 /** 479 * Creates a new category. 480 * 481 * @param string $event The type of category 482 */ 483 484 function cat_event_category_create($event) 485 { 486 $title = ps('title'); 487 488 $name = strtolower(sanitizeForUrl($title)); 489 490 if (!$name) { 491 $message = array(gTxt($event.'_category_invalid', array('{name}' => $title)), E_ERROR); 492 493 return cat_category_list($message); 494 } 495 496 $exists = safe_field("name", 'txp_category', "name = '".doSlash($name)."' AND type = '".doSlash($event)."'"); 497 498 if ($exists !== false) { 499 $message = array(gTxt($event.'_category_already_exists', array('{name}' => $name)), E_ERROR); 500 501 return cat_category_list($message); 502 } 503 504 $parent = strtolower(sanitizeForUrl(ps('parent_cat'))); 505 $parent_exists = safe_field("name", 'txp_category', "name = '".doSlash($parent)."' AND type = '".doSlash($event)."'"); 506 $parent = ($parent_exists !== false) ? $parent_exists : 'root'; 507 508 $q = safe_insert('txp_category', "name = '".doSlash($name)."', title = '".doSlash($title)."', type = '".doSlash($event)."', parent = '".$parent."'"); 509 510 if ($q) { 511 rebuild_tree_full($event); 512 513 $message = gTxt($event.'_category_created', array('{name}' => $name)); 514 515 cat_category_list($message); 516 } else { 517 cat_category_list(array(gTxt('category_save_failed'), E_ERROR)); 518 } 519 } 520 521 /** 522 * Renders and outputs a category editor panel. 523 * 524 * @param string $evname Type of category 525 */ 526 527 function cat_event_category_edit($evname, $message = '') 528 { 529 $id = assert_int(gps('id')); 530 $parent = doSlash(gps('parent')); 531 532 $row = safe_row("*", 'txp_category', "id = $id"); 533 534 if ($row) { 535 pagetop(gTxt('edit_category'), $message); 536 extract($row); 537 list($parent_widget, $has_parent) = cat_parent_pop($parent, $evname, $id); 538 539 $out = hed(gTxt('edit_category'), 2). 540 inputLabel( 541 'category_name', 542 fInput('text', 'name', $name, '', '', '', INPUT_REGULAR, '', 'category_name'), 543 $evname.'_category_name', '', array('class' => 'txp-form-field edit-category-name') 544 ). 545 inputLabel( 546 'category_parent', 547 $parent_widget, 548 'parent', '', array('class' => 'txp-form-field edit-category-parent') 549 ). 550 inputLabel( 551 'category_title', 552 fInput('text', 'title', $title, '', '', '', INPUT_REGULAR, '', 'category_title'), 553 $evname.'_category_title', '', array('class' => 'txp-form-field edit-category-title') 554 ). 555 inputLabel( 556 'category_description', 557 '<textarea id="category_description" name="description" cols="'.INPUT_LARGE.'" rows="'.TEXTAREA_HEIGHT_SMALL.'">'.$description.'</textarea>', 558 $evname.'_category_description', 'category_description', array('class' => 'txp-form-field txp-form-field-textarea edit-category-description') 559 ). 560 pluggable_ui('category_ui', 'extend_detail_form', '', $row). 561 hInput('id', $id). 562 graf( 563 sLink('category', '', gTxt('cancel'), 'txp-button'). 564 fInput('submit', '', gTxt('save'), 'publish'), 565 array('class' => 'txp-edit-actions') 566 ). 567 eInput('category'). 568 sInput('cat_'.$evname.'_save'). 569 hInput('old_name', $name); 570 571 echo form($out, '', '', 'post', 'txp-edit'); 572 } else { 573 cat_category_list(array(gTxt('category_not_found'), E_ERROR)); 574 } 575 } 576 577 /** 578 * Saves a category from HTTP POST data. 579 * 580 * @param string $event Type of category 581 * @param string $table Affected database table 582 */ 583 584 function cat_event_category_save($event, $table_name) 585 { 586 extract(doSlash(array_map('assert_string', psa(array('id', 'name', 'description', 'old_name', 'parent', 'title'))))); 587 $id = assert_int($id); 588 589 $rawname = $name; 590 $name = sanitizeForUrl($rawname); 591 592 // Make sure the name is valid. 593 if (!$name) { 594 $message = array(gTxt($event.'_category_invalid', array('{name}' => $rawname)), E_ERROR); 595 596 return cat_event_category_edit($event, $message); 597 } 598 599 // Don't allow rename to clobber an existing category. 600 $existing_id = safe_field("id", 'txp_category', "name = '$name' AND type = '$event'"); 601 602 if ($existing_id and $existing_id != $id) { 603 $message = array(gTxt($event.'_category_already_exists', array('{name}' => $name)), E_ERROR); 604 605 return cat_event_category_edit($event, $message); 606 } 607 608 // TODO: validate parent? 609 $parent = ($parent) ? $parent : 'root'; 610 611 $message = array(gTxt('category_save_failed'), E_ERROR); 612 613 if (safe_update('txp_category', "name = '$name', parent = '$parent', title = '$title', description = '$description'", "id = $id") && 614 safe_update('txp_category', "parent = '$name'", "parent = '$old_name' AND type = '$event'")) { 615 rebuild_tree_full($event); 616 617 if ($event == 'article') { 618 if (safe_update('textpattern', "Category1 = '$name'", "Category1 = '$old_name'") && 619 safe_update('textpattern', "Category2 = '$name'", "Category2 = '$old_name'")) { 620 $message = gTxt($event.'_category_updated', array('{name}' => doStrip($name))); 621 } 622 } else { 623 if (safe_update($table_name, "category = '$name'", "category = '$old_name'")) { 624 $message = gTxt($event.'_category_updated', array('{name}' => doStrip($name))); 625 } 626 } 627 } 628 cat_category_list($message); 629 } 630 631 /** 632 * Renders a list of file categories. 633 */ 634 635 function cat_file_list() 636 { 637 return cat_event_category_list('file'); 638 } 639 640 /** 641 * Processes a saved editor form and creates a file category. 642 */ 643 644 function cat_file_create() 645 { 646 return cat_event_category_create('file'); 647 } 648 649 /** 650 * Renders an editor form for file categories. 651 */ 652 653 function cat_file_edit() 654 { 655 return cat_event_category_edit('file'); 656 } 657 658 /** 659 * Saves a file category. 660 */ 661 662 function cat_file_save() 663 { 664 return cat_event_category_save('file', 'txp_file'); 665 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title