Textpattern | PHP Cross Reference | Content Management Systems |
Description: Sections panel.
1 <?php 2 3 /* 4 * Textpattern Content Management System 5 * https://textpattern.com/ 6 * 7 * Copyright (C) 2020 The Textpattern Development Team 8 * 9 * This file is part of Textpattern. 10 * 11 * Textpattern is free software; you can redistribute it and/or 12 * modify it under the terms of the GNU General Public License 13 * as published by the Free Software Foundation, version 2. 14 * 15 * Textpattern is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU General Public License for more details. 19 * 20 * You should have received a copy of the GNU General Public License 21 * along with Textpattern. If not, see <https://www.gnu.org/licenses/>. 22 */ 23 24 /** 25 * Sections panel. 26 * 27 * @package Admin\Section 28 */ 29 30 use Textpattern\Search\Filter; 31 32 if (!defined('txpinterface')) { 33 die('txpinterface is undefined.'); 34 } 35 36 if ($event == 'section') { 37 require_privs('section'); 38 39 global $all_skins, $all_pages, $all_styles; 40 41 $all_skins = \Txp::get('Textpattern\Skin\Skin')->getInstalled(); 42 $all_pages = \Txp::get('Textpattern\Skin\Page')->getInstalled(); 43 $all_styles = \Txp::get('Textpattern\Skin\Css')->getInstalled(); 44 45 $available_steps = array( 46 'section_change_pageby' => true, 47 'sec_section_list' => false, 48 'section_delete' => true, 49 'section_save' => true, 50 'section_edit' => false, 51 'section_multi_edit' => true, 52 'section_set_default' => true, 53 'section_set_theme' => true, 54 'section_select_skin' => false, 55 'section_toggle_option' => true, 56 ); 57 58 if ($step && is_callable($step) && bouncer($step, $available_steps)) { 59 $step(); 60 } else { 61 sec_section_list(); 62 } 63 } 64 65 /** 66 * The main panel listing all sections. 67 * 68 * So-named to avoid clashing with the <txp:section_list /> tag. 69 * 70 * @param string|array $message The activity message 71 */ 72 73 function sec_section_list($message = '', $update = false) 74 { 75 global $event, $step, $all_pages, $all_styles, $txp_sections; 76 77 if ($update) { 78 $txp_sections = safe_column(array('name'), 'txp_section', '1 ORDER BY title, name'); 79 } 80 81 pagetop(gTxt('tab_sections'), $message); 82 83 extract(gpsa(array( 84 'page', 85 'sort', 86 'dir', 87 'crit', 88 'search_method', 89 ))); 90 91 $columns = array('name', 'title', 'skin', 'page', 'css', 'permlink_mode', 'on_frontpage', 'in_rss', 'searchable', 'article_count'); 92 $columns = array_merge( 93 array_combine($columns, $columns), 94 array('on_frontpage' => 'on_front_page', 'in_rss' => 'syndicate', 'searchable' => 'include_in_search', 'article_count' => 'articles') 95 ); 96 97 if ($sort === '') { 98 $sort = get_pref('section_sort_column', 'name'); 99 } else { 100 if (!isset($columns[$sort])) { 101 $sort = 'name'; 102 } 103 104 set_pref('section_sort_column', $sort, 'section', PREF_HIDDEN, '', 0, PREF_PRIVATE); 105 } 106 107 if ($dir === '') { 108 $dir = get_pref('section_sort_dir', 'desc'); 109 } else { 110 $dir = ($dir == 'asc') ? "asc" : "desc"; 111 set_pref('section_sort_dir', $dir, 'section', PREF_HIDDEN, '', 0, PREF_PRIVATE); 112 } 113 114 if (isset($columns[$sort])) { 115 $sort_sql = "$sort $dir"; 116 } else { 117 $sort_sql = "name $dir"; 118 } 119 120 $switch_dir = ($dir == 'desc') ? 'asc' : 'desc'; 121 122 $search = new Filter($event, 123 array( 124 'name' => array( 125 'column' => 'txp_section.name', 126 'label' => gTxt('name'), 127 ), 128 'title' => array( 129 'column' => 'txp_section.title', 130 'label' => gTxt('title'), 131 ), 132 'skin' => array( 133 'column' => array('txp_section.skin', 'txp_section.dev_skin'), 134 'label' => gTxt('skin'), 135 ), 136 'page' => array( 137 'column' => array('txp_section.page', 'txp_section.dev_page'), 138 'label' => gTxt('page'), 139 ), 140 'css' => array( 141 'column' => array('txp_section.css', 'txp_section.dev_css'), 142 'label' => gTxt('css'), 143 ), 144 'description' => array( 145 'column' => 'txp_section.description', 146 'label' => gTxt('description'), 147 ), 148 'permlink_mode' => array( 149 'column' => 'txp_section.permlink_mode', 150 'label' => gTxt('permlink_mode'), 151 ), 152 'on_frontpage' => array( 153 'column' => 'txp_section.on_frontpage', 154 'label' => gTxt('on_front_page'), 155 'type' => 'boolean', 156 ), 157 'in_rss' => array( 158 'column' => 'txp_section.in_rss', 159 'label' => gTxt('syndicate'), 160 'type' => 'boolean', 161 ), 162 'searchable' => array( 163 'column' => 'txp_section.searchable', 164 'label' => gTxt('include_in_search'), 165 'type' => 'boolean', 166 ), 167 ) 168 ); 169 170 $alias_yes = '1, Yes'; 171 $alias_no = '0, No'; 172 $search->setAliases('on_frontpage', array($alias_no, $alias_yes)); 173 $search->setAliases('in_rss', array($alias_no, $alias_yes)); 174 $search->setAliases('searchable', array($alias_no, $alias_yes)); 175 176 list($criteria, $crit, $search_method) = $search->getFilter(); 177 178 $search_render_options = array('placeholder' => 'search_sections'); 179 $total = safe_count('txp_section', $criteria); 180 181 $searchBlock = 182 n.tag( 183 $search->renderForm('sec_section', $search_render_options), 184 'div', array( 185 'class' => 'txp-layout-4col-3span', 186 'id' => $event.'_control', 187 ) 188 ); 189 190 191 getDefaultSection(); 192 $createBlock = array(); 193 194 if (has_privs('section.edit')) { 195 $createBlock[] = 196 n.tag( 197 sLink('section', 'section_edit', gTxt('create_section'), 'txp-button'). 198 n.tag_start('form', array( 199 'class' => 'async', 200 'id' => 'default_section_form', 201 'name' => 'default_section_form', 202 'method' => 'post', 203 'action' => 'index.php', 204 )). 205 tag(gTxt('default_write_section'), 'label', array('for' => 'default_section')). 206 popHelp('section_default'). 207 section_select_list(). 208 eInput('section'). 209 sInput('section_set_default'). 210 n.tag_end('form'), 211 'div', array('class' => 'txp-control-panel') 212 ); 213 } 214 215 $paginator = new \Textpattern\Admin\Paginator(); 216 $limit = $step == 'section_select_skin' ? PHP_INT_MAX : $paginator->getLimit(); 217 218 list($page, $offset, $numPages) = pager($total, $limit, $page); 219 220 $createBlock = implode(n, $createBlock); 221 $contentBlock = ''; 222 223 if ($total < 1) { 224 if ($crit !== '') { 225 $contentBlock .= graf( 226 span(null, array('class' => 'ui-icon ui-icon-info')).' '. 227 gTxt('no_results_found'), 228 array('class' => 'alert-block information') 229 ); 230 } 231 } else { 232 $rs = safe_rows_start( 233 "*, (SELECT COUNT(*) FROM ".safe_pfx_j('textpattern')." WHERE textpattern.Section = txp_section.name) AS article_count", 234 'txp_section', 235 "$criteria ORDER BY $sort_sql LIMIT $offset, $limit" 236 ); 237 238 if ($rs) { 239 $dev_set = false; 240 $dev_preview = get_pref('enable_dev_preview') && has_privs('skin.edit'); 241 $contentBlock .= n.tag_start('form', array( 242 'class' => 'multi_edit_form', 243 'id' => 'section_form', 244 'name' => 'longform', 245 'method' => 'post', 246 'action' => 'index.php', 247 )). 248 n.tag_start('div', array( 249 'class' => 'txp-listtables', 250 'tabindex' => 0, 251 'aria-label' => gTxt('list'), 252 )). 253 n.tag_start('table', array('class' => 'txp-list')). 254 n.tag_start('thead'); 255 $thead = hCell( 256 fInput('checkbox', 'select_all', 0, '', '', '', '', '', 'select_all'), 257 '', ' class="txp-list-col-multi-edit" scope="col" title="'.gTxt('toggle_all_selected').'"' 258 ); 259 260 foreach ($columns as $column => $label) { 261 $thead .= column_head( 262 $label, $column, 'section', true, $switch_dir, $crit, $search_method, 263 (($column == $sort) ? "$dir " : '').'txp-list-col-'.$column 264 ); 265 } 266 267 $contentBlock .= tr($thead). 268 n.tag_end('thead'). 269 n.tag_start('tbody'); 270 271 while ($a = nextRow($rs)) { 272 extract($a, EXTR_PREFIX_ALL, 'sec'); 273 274 $edit_url = array( 275 'event' => 'section', 276 'step' => 'section_edit', 277 'name' => $sec_name, 278 'sort' => $sort, 279 'dir' => $dir, 280 'page' => $page, 281 'search_method' => $search_method, 282 'crit' => $crit, 283 ); 284 285 if ($sec_name == 'default') { 286 $articles = $sec_searchable = $sec_in_rss = $sec_on_frontpage = '-'; 287 } else { 288 $sec_on_frontpage = asyncHref(yes_no($sec_on_frontpage), array( 289 'step' => 'section_toggle_option', 290 'thing' => $sec_name, 291 'property' => 'on_frontpage', 292 )); 293 294 $sec_in_rss = asyncHref(yes_no($sec_in_rss), array( 295 'step' => 'section_toggle_option', 296 'thing' => $sec_name, 297 'property' => 'in_rss', 298 )); 299 300 $sec_searchable = asyncHref(yes_no($sec_searchable), array( 301 'step' => 'section_toggle_option', 302 'thing' => $sec_name, 303 'property' => 'searchable', 304 )); 305 306 if ($sec_article_count > 0) { 307 $articles = href($sec_article_count, array( 308 'event' => 'list', 309 'search_method' => 'section', 310 'crit' => '"'.$sec_name.'"', 311 ), array( 312 'title' => gTxt('article_count', array('{num}' => $sec_article_count)), 313 )); 314 } else { 315 $articles = 0; 316 } 317 } 318 319 $has_dev_skin = !empty($sec_dev_skin) && $sec_dev_skin !== $sec_skin; 320 !empty($sec_dev_skin) or $sec_dev_skin = $sec_skin; 321 !empty($sec_dev_page) or $sec_dev_page = $sec_page; 322 !empty($sec_dev_css) or $sec_dev_css = $sec_css; 323 324 $in_dev = false; 325 326 foreach (array('page', 'css') as $item) { 327 $all_items = $item === 'page' ? $all_pages : $all_styles; 328 $sec_item = ${"sec_$item"}; 329 $sec_dev_item = ${"sec_dev_$item"}; 330 331 $missing = $sec_dev_item && isset($all_items[$sec_dev_skin]) && !in_array($sec_dev_item, $all_items[$sec_dev_skin]); 332 $replaced = $dev_preview && ($has_dev_skin && $sec_dev_item || $sec_item != $sec_dev_item || $sec_dev_item && $missing) ? 'disabled' : false; 333 $dev_set = $dev_set || $replaced; 334 $in_dev = $in_dev || $replaced; 335 336 ${"sec_$item"} = ($sec_item ? tag(href(txpspecialchars($sec_item), array( 337 'event' => $item, 338 'name' => $sec_item, 339 'skin' => $sec_skin, 340 ), array('title' => gTxt('edit')) 341 ), $replaced ? 'span' : null, $replaced ? array('class' => 'secondary-text') : '') : tag(gTxt('none'), 'span', array('class' => 'disabled'))). 342 ($replaced ? 343 n.'<hr class="secondary" />'.n. 344 href(txpspecialchars($sec_dev_item), array( 345 'event' => $item, 346 'name' => $sec_dev_item, 347 'skin' => $sec_dev_skin, 348 ), array('title' => gTxt('edit'))). 349 ($missing ? sp.tag(gTxt('status_missing'), 'small', array('class' => 'alert-block alert-pill error')) : '') 350 : ''); 351 } 352 353 $replaced = $dev_preview && ($sec_skin != $sec_dev_skin) ? 'disabled' : false; 354 $dev_set = $dev_set || $replaced; 355 $in_dev = $in_dev || $replaced; 356 357 $contentBlock .= tr( 358 td( 359 fInput('checkbox', 'selected[]', $sec_name), '', 'txp-list-col-multi-edit' 360 ). 361 hCell( 362 href( 363 txpspecialchars($sec_name), $edit_url, array('title' => gTxt('edit')) 364 ). 365 span( 366 sp.span('|', array('role' => 'separator')). 367 sp.href(gTxt('view'), pagelinkurl(array('s' => $sec_name), null, $sec_permlink_mode)), 368 array('class' => 'txp-option-link') 369 ). 370 ($in_dev ? n.'<hr class="secondary" />'.n.tag(gTxt('dev_theme'), 'small', array('class' => 'alert-block alert-pill warning')) : ''), '', array( 371 'class' => 'txp-list-col-name', 372 'scope' => 'row', 373 ) 374 ). 375 td( 376 txpspecialchars($sec_title), '', 'txp-list-col-title' 377 ). 378 td( 379 tag($sec_skin, $replaced ? 'span' : null, $replaced ? array('class' => 'secondary-text') : '').($replaced ? n.'<hr class="secondary" />'.n.$sec_dev_skin : ''), 380 '', 'txp-list-col-skin' 381 ). 382 td( 383 $sec_page, '', 'txp-list-col-page' 384 ). 385 td( 386 $sec_css, '', 'txp-list-col-style' 387 ). 388 td( 389 $sec_permlink_mode ? gTxt($sec_permlink_mode) : '<span class="secondary-text">'.gTxt(get_pref('permlink_mode')).'</span>', '', 'txp-list-col-permlink_mode' 390 ). 391 td( 392 $sec_on_frontpage, '', 'txp-list-col-on_frontpage' 393 ). 394 td( 395 $sec_in_rss, '', 'txp-list-col-in_rss' 396 ). 397 td( 398 $sec_searchable, '', 'txp-list-col-searchable' 399 ). 400 td( 401 $articles, '', 'txp-list-col-article_count' 402 ), 403 array('id' => 'txp_section_'.$sec_name) 404 ); 405 } 406 407 $disabled = $dev_set ? array() : array('switchdevlive'); 408 409 $contentBlock .= n.tag_end('tbody'). 410 n.tag_end('table'). 411 n.tag_end('div'). // End of .txp-listtables. 412 section_multiedit_form($page, $sort, $dir, $crit, $search_method, $disabled). 413 tInput(). 414 n.tag_end('form'); 415 } 416 } 417 418 $pageBlock = $paginator->render(). 419 nav_form('section', $page, $numPages, $sort, $dir, $crit, $search_method, $total, $limit); 420 421 $table = new \Textpattern\Admin\Table($event); 422 echo $table->render(compact('total', 'crit') + array('heading' => 'tab_sections'), $searchBlock, $createBlock, $contentBlock, $pageBlock); 423 } 424 425 /** 426 * Renders and outputs the section editor panel. 427 */ 428 429 function section_edit() 430 { 431 global $event, $step, $all_skins, $all_pages, $all_styles; 432 433 require_privs('section.edit'); 434 435 extract(gpsa(array( 436 'page', 437 'sort', 438 'dir', 439 'crit', 440 'search_method', 441 'name', 442 ))); 443 444 $is_edit = ($name && $step == 'section_edit'); 445 $caption = gTxt('create_section'); 446 $is_default_section = false; 447 448 if ($is_edit) { 449 $rs = safe_row( 450 "*", 451 'txp_section', 452 "name = '".doSlash($name)."'" 453 ); 454 455 if ($name == 'default') { 456 $caption = gTxt('edit_default_section'); 457 $is_default_section = true; 458 } else { 459 $caption = gTxt('edit_section'); 460 } 461 } else { 462 // Pulls defaults for the new section from the 'default'. 463 $rs = safe_row( 464 "skin, page, css, on_frontpage, in_rss, searchable", 465 'txp_section', 466 "name = 'default'" 467 ); 468 469 if ($rs) { 470 $rs['name'] = $rs['title'] = $rs['description'] = $rs['permlink_mode'] = ''; 471 } 472 } 473 474 if (!$rs) { 475 sec_section_list(array(gTxt('unknown_section'), E_ERROR)); 476 477 return; 478 } 479 480 extract($rs, EXTR_PREFIX_ALL, 'sec'); 481 pagetop(gTxt('tab_sections')); 482 483 $out = array(); 484 485 $out[] = hed($caption, 2); 486 487 if ($is_default_section) { 488 $out[] = hInput('name', 'default'); 489 } else { 490 $out[] = inputLabel( 491 'section_name', 492 fInput('text', 'name', $sec_name, '', '', '', INPUT_REGULAR, '', 'section_name', false, true), 493 'section_name', '', array('class' => 'txp-form-field edit-section-name') 494 ). 495 inputLabel( 496 'section_title', 497 fInput('text', 'title', $sec_title, '', '', '', INPUT_REGULAR, '', 'section_title'), 498 'section_longtitle', '', array('class' => 'txp-form-field edit-section-longtitle') 499 ); 500 } 501 502 $pageSelect = selectInput(array('name' => 'section_page', 'required' => false), array(), '', '', '', 'section_page'); 503 $styleSelect = selectInput(array('name' => 'css', 'required' => false), array(), '', '', '', 'section_css'); 504 $json_page = json_encode($all_pages, TEXTPATTERN_JSON); 505 $json_style = json_encode($all_styles, TEXTPATTERN_JSON); 506 507 $out[] = 508 inputLabel( 509 'section_skin', 510 selectInput('skin', $all_skins, $sec_skin, '', '', 'section_skin'), 511 'uses_skin', 512 'section_uses_skin', 513 array('class' => 'txp-form-field edit-section-uses-skin') 514 ). 515 inputLabel( 516 'section_page', 517 $pageSelect, 518 'uses_page', 519 'section_uses_page', 520 array('class' => 'txp-form-field edit-section-uses-page') 521 ). 522 inputLabel( 523 'section_css', 524 $styleSelect, 525 'uses_style', 526 'section_uses_css', 527 array('class' => 'txp-form-field edit-section-uses-css') 528 ). 529 inputLabel( 530 'permlink_mode', 531 permlinkmodes('permlink_mode', $is_default_section ? get_pref('permlink_mode') : $sec_permlink_mode, $is_default_section ? false : array('' => gTxt('default'))), 532 'permlink_mode', 533 'permlink_mode', 534 array('class' => 'txp-form-field edit-section-permlink-mode') 535 ). 536 script_js(<<<EOJS 537 var skin_page = {$json_page}; 538 var skin_style = {$json_style}; 539 var page_sel = '{$sec_page}'; 540 var style_sel = '{$sec_css}'; 541 EOJS 542 ); 543 544 if (!$is_default_section) { 545 $out[] = inputLabel( 546 'on_front_page', 547 yesnoradio('on_frontpage', $sec_on_frontpage, '', $sec_name), 548 '', 'section_on_frontpage', array('class' => 'txp-form-field edit-section-on-frontpage') 549 ). 550 inputLabel( 551 'syndicate', 552 yesnoradio('in_rss', $sec_in_rss, '', $sec_name), 553 '', 'section_syndicate', array('class' => 'txp-form-field edit-section-syndicate') 554 ). 555 inputLabel( 556 'include_in_search', 557 yesnoradio('searchable', $sec_searchable, '', $sec_name), 558 '', 'section_searchable', array('class' => 'txp-form-field edit-section-searchable') 559 ); 560 } 561 562 $out[] = inputLabel( 563 'section_description', 564 '<textarea id="section_description" name="description" cols="'.INPUT_LARGE.'" rows="'.TEXTAREA_HEIGHT_SMALL.'">'.$sec_description.'</textarea>', 565 'description', 'section_description', array('class' => 'txp-form-field txp-form-field-textarea edit-section-description') 566 ); 567 568 $out[] = pluggable_ui('section_ui', 'extend_detail_form', '', $rs). 569 graf( 570 sLink('section', '', gTxt('cancel'), 'txp-button'). 571 fInput('submit', '', gTxt('save'), 'publish'), 572 array('class' => 'txp-edit-actions') 573 ). 574 eInput('section'). 575 sInput('section_save'). 576 hInput('old_name', $sec_name). 577 hInput('search_method', $search_method). 578 hInput('crit', $crit). 579 hInput('page', $page). 580 hInput('sort', $sort). 581 hInput('dir', $dir); 582 583 echo form(join('', $out), '', '', 'post', 'txp-edit', '', 'section_details'); 584 } 585 586 /** 587 * Saves a section. 588 */ 589 590 function section_save() 591 { 592 $in = array_map('assert_string', psa(array( 593 'name', 594 'title', 595 'skin', 596 'description', 597 'old_name', 598 'section_page', 599 'css', 600 'permlink_mode', 601 ))); 602 603 if (empty($in['title'])) { 604 $in['title'] = $in['name']; 605 } 606 607 // Prevent non-URL characters on section names. 608 $mbstrings = extension_loaded('mbstrings'); 609 $in['name'] = $mbstrings ? 610 mb_strtolower(sanitizeForUrl($in['name']), 'UTF-8') : 611 strtolower(sanitizeForUrl($in['name'])); 612 613 extract($in); 614 615 $in = doSlash($in); 616 extract($in, EXTR_PREFIX_ALL, 'safe'); 617 $lower_name = $mbstrings ? 618 mb_strtolower($old_name, 'UTF-8') : 619 strtolower($old_name); 620 621 if ($name != $lower_name) { 622 if (safe_field("name", 'txp_section', "name = '$safe_name'")) { 623 // Invalid input. Halt all further processing (e.g. plugin event 624 // handlers). 625 $message = array(gTxt('section_name_already_exists', array('{name}' => $name)), E_ERROR); 626 sec_section_list($message); 627 628 return; 629 } 630 } 631 632 $ok = false; 633 634 if ($name == 'default') { 635 $on_frontpage = $in_rss = $searchable = 0; 636 637 $ok = safe_update('txp_section', "skin = '$safe_skin', page = '$safe_section_page', css = '$safe_css', description = '$safe_description'", "name = 'default'"); 638 set_pref('permlink_mode', $permlink_mode); 639 } elseif ($name) { 640 extract(array_map('assert_int', psa(array('on_frontpage', 'in_rss', 'searchable')))); 641 642 if ($safe_old_name) { 643 $ok = safe_update('txp_section', " 644 name = '$safe_name', 645 title = '$safe_title', 646 skin = '$safe_skin', 647 page = '$safe_section_page', 648 css = '$safe_css', 649 description = '$safe_description', 650 permlink_mode = '$safe_permlink_mode', 651 on_frontpage = '$on_frontpage', 652 in_rss = '$in_rss', 653 searchable = '$searchable' 654 ", "name = '$safe_old_name'"); 655 656 // Manually maintain referential integrity. 657 if ($ok) { 658 $ok = safe_update('textpattern', "Section = '$safe_name'", "Section = '$safe_old_name'"); 659 } 660 } else { 661 $ok = safe_insert('txp_section', " 662 name = '$safe_name', 663 title = '$safe_title', 664 skin = '$safe_skin', 665 page = '$safe_section_page', 666 css = '$safe_css', 667 description = '$safe_description', 668 permlink_mode = '$safe_permlink_mode', 669 on_frontpage = '$on_frontpage', 670 in_rss = '$in_rss', 671 searchable = '$searchable'"); 672 } 673 } 674 675 if ($ok) { 676 if ($name != $lower_name && $lower_name == get_pref('default_section')) { 677 set_pref('default_section', $name, 'section', PREF_HIDDEN); 678 } 679 update_lastmod('section_saved', compact('name', 'title', 'section_page', 'css', 'description', 'on_frontpage', 'in_rss', 'searchable', 'permlink_mode')); 680 Txp::get('Textpattern\Skin\Skin')->setEditing($safe_skin); 681 } 682 683 if ($ok) { 684 sec_section_list(gTxt(($safe_old_name ? 'section_updated' : 'section_created'), array('{name}' => $name)), true); 685 } else { 686 sec_section_list(array(gTxt('section_save_failed'), E_ERROR)); 687 } 688 } 689 690 /** 691 * Changes and saves the pageby value. 692 */ 693 694 function section_change_pageby() 695 { 696 Txp::get('\Textpattern\Admin\Paginator')->change(); 697 sec_section_list(); 698 } 699 700 /** 701 * Toggles section yes/no parameters. 702 * 703 * This function requires three HTTP POST parameters: 'column', 'value' and 704 * 'name'. The 'value' is the new value, localised 'Yes' or 'No', 705 * 'name' is the section and the 'column' is the altered setting, 706 * either 'on_frontpage', 'in_rss' or 'searchable'. 707 * 708 * Outputs a text/plain response comprising the new displayable 709 * value for the toggled parameter. 710 */ 711 712 function section_toggle_option() 713 { 714 extract(psa(array( 715 'property', 716 'value', 717 'thing', 718 ))); 719 720 $value = (int) ($value === gTxt('no')); 721 722 if (in_array($property, array('on_frontpage', 'in_rss', 'searchable'))) { 723 if (safe_update('txp_section', "$property = $value", "name = '".doSlash($thing)."'")) { 724 echo yes_no($value); 725 726 return; 727 } 728 } 729 730 trigger_error(gTxt('section_save_failed'), E_USER_ERROR); 731 } 732 733 /** 734 * Sets a section as the default. 735 */ 736 737 function section_set_default() 738 { 739 extract(psa(array('default_section'))); 740 741 $exists = safe_row("name", 'txp_section', "name = '".doSlash($default_section)."'"); 742 743 if ($exists && set_pref('default_section', $default_section, 'section', PREF_HIDDEN)) { 744 send_script_response(announce(gTxt('default_section_updated'))); 745 746 return; 747 } 748 749 send_script_response(announce(gTxt('section_save_failed'), E_ERROR)); 750 } 751 752 /** 753 * Renders a 'default_section' <select> input listing all sections. 754 * 755 * Used for changing the default section. 756 * 757 * @return string HTML 758 */ 759 760 function section_select_list() 761 { 762 global $txp_sections; 763 764 $val = get_pref('default_section'); 765 $vals = array(); 766 767 foreach ($txp_sections as $name => $row) { 768 $name == 'default' or $vals[$name] = $row['title']; 769 } 770 771 return selectInput(array( 772 'name' => 'default_section', 'class' => 'txp-async-update' 773 ), $vals, $val, false, true, 'default_section'); 774 } 775 776 /** 777 * Processes delete actions sent using the multi-edit form. 778 */ 779 780 function section_delete() 781 { 782 global $txp_sections; 783 784 $selectedList = ps('selected'); 785 $selected = join(',', quote_list($selectedList)); 786 $message = ''; 787 788 $sections = safe_column( 789 "name", 790 'txp_section', 791 "name != 'default' AND name IN ($selected) AND name NOT IN (SELECT Section FROM ".safe_pfx('textpattern').")" 792 ); 793 794 $sectionsNotDeleted = array_diff($selectedList, $sections); 795 796 if ($sections && safe_delete('txp_section', "name IN (".join(',', quote_list($sections)).")")) { 797 foreach ($sections as $section) { 798 unset($txp_sections[$section]); 799 } 800 801 callback_event('sections_deleted', '', 0, $sections); 802 $message = gTxt('section_deleted', array('{name}' => join(', ', $sections))); 803 } 804 805 if ($sectionsNotDeleted) { 806 $severity = ($message) ? E_WARNING : E_ERROR; 807 $message = array(($message ? $message.n : '').gTxt('section_delete_failure', array('{name}' => join(', ', $sectionsNotDeleted))), $severity); 808 } 809 810 sec_section_list($message); 811 } 812 813 /** 814 * Processes theme preview actions. 815 */ 816 817 function section_set_theme($type = 'dev_skin') 818 { 819 global $all_skins, $all_pages, $all_styles; 820 821 $skin = gps('skin'); 822 $message = ''; 823 824 if (isset($all_skins[$skin]) && has_privs('skin.edit')) { 825 safe_update( 826 'txp_section', 827 "$type = '".doSlash($skin)."'", 828 $type == 'dev_skin' ? '1' : 'page IN ('.join(',', quote_list($all_pages[$skin])).') AND css IN ('.join(',', quote_list($all_styles[$skin])).')' 829 ); 830 $message = gTxt($type == 'dev_skin' ? 'dev_theme' : 'live_theme').': '.txpspecialchars($all_skins[$skin]); 831 832 if ($type == 'dev_skin') { 833 Txp::get('Textpattern\Skin\Skin')->setName($skin)->setEditing(); 834 } 835 } 836 837 script_js(<<<EOS 838 if (typeof window.history.replaceState == 'function') {history.replaceState({}, '', '?event=section')} 839 EOS 840 , false); 841 sec_section_list($message, true); 842 } 843 844 /** 845 * Renders a multi-edit form widget. 846 * 847 * @param int $page The page number 848 * @param string $sort The current sorting value 849 * @param string $dir The current sorting direction 850 * @param string $crit The current search criteria 851 * @param string $search_method The current search method 852 * @return string HTML 853 */ 854 855 function section_multiedit_form($page, $sort, $dir, $crit, $search_method, $disabled = array()) 856 { 857 global $all_skins, $all_pages, $all_styles, $step; 858 859 $json_page = json_encode($all_pages, TEXTPATTERN_JSON); 860 $json_style = json_encode($all_styles, TEXTPATTERN_JSON); 861 862 $themeSelect = inputLabel( 863 'multiedit_skin', 864 selectInput('skin', $all_skins, gps('skin'), false, '', 'multiedit_skin'), 865 'skin', '', array('class' => 'multi-option multi-step'), '' 866 ); 867 868 $pageSelect = inputLabel( 869 'multiedit_page', 870 selectInput('section_page', array(), '', '', '', 'multiedit_page'), 871 'page', '', array('class' => 'multi-option multi-step'), '' 872 ); 873 874 $styleSelect = inputLabel( 875 'multiedit_css', 876 selectInput('css', array(), '', '', '', 'multiedit_css'), 877 'css', '', array('class' => 'multi-option multi-step'), '' 878 ); 879 880 $devThemeSelect = inputLabel( 881 'multiedit_skin', 882 selectInput('dev_skin', $all_skins, '', false, '', 'multiedit_dev_skin'), 883 'skin', '', array('class' => 'multi-option multi-step'), '' 884 ); 885 886 $devPageSelect = inputLabel( 887 'multiedit_page', 888 selectInput('dev_page', array(), '', '', '', 'multiedit_dev_page'), 889 'page', '', array('class' => 'multi-option multi-step'), '' 890 ); 891 892 $devStyleSelect = inputLabel( 893 'multiedit_css', 894 selectInput('dev_css', array(), '', '', '', 'multiedit_dev_css'), 895 'css', '', array('class' => 'multi-option multi-step'), '' 896 ); 897 898 $dev_preview = get_pref('enable_dev_preview') && has_privs('skin.edit'); 899 900 $methods = array( 901 'changepagestyle' => array( 902 'label' => gTxt('change_page_style'), 903 'html' => (!$dev_preview ? 904 hInput('live_theme', 1) : 905 inputLabel('dev_theme', 906 checkbox2('dev_theme', 1, 0, 'dev_theme'), 907 'dev_theme', '', array('class' => 'multi-option multi-step'), '' 908 ) . inputLabel('live_theme', 909 checkbox2('live_theme', 0, 0, 'live_theme'), 910 'live_theme', '', array('class' => 'multi-option multi-step'), '' 911 ) 912 ) . $themeSelect . $pageSelect . $styleSelect 913 ), 914 'switchdevlive' => array( 915 'label' => gTxt('switch_dev_live'), 916 'html' => radioSet(array( 917 0 => gTxt('live_to_dev'), 918 1 => gTxt('dev_to_live'), 919 ), 'switch_dev_live', 0), 920 ), 921 'permlinkmode' => array( 922 'label' => gTxt('permlink_mode'), 923 'html' => permlinkmodes('permlink_mode', '', array('' => gTxt('default'))), 924 ), 925 'changeonfrontpage' => array( 926 'label' => gTxt('on_front_page'), 927 'html' => yesnoRadio('on_frontpage', 1), 928 ), 929 'changesyndicate' => array( 930 'label' => gTxt('syndicate'), 931 'html' => yesnoRadio('in_rss', 1), 932 ), 933 'changesearchable' => array( 934 'label' => gTxt('include_in_search'), 935 'html' => yesnoRadio('searchable', 1), 936 ), 937 'delete' => gTxt('delete'), 938 ); 939 940 foreach ($disabled as $method) { 941 unset($methods[$method]); 942 } 943 944 $script = <<<EOJS 945 var skin_page = {$json_page}; 946 var skin_style = {$json_style}; 947 var page_sel = null; 948 var style_sel = null; 949 EOJS; 950 951 if ($step == 'section_select_skin') { 952 $script .= <<<EOJS 953 $(function() { 954 $('#select_all').click(); 955 $('[name="edit_method"]').val('changepagestyle').change(); 956 var skin = $('#multiedit_skin'); 957 var selected = skin.find('option[selected]').val(); 958 skin.val(selected || '').change(); 959 }); 960 EOJS; 961 } 962 return multi_edit($methods, 'section', 'section_multi_edit', $page, $sort, $dir, $crit, $search_method). 963 script_js($script, false); 964 } 965 966 /** 967 * Processes multi-edit actions. 968 */ 969 970 function section_multi_edit() 971 { 972 global $txp_user, $all_skins, $all_pages, $all_styles; 973 974 extract(psa(array( 975 'edit_method', 976 'selected', 977 ))); 978 979 if (!$selected || !is_array($selected)) { 980 return sec_section_list(); 981 } 982 983 $nameVal = array(); 984 985 switch ($edit_method) { 986 case 'delete': 987 return section_delete(); 988 break; 989 case 'changepagestyle': 990 if (ps('live_theme')) { 991 $nameVal += array( 992 'skin' => ps('skin'), 993 'page' => ps('section_page'), 994 'css' => ps('css'), 995 ); 996 } 997 998 if (ps('dev_theme')) { 999 $nameVal += array( 1000 'dev_skin' => ps('skin'), 1001 'dev_page' => ps('section_page'), 1002 'dev_css' => ps('css'), 1003 ); 1004 } 1005 1006 break; 1007 case 'switchdevlive': 1008 $nameVal['switch_dev_live'] = (int) ps('switch_dev_live'); 1009 break; 1010 case 'permlinkmode': 1011 $nameVal['permlink_mode'] = (string) ps('permlink_mode'); 1012 break; 1013 case 'changeonfrontpage': 1014 $nameVal['on_frontpage'] = (int) ps('on_frontpage'); 1015 break; 1016 case 'changesyndicate': 1017 $nameVal['in_rss'] = (int) ps('in_rss'); 1018 break; 1019 case 'changesearchable': 1020 $nameVal['searchable'] = (int) ps('searchable'); 1021 break; 1022 } 1023 1024 $setskin = "IF(dev_skin > '', dev_skin, skin)"; 1025 $setpage = "IF(dev_page > '', dev_page, page)"; 1026 $setcss = "IF(dev_css > '', dev_css, css)"; 1027 1028 $filter = array("name IN (".join(',', quote_list($selected)).")"); 1029 $message = ''; 1030 1031 if ($edit_method === 'changepagestyle' && !empty($nameVal['skin'])) { 1032 $skin = $nameVal['skin']; 1033 1034 if (empty($nameVal['page'])) { 1035 $filter[] = empty($all_pages[$skin]) ? 1036 '0' : 1037 "page IN (".join(',', quote_list($all_pages[$skin])).")"; 1038 } 1039 1040 if (empty($nameVal['css'])) { 1041 $filter[] = empty($all_styles[$skin]) ? 1042 '0' : 1043 "css IN (".join(',', quote_list($all_styles[$skin])).")"; 1044 } 1045 } elseif ($edit_method === 'switchdevlive' && empty($nameVal['switch_dev_live'])) { 1046 $skinset = array(); 1047 1048 foreach ($all_skins as $skin => $title) { 1049 $skinset[] = "$setskin = '".doSlash($skin)."' AND ($setpage = '' OR ". 1050 (empty($all_pages[$skin]) ? 1051 '0' : 1052 "$setpage IN (".join(',', quote_list($all_pages[$skin]))."))" 1053 )." AND ($setcss = '' OR ". 1054 (empty($all_styles[$skin]) ? 1055 '0' : 1056 "$setcss IN (".join(',', quote_list($all_styles[$skin]))."))" 1057 ); 1058 } 1059 1060 $filter[] = '('.implode(' OR ', $skinset).')'; 1061 } 1062 1063 $sections = safe_column( 1064 "name", 1065 'txp_section', 1066 implode(' AND ', $filter) 1067 ); 1068 1069 if ($nameVal && $sections) { 1070 if ($edit_method == 'switchdevlive') { 1071 $set = ($nameVal['switch_dev_live'] ? '' : 1072 "skin = $setskin, 1073 page = $setpage, 1074 css = $setcss, " 1075 )."dev_skin = '', dev_page = '', dev_css = ''"; 1076 } elseif ($edit_method == 'permlinkmode') { 1077 $set = "permlink_mode = IF(name='default', '', '".doSlash($nameVal['permlink_mode'])."')"; 1078 1079 if ($nameVal['permlink_mode'] && in_array('default', $sections)) { 1080 set_pref('permlink_mode', $nameVal['permlink_mode']); 1081 } 1082 } else { 1083 $in = array(); 1084 1085 foreach ($nameVal as $key => $val) { 1086 if ((string)$val != '*') { 1087 $in[] = "{$key} = '".doSlash($val)."'"; 1088 } 1089 } 1090 1091 $set = implode(',', $in); 1092 } 1093 1094 if ($set && 1095 safe_update( 1096 'txp_section', 1097 $set, 1098 "name IN (".join(',', quote_list($sections)).")" 1099 ) 1100 ) { 1101 $message = gTxt('section_updated', array('{name}' => join(', ', $sections))); 1102 1103 if ($edit_method === 'changepagestyle') { 1104 Txp::get('Textpattern\Skin\Skin')->setEditing(doSlash($nameVal['skin'])); 1105 } 1106 } 1107 } else { 1108 $message = array(gTxt('section_save_failed'), E_ERROR); 1109 } 1110 1111 sec_section_list($message, $nameVal && $sections); 1112 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title