Textpattern | PHP Cross Reference | Content Management Systems |
Description: Collection of HTML widgets.
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 * Collection of HTML widgets. 26 * 27 * @package HTML 28 */ 29 30 /** 31 * Renders the admin-side footer. 32 * 33 * The footer's default markup is provided by a theme. It can be further 34 * customised via the "admin_side > footer" pluggable UI callback event. 35 * 36 * In addition to the pluggable UI, this function also calls callback events 37 * "admin_side > main_content_end" and "admin_side > body_end". 38 */ 39 40 function end_page() 41 { 42 global $event, $app_mode, $theme, $textarray_script; 43 44 if ($app_mode != 'async' && $event != 'tag') { 45 callback_event('admin_side', 'main_content_end'); 46 echo n.'</main><!-- /txp-body -->'.n.'<footer class="txp-footer">'; 47 echo pluggable_ui('admin_side', 'footer', $theme->footer()); 48 callback_event('admin_side', 'body_end'); 49 echo script_js('vendors/PrismJS/prism/prism.js', TEXTPATTERN_SCRIPT_URL). 50 script_js('textpattern.textarray = '.json_encode($textarray_script, TEXTPATTERN_JSON), true). 51 n.'</footer><!-- /txp-footer -->'.n.'</body>'.n.'</html>'; 52 } 53 } 54 55 /** 56 * Renders the user interface for one head cell of columnar data. 57 * 58 * @param string $value Element text 59 * @param string $sort Sort criterion 60 * @param string $event Event name 61 * @param bool $is_link Include link to admin action in user interface according to the other params 62 * @param string $dir Sort direction, either "asc" or "desc" 63 * @param string $crit Search criterion 64 * @param string $method Search method 65 * @param string $class HTML "class" attribute applied to the resulting element 66 * @param string $step Step name 67 * @return string HTML 68 */ 69 70 function column_head($value, $sort = '', $event = '', $is_link = '', $dir = '', $crit = '', $method = '', $class = '', $step = 'list') 71 { 72 if (is_array($value)) { 73 extract($value); 74 } 75 76 $options = (isset($options) ? (array) $options : array()) + array( 77 'class' => $class, 78 'data-col' => $sort, 79 ); 80 81 $head_items = array( 82 'value' => $value, 83 'sort' => $sort, 84 'event' => $event, 85 'step' => $step, 86 'is_link' => $is_link, 87 'dir' => $dir, 88 'crit' => $crit, 89 'method' => $method, 90 ); 91 92 return column_multi_head(array($head_items), $options); 93 } 94 95 /** 96 * Renders the user interface for multiple head cells of columnar data. 97 * 98 * @param array $head_items An array of hashed elements. Valid keys: 'value', 'sort', 'event', 'is_link', 'dir', 'crit', 'method' 99 * @param string $class HTML "class" attribute applied to the resulting element 100 * @return string HTML 101 */ 102 103 function column_multi_head($head_items, $class = '') 104 { 105 $o = ''; 106 $first_item = true; 107 108 foreach ($head_items as $item) { 109 if (empty($item)) { 110 continue; 111 } 112 113 extract(lAtts(array( 114 'value' => '', 115 'sort' => '', 116 'event' => '', 117 'step' => 'list', 118 'is_link' => '', 119 'dir' => '', 120 'crit' => '', 121 'method' => '', 122 ), $item)); 123 124 $o .= ($first_item) ? '' : ', '; 125 $first_item = false; 126 127 if ($is_link) { 128 $o .= href(gTxt($value), array( 129 'event' => $event, 130 'step' => $step, 131 'sort' => $sort, 132 'dir' => $dir, 133 'crit' => $crit, 134 'search_method' => $method, 135 ), array()); 136 } else { 137 $o .= gTxt($value); 138 } 139 } 140 141 $extra_atts = is_array($class) ? $class : array('class' => $class); 142 143 return hCell($o, '', $extra_atts + array('scope' => 'col')); 144 } 145 146 /** 147 * Renders a <th> element. 148 * 149 * @param string $text Cell text 150 * @param string $caption Is not used 151 * @param string|array $atts HTML attributes 152 * @return string HTML 153 */ 154 155 function hCell($text = '', $caption = '', $atts = '') 156 { 157 $text = ('' === $text) ? sp : $text; 158 159 return n.tag($text, 'th', $atts); 160 } 161 162 /** 163 * Renders a link invoking an admin-side action. 164 * 165 * @param string $event Event 166 * @param string $step Step 167 * @param string $linktext Link text 168 * @param string $class HTML class attribute for link 169 * @return string HTML 170 */ 171 172 function sLink($event, $step, $linktext, $class = '') 173 { 174 if ($linktext === '') { 175 $linktext = null; 176 } 177 178 return href($linktext, array( 179 'event' => $event, 180 'step' => $step, 181 ), array('class' => $class)); 182 } 183 184 /** 185 * Renders a link with two additional URL parameters. 186 * 187 * Renders a link invoking an admin-side action while taking up to two 188 * additional URL parameters. 189 * 190 * @param string $event Event 191 * @param string $step Step 192 * @param string $thing URL parameter key #1 193 * @param string $value URL parameter value #1 194 * @param string $linktext Link text 195 * @param string $thing2 URL parameter key #2 196 * @param string $val2 URL parameter value #2 197 * @param string $title Anchor title 198 * @param string $class HTML class attribute 199 * @return string HTML 200 */ 201 202 function eLink($event, $step, $thing, $value, $linktext, $thing2 = '', $val2 = '', $title = '', $class = '') 203 { 204 if ($title) { 205 $title = gTxt($title); 206 } 207 208 if ($linktext === '') { 209 $linktext = null; 210 } else { 211 $linktext = escape_title($linktext); 212 } 213 214 return href($linktext, array( 215 'event' => $event, 216 'step' => $step, 217 $thing => $value, 218 $thing2 => $val2, 219 '_txp_token' => form_token(), 220 ), array( 221 'class' => $class, 222 'title' => $title, 223 )); 224 } 225 226 /** 227 * Renders a link with one additional URL parameter. 228 * 229 * Renders an link invoking an admin-side action while taking up to one 230 * additional URL parameter. 231 * 232 * @param string $event Event 233 * @param string $step Step 234 * @param string $thing URL parameter key 235 * @param string $value URL parameter value 236 * @param string $class HTML class attribute 237 * @return string HTML 238 */ 239 240 function wLink($event, $step = '', $thing = '', $value = '', $class = '') 241 { 242 return href(sp.'!'.sp, array( 243 'event' => $event, 244 'step' => $step, 245 $thing => $value, 246 '_txp_token' => form_token(), 247 ), array('class' => $class)); 248 } 249 250 /** 251 * Renders a delete link. 252 * 253 * Renders a link invoking an admin-side "delete" action while taking up to two 254 * additional URL parameters. 255 * 256 * @param string $event Event 257 * @param string $step Step 258 * @param string $thing URL parameter key #1 259 * @param string $value URL parameter value #1 260 * @param string $verify Show an "Are you sure?" dialogue with this text 261 * @param string $thing2 URL parameter key #2 262 * @param string $thing2val URL parameter value #2 263 * @param bool $get If TRUE, uses GET request 264 * @param array $remember Convey URL parameters for page state. Member sequence is $page, $sort, $dir, $crit, $search_method 265 * @return string HTML 266 */ 267 268 function dLink($event, $step, $thing, $value, $verify = '', $thing2 = '', $thing2val = '', $get = '', $remember = null) 269 { 270 if ($remember) { 271 list($page, $sort, $dir, $crit, $search_method) = $remember; 272 } 273 274 if ($get) { 275 if ($verify) { 276 $verify = gTxt($verify); 277 } else { 278 $verify = gTxt('confirm_delete_popup'); 279 } 280 281 if ($remember) { 282 return href(gTxt('delete'), array( 283 'event' => $event, 284 'step' => $step, 285 $thing => $value, 286 $thing2 => $thing2val, 287 '_txp_token' => form_token(), 288 'page' => $page, 289 'sort' => $sort, 290 'dir' => $dir, 291 'crit' => $crit, 292 'search_method' => $search_method, 293 ), array( 294 'class' => 'destroy ui-icon ui-icon-close', 295 'title' => gTxt('delete'), 296 'data-verify' => $verify, 297 )); 298 } 299 300 return href(gTxt('delete'), array( 301 'event' => $event, 302 'step' => $step, 303 $thing => $value, 304 $thing2 => $thing2val, 305 '_txp_token' => form_token(), 306 ), array( 307 'class' => 'destroy ui-icon ui-icon-close', 308 'title' => gTxt('delete'), 309 'data-verify' => $verify, 310 )); 311 } 312 313 return join('', array( 314 n.'<form method="post" action="index.php" data-verify="'.gTxt('confirm_delete_popup').'">', 315 tag( 316 span(gTxt('delete'), array('class' => 'ui-icon ui-icon-close')), 317 'button', 318 array( 319 'class' => 'destroy', 320 'type' => 'submit', 321 'title' => gTxt('delete'), 322 'aria-label' => gTxt('delete'), 323 ) 324 ), 325 eInput($event). 326 sInput($step), 327 hInput($thing, $value), 328 ($thing2) ? hInput($thing2, $thing2val) : '', 329 ($remember) ? hInput(compact('page', 'sort', 'dir', 'crit', 'search_method')) : '', 330 tInput(), 331 n.'</form>', 332 )); 333 } 334 335 /** 336 * Renders an add link. 337 * 338 * This function can be used for invoking an admin-side "add" action while 339 * taking up to two additional URL parameters. 340 * 341 * @param string $event Event 342 * @param string $step Step 343 * @param string $thing URL parameter key #1 344 * @param string $value URL parameter value #1 345 * @param string $thing2 URL parameter key #2 346 * @param string $value2 URL parameter value #2 347 * @return string HTML 348 */ 349 350 function aLink($event, $step, $thing = '', $value = '', $thing2 = '', $value2 = '') 351 { 352 return href('+', array( 353 'event' => $event, 354 'step' => $step, 355 $thing => $value, 356 $thing2 => $value2, 357 '_txp_token' => form_token(), 358 ), array('class' => 'alink')); 359 } 360 361 /** 362 * Renders a link invoking an admin-side "previous/next article" action. 363 * 364 * @param string $name Link text 365 * @param string $event Event 366 * @param string $step Step 367 * @param int $id ID of target Textpattern object (article,...) 368 * @param string $title HTML title attribute 369 * @param string $rel HTML rel attribute 370 * @return string HTML 371 */ 372 373 function prevnext_link($name, $event, $step, $id, $title = '', $rel = '') 374 { 375 return href($name, array( 376 'event' => $event, 377 'step' => $step, 378 'ID' => $id, 379 ), array( 380 'class' => 'navlink', 381 'title' => $title, 382 'rel' => $rel, 383 )); 384 } 385 386 /** 387 * Renders a link invoking an admin-side "previous/next page" action. 388 * 389 * @param string $event Event 390 * @param int $page Target page number 391 * @param string $label Link text 392 * @param string $type Direction, either "prev" or "next" 393 * @param string $sort Sort field 394 * @param string $dir Sort direction, either "asc" or "desc" 395 * @param string $crit Search criterion 396 * @param string $search_method Search method 397 * @param string $step Step 398 * @return string HTML 399 */ 400 401 function PrevNextLink($event, $page, $label, $type, $sort = '', $dir = '', $crit = '', $search_method = '', $step = 'list') 402 { 403 $theClass = ($type === 'next') ? 'ui-icon-arrowthick-1-e' : 'ui-icon-arrowthick-1-w'; 404 405 return href( 406 span( 407 $label, 408 array('class' => 'ui-icon '.$theClass) 409 ), 410 array( 411 'event' => $event, 412 'step' => $step, 413 'page' => (int) $page, 414 'dir' => $dir, 415 'crit' => $crit, 416 'search_method' => $search_method, 417 ), 418 array( 419 'rel' => $type, 420 'title' => $label, 421 'aria-label' => $label, 422 ) 423 ); 424 } 425 426 /** 427 * Renders a page navigation form. 428 * 429 * @param string $event Event 430 * @param int $page Current page number 431 * @param int $numPages Total pages 432 * @param string $sort Sort criterion 433 * @param string $dir Sort direction, either "asc" or "desc" 434 * @param string $crit Search criterion 435 * @param string $search_method Search method 436 * @param int $total Total search term hit count [0] 437 * @param int $limit First visible search term hit number [0] 438 * @param string $step Step 439 * @param int $list Number of displayed page links discounting jump links, previous and next 440 * @return string HTML 441 */ 442 443 function nav_form($event, $page, $numPages, $sort = '', $dir = '', $crit = '', $search_method = '', $total = 0, $limit = 0, $step = 'list', $list = 5) 444 { 445 $out = array(); 446 447 if ($numPages > 1 && $crit !== '') { 448 $out[] = announce( 449 gTxt('showing_search_results', array( 450 '{from}' => (($page - 1) * $limit) + 1, 451 '{to}' => min($total, $page * $limit), 452 '{total}' => $total, 453 )), 454 TEXTPATTERN_ANNOUNCE_REGULAR 455 ); 456 } 457 458 $nav = array(); 459 $list--; 460 $page = max(min($page, $numPages), 1); 461 462 $parameters = array( 463 'event' => $event, 464 'step' => $step, 465 'dir' => $dir, 466 'crit' => $crit, 467 'search_method' => $search_method, 468 ); 469 470 // Previous page. 471 if ($page > 1) { 472 $nav[] = n.PrevNextLink($event, $page - 1, gTxt('prev'), 'prev', $sort, $dir, $crit, $search_method, $step); 473 } else { 474 $nav[] = n.span( 475 span(gTxt('prev'), array('class' => 'ui-icon ui-icon-arrowthick-1-w')), 476 array( 477 'class' => 'disabled', 478 'aria-disabled' => 'true', 479 'aria-label' => gTxt('prev'), 480 ) 481 ); 482 } 483 484 485 $nav[] = form( 486 n.tag(gTxt('page'), 'label', array('for' => 'current-page')). 487 n.tag_void('input', array( 488 'class' => 'current-page', 489 'id' => 'current-page', 490 'name' => 'page', 491 'type' => 'text', 492 'size' => INPUT_XSMALL, 493 'inputmode' => 'numeric', 494 'pattern' => '[0-9]+', 495 'value' => $page, 496 )). 497 n.gTxt('of'). 498 n.span($numPages, array('class' => 'total-pages')). 499 eInput($event). 500 hInput(compact('sort', 'dir', 'crit', 'search_method')), 501 '', 502 '', 503 'get' 504 ); 505 506 // Next page. 507 if ($page < $numPages) { 508 $nav[] = n.PrevNextLink($event, $page + 1, gTxt('next'), 'next', $sort, $dir, $crit, $search_method, $step); 509 } else { 510 $nav[] = n.span( 511 span(gTxt('next'), array('class' => 'ui-icon ui-icon-arrowthick-1-e')), 512 array( 513 'class' => 'disabled', 514 'aria-disabled' => 'true', 515 'aria-label' => gTxt('next'), 516 ) 517 ); 518 } 519 520 $out[] = n.tag(join($nav).n, 'nav', array( 521 'class' => 'prev-next', 522 'aria-label' => gTxt('page_nav'), 523 'style' => ($numPages > 1 ? false : 'display:none'), 524 )); 525 526 return join('', $out); 527 } 528 529 /** 530 * Wraps a collapsible region and group structure around content. 531 * 532 * @param string $id HTML id attribute for the region wrapper and ARIA label 533 * @param string $content Content to wrap. If empty, only the outer wrapper will be rendered 534 * @param string $anchor_id HTML id attribute for the collapsible wrapper 535 * @param string $label L10n label name 536 * @param string $pane Pane reference for maintaining toggle state in prefs. Prefixed with 'pane_', suffixed with '_visible' 537 * @param string $class CSS class name to apply to wrapper 538 * @param string $help Help text item 539 * @return string HTML 540 * @since 4.6.0 541 */ 542 543 function wrapRegion($id, $content = '', $anchor_id = '', $label = '', $pane = '', $class = '', $help = '', $visible = null) 544 { 545 global $event; 546 $label = $label ? gTxt($label) : null; 547 548 if ($anchor_id && $pane) { 549 $heading_class = 'txp-summary'.($visible ? ' expanded' : ''); 550 $display_state = array( 551 'class' => 'toggle', 552 'id' => $anchor_id, 553 'role' => 'group', 554 'style' => $visible ? '' : 'display:none', 555 ); 556 557 $label = href($label, '#'.$anchor_id, array( 558 'role' => 'button', 559 'data-txp-token' => md5($pane.$event.form_token().get_pref('blog_uid')), 560 'data-txp-pane' => $pane, 561 )); 562 563 $help = ''; 564 } else { 565 $heading_class = ''; 566 $display_state = array('role' => 'group'); 567 } 568 569 if ($content) { 570 $content = 571 hed($label.popHelp($help), 3, array( 572 'class' => $heading_class, 573 'id' => $id.'-label', 574 )). 575 n.tag($content.n, 'div', $display_state).n; 576 } 577 578 return n.tag($content, 'section', array( 579 'class' => trim('txp-details '.$class), 580 'id' => $id, 581 'aria-labelledby' => $content ? $id.'-label' : '', 582 )); 583 } 584 585 /** 586 * Wraps a region and group structure around content. 587 * 588 * @param string $name HTML id attribute for the group wrapper and ARIA label 589 * @param string $content Content to wrap 590 * @param string $label L10n label name 591 * @param string $class CSS class name to apply to wrapper 592 * @param string $help Help text item 593 * @return string HTML 594 * @see wrapRegion() 595 * @since 4.6.0 596 */ 597 598 function wrapGroup($id, $content, $label, $class = '', $help = '') 599 { 600 return wrapRegion($id, $content, '', $label, '', $class, $help); 601 } 602 603 /** 604 * Renders start of a layout <table> element. 605 * 606 * @param string $id HTML id attribute 607 * @param string $align HTML align attribute 608 * @param string $class HTML class attribute 609 * @param int $p HTML cellpadding attribute 610 * @param int $w HTML width attribute 611 * @return string HTML 612 * @example 613 * startTable(). 614 * tr(td('column') . td('column')). 615 * tr(td('column') . td('column')). 616 * endTable(); 617 */ 618 619 function startTable($id = '', $align = '', $class = '', $p = 0, $w = 0) 620 { 621 $atts = join_atts(array( 622 'class' => $class, 623 'id' => $id, 624 'cellpadding' => (int) $p, 625 'width' => (int) $w, 626 'align' => $align, 627 ), TEXTPATTERN_STRIP_EMPTY); 628 629 return n.'<table'.$atts.'>'; 630 } 631 632 /** 633 * Renders closing </table> tag. 634 * 635 * @return string HTML 636 */ 637 638 function endTable() 639 { 640 return n.'</table>'; 641 } 642 643 /** 644 * Renders <tr> elements from input parameters. 645 * 646 * Takes a list of arguments containing each making a row. 647 * 648 * @return string HTML 649 * @example 650 * stackRows( 651 * td('cell') . td('cell'), 652 * td('cell') . td('cell') 653 * ); 654 */ 655 656 function stackRows() 657 { 658 foreach (func_get_args() as $a) { 659 $o[] = tr($a); 660 } 661 662 return join('', $o); 663 } 664 665 /** 666 * Renders a <td> element. 667 * 668 * @param string $content Cell content 669 * @param int $width HTML width attribute 670 * @param string $class HTML class attribute 671 * @param string $id HTML id attribute 672 * @return string HTML 673 */ 674 675 function td($content = '', $width = null, $class = '', $id = '') 676 { 677 $opts = array( 678 'class' => $class, 679 'id' => $id, 680 ); 681 682 if (is_numeric($width)) { 683 $opts['width'] = (int) $width; 684 } elseif (is_array($width)) { 685 $opts = array_merge($opts, $width); 686 } 687 688 return tda($content, $opts); 689 } 690 691 /** 692 * Renders a <td> element with attributes. 693 * 694 * @param string $content Cell content 695 * @param string|array $atts Cell attributes 696 * @return string HTML 697 */ 698 699 function tda($content, $atts = '') 700 { 701 $content = ($content === '') ? sp : $content; 702 703 return n.tag($content, 'td', $atts); 704 } 705 706 /** 707 * Renders a <td> element with attributes. 708 * 709 * This function is identical to tda(). 710 * 711 * @param string $content Cell content 712 * @param string|array $atts Cell attributes 713 * @return string HTML 714 * @access private 715 * @see tda() 716 */ 717 718 function tdtl($content, $atts = '') 719 { 720 return tda($content, $atts); 721 } 722 723 /** 724 * Renders a <tr> element with attributes. 725 * 726 * @param string $content Row content 727 * @param string|array $atts Row attributes 728 * @return string HTML 729 */ 730 731 function tr($content, $atts = '') 732 { 733 return n.tag($content, 'tr', $atts); 734 } 735 736 /** 737 * Renders a <td> element with top/left text orientation, colspan and 738 * other attributes. 739 * 740 * @param string $content Cell content 741 * @param int $span Cell colspan attribute 742 * @param int $width Cell width attribute 743 * @param string $class Cell class attribute 744 * @return string HTML 745 */ 746 747 function tdcs($content, $span, $width = null, $class = '') 748 { 749 $opts = array( 750 'class' => $class, 751 'colspan' => (int) $span, 752 ); 753 754 if (is_numeric($width)) { 755 $opts['width'] = (int) $width; 756 } 757 758 return tda($content, $opts); 759 } 760 761 /** 762 * Renders a <td> element with a rowspan attribute. 763 * 764 * @param string $content Cell content 765 * @param int $span Cell rowspan attribute 766 * @param int $width Cell width attribute 767 * @param string $class Cell class attribute 768 * @return string HTML 769 */ 770 771 function tdrs($content, $span, $width = null, $class = '') 772 { 773 $opts = array( 774 'class' => $class, 775 'rowspan' => (int) $span, 776 ); 777 778 if (is_numeric($width)) { 779 $opts['width'] = (int) $width; 780 } 781 782 return tda($content, $opts); 783 } 784 785 /** 786 * Renders a form label inside a table cell. 787 * 788 * @param string $text Label text 789 * @param string $help Help text 790 * @param string $label_id HTML "for" attribute, i.e. id of corresponding form element 791 * @return string HTML 792 */ 793 794 function fLabelCell($text, $help = '', $label_id = '') 795 { 796 $cell = gTxt($text).' '.popHelp($help); 797 798 if ($label_id) { 799 $cell = tag($cell, 'label', array('for' => $label_id)); 800 } 801 802 return tda($cell, array('class' => 'cell-label')); 803 } 804 805 /** 806 * Renders a form input inside a table cell. 807 * 808 * @param string $name HTML name attribute 809 * @param string $var Input value 810 * @param int $tabindex HTML tabindex attribute 811 * @param int $size HTML size attribute 812 * @param bool $help TRUE to display help link 813 * @param string $id HTML id attribute 814 * @return string HTML 815 */ 816 817 function fInputCell($name, $var = '', $tabindex = 0, $size = 0, $help = false, $id = '') 818 { 819 $pop = ($help) ? popHelp($name) : ''; 820 821 return tda(fInput('text', $name, $var, '', '', '', $size, $tabindex, $id).$pop); 822 } 823 824 /** 825 * Renders a name-value input control with label. 826 * 827 * The rendered input can be customised via the 828 * '{$event}_ui > inputlabel.{$name}' pluggable UI callback event. 829 * 830 * @param string $name Input name 831 * @param string $input Complete input control widget 832 * @param string|array $label Label text | array (label text, HTML block to append to label) 833 * @param string|array $help Help text item | array(help text item, inline help text) 834 * @param string|array $atts Class name | attribute pairs to assign to container div 835 * @param string|array $wraptag_val Tag to wrap the value / label in, or empty to omit 836 * @return string HTML 837 * @example 838 * echo inputLabel('active', yesnoRadio('active'), 'Keep active?'); 839 */ 840 841 function inputLabel($name, $input, $label = '', $help = array(), $atts = array(), $wraptag_val = array('div', 'div')) 842 { 843 global $event; 844 845 $arguments = compact('name', 'input', 'label', 'help', 'atts', 'wraptag_val'); 846 847 $fallback_class = 'txp-form-field edit-'.str_replace('_', '-', $name); 848 $tools = ''; 849 850 if ($atts && is_string($atts)) { 851 $atts = array('class' => $atts); 852 } elseif (!$atts) { 853 $atts = array('class' => $fallback_class); 854 } elseif (is_array($atts) && !array_key_exists('class', $atts)) { 855 $atts['class'] = $fallback_class; 856 } 857 858 if (!is_array($help)) { 859 $help = array($help); 860 } 861 862 if (is_array($label)) { 863 if (isset($label[1])) { 864 $tools = (string) $label[1]; 865 } 866 867 $label = (string) $label[0]; 868 } 869 870 if (empty($help)) { 871 $help = array( 872 0 => '', 873 1 => '', 874 ); 875 } 876 877 $inlineHelp = (isset($help[1])) ? $help[1] : ''; 878 879 if ($label !== '') { 880 $labelContent = tag(gTxt($label).popHelp($help[0]), 'label', array('for' => $name)).$tools; 881 } else { 882 $labelContent = gTxt($name).popHelp($help[0]).$tools; 883 } 884 885 if (!is_array($wraptag_val)) { 886 $wraptag_val = array($wraptag_val, $wraptag_val); 887 } 888 889 if ($wraptag_val[0]) { 890 $input = n.tag($input, $wraptag_val[0], array('class' => 'txp-form-field-value')); 891 } 892 893 if (isset($wraptag_val[1]) && $wraptag_val[1]) { 894 $labeltag = n.tag($labelContent, $wraptag_val[1], array('class' => 'txp-form-field-label')); 895 } else { 896 $labeltag = $labelContent; 897 } 898 899 $out = n.tag( 900 $labeltag. 901 fieldHelp($inlineHelp). 902 $input.n, 903 'div', 904 $atts 905 ); 906 907 return pluggable_ui($event.'_ui', 'inputlabel.'.$name, $out, $arguments); 908 } 909 910 /** 911 * Renders anything as an XML element. 912 * 913 * @param string $content Enclosed content 914 * @param string $tag The tag without brackets 915 * @param string|array $atts The element's HTML attributes 916 * @return string HTML 917 * @example 918 * echo tag('Link text', 'a', array('href' => '#', 'class' => 'warning')); 919 */ 920 921 function tag($content, $tag, $atts = '') 922 { 923 static $tags = array(); 924 925 if (empty($tag) || $content === '') { 926 return $content; 927 } 928 929 if (!isset($tags[$tag])) { 930 $tags[$tag] = preg_match('/^\w[\w\-\.\:]*$/', $tag) ? 1 : 931 (strpos($tag, '<+>') === false ? 2 : 3); 932 } 933 934 switch ($tags[$tag]) { 935 case 1: 936 $atts = $atts ? join_atts($atts) : ''; 937 return '<'.$tag.$atts.'>'.$content.'</'.$tag.'>'; 938 case 2: 939 return $tag.$content.$tag; 940 default: 941 return str_replace('<+>', $content, $tag); 942 } 943 } 944 945 /** 946 * Renders anything as a HTML void element. 947 * 948 * @param string $tag The tag without brackets 949 * @param string|array $atts HTML attributes 950 * @return string HTML 951 * @since 4.6.0 952 * @example 953 * echo tag_void('input', array('name' => 'name', 'type' => 'text')); 954 */ 955 956 function tag_void($tag, $atts = '') 957 { 958 return '<'.$tag.join_atts($atts).' />'; 959 } 960 961 /** 962 * Renders anything as a HTML start tag. 963 * 964 * @param string $tag The tag without brackets 965 * @param string|array $atts HTML attributes 966 * @return string A HTML start tag 967 * @since 4.6.0 968 * @example 969 * echo tag_start('section', array('class' => 'myClass')); 970 */ 971 972 function tag_start($tag, $atts = '') 973 { 974 return '<'.$tag.join_atts($atts).'>'; 975 } 976 977 /** 978 * Renders anything as a HTML end tag. 979 * 980 * @param string $tag The tag without brackets 981 * @return string A HTML end tag 982 * @since 4.6.0 983 * @example 984 * echo tag_end('section'); 985 */ 986 987 function tag_end($tag) 988 { 989 return '</'.$tag.'>'; 990 } 991 992 /** 993 * Renders a <p> element. 994 * 995 * @param string $item Enclosed content 996 * @param string|array $atts HTML attributes 997 * @return string HTML 998 * @example 999 * echo graf('This a paragraph.'); 1000 */ 1001 1002 function graf($item, $atts = '') 1003 { 1004 return n.tag($item, 'p', $atts); 1005 } 1006 1007 /** 1008 * Renders a <hx> element. 1009 * 1010 * @param string $item The Enclosed content 1011 * @param int $level Heading level 1...6 1012 * @param string|array $atts HTML attributes 1013 * @return string HTML 1014 * @example 1015 * echo hed('Heading', 2); 1016 */ 1017 1018 function hed($item, $level, $atts = '') 1019 { 1020 return n.tag($item, 'h'.$level, $atts).n; 1021 } 1022 1023 /** 1024 * Renders an <a> element. 1025 * 1026 * @param string $item Enclosed content 1027 * @param string|array $href The link target 1028 * @param string|array $atts HTML attributes 1029 * @return string HTML 1030 */ 1031 1032 function href($item, $href, $atts = '') 1033 { 1034 if (is_array($atts)) { 1035 $atts['href'] = $href; 1036 } else { 1037 if (is_array($href)) { 1038 $href = join_qs($href); 1039 } 1040 1041 $atts .= ' href="'.$href.'"'; 1042 } 1043 1044 return tag($item, 'a', $atts); 1045 } 1046 1047 /** 1048 * Renders a <strong> element. 1049 * 1050 * @param string $item Enclosed content 1051 * @param string|array $atts HTML attributes 1052 * @return string HTML 1053 */ 1054 1055 function strong($item, $atts = '') 1056 { 1057 return tag($item, 'strong', $atts); 1058 } 1059 1060 /** 1061 * Renders a <span> element. 1062 * 1063 * @param string $item Enclosed content 1064 * @param string|array $atts HTML attributes 1065 * @return string HTML 1066 */ 1067 1068 function span($item, $atts = '') 1069 { 1070 return tag($item, 'span', $atts); 1071 } 1072 1073 /** 1074 * Renders a <pre> element. 1075 * 1076 * @param string $item The input string 1077 * @param string|array $atts HTML attributes 1078 * @return string HTML 1079 * @example 1080 * echo htmlPre('<?php echo "Hello World"; ?>'); 1081 */ 1082 1083 function htmlPre($item, $atts = '') 1084 { 1085 if (($item = tag($item, 'code')) === '') { 1086 $item = null; 1087 } 1088 1089 return tag($item, 'pre', $atts); 1090 } 1091 1092 /** 1093 * Renders a HTML comment (<!-- -->) element. 1094 * 1095 * @param string $item The input string 1096 * @return string HTML 1097 * @example 1098 * echo comment('Some HTML comment.'); 1099 */ 1100 1101 function comment($item) 1102 { 1103 return '<!-- '.str_replace('--', '- - ', $item).' -->'; 1104 } 1105 1106 /** 1107 * Renders a <small> element. 1108 * 1109 * @param string $item The input string 1110 * @param string|array $atts HTML attributes 1111 * @return string HTML 1112 */ 1113 1114 function small($item, $atts = '') 1115 { 1116 return tag($item, 'small', $atts); 1117 } 1118 1119 /** 1120 * Renders a table data row from an array of content => width pairs. 1121 * 1122 * @param array $array Array of content => width pairs 1123 * @param string|array $atts Table row attributes 1124 * @return string A HTML table row 1125 */ 1126 1127 function assRow($array, $atts = '') 1128 { 1129 $out = array(); 1130 1131 foreach ($array as $value => $width) { 1132 $out[] = tda($value, array('width' => (int) $width)); 1133 } 1134 1135 return tr(join('', $out), $atts); 1136 } 1137 1138 /** 1139 * Renders a table head row from an array of strings. 1140 * 1141 * Takes an argument list of head text strings. i18n is applied to the strings. 1142 * 1143 * @return string HTML 1144 */ 1145 1146 function assHead() 1147 { 1148 $array = func_get_args(); 1149 $o = array(); 1150 1151 foreach ($array as $a) { 1152 $o[] = hCell(gTxt($a), '', ' scope="col"'); 1153 } 1154 1155 return tr(join('', $o)); 1156 } 1157 1158 /** 1159 * Renders the ubiquitous popup help button. 1160 * 1161 * The rendered link can be customised via a 'admin_help > {$help_var}' 1162 * pluggable UI callback event. 1163 * 1164 * @param string|array $help_var Help topic or topic and lang in an array 1165 * @param int $width Popup window width 1166 * @param int $height Popup window height 1167 * @param string $class HTML class 1168 * @param string $inline Inline pophelp 1169 * @return string HTML 1170 */ 1171 1172 function popHelp($help_var, $width = 0, $height = 0, $class = 'pophelp', $inline = '') 1173 { 1174 global $txp_user, $prefs; 1175 1176 $lang = null; 1177 1178 if (empty($help_var) || empty($prefs['module_pophelp'])) { 1179 return ''; 1180 } 1181 1182 if (is_array($help_var)) { 1183 $lang = empty($help_var[1]) ? null : $help_var[1]; 1184 $help_var = $help_var[0]; 1185 } 1186 1187 $url = filter_var($help_var, FILTER_VALIDATE_URL); 1188 1189 $atts = array( 1190 'rel' => 'help', 1191 'title' => gTxt('help'), 1192 'role' => 'button', 1193 ); 1194 1195 if ($url === false) { 1196 $atts['class'] = $class; 1197 $url = '#'; 1198 if (! empty($inline)) { 1199 $atts['data-item'] = $inline; 1200 } elseif (empty($txp_user)) { 1201 // Use inline pophelp, if unauthorized user or setup stage 1202 if (class_exists('\Textpattern\Module\Help\HelpAdmin')) { 1203 $atts['data-item'] = \Txp::get('\Textpattern\Module\Help\HelpAdmin')->pophelp($help_var, $lang); 1204 } 1205 } else { 1206 $url = '?event=help&step=pophelp&item='.urlencode($help_var); 1207 } 1208 $ui = sp.href(span(gTxt('help'), array('class' => 'ui-icon ui-icon-help')), $url, $atts); 1209 } else { 1210 $atts['target'] = '_blank'; 1211 $ui = sp.href(span(gTxt('help'), array('class' => 'ui-icon ui-icon-extlink')), $url, $atts); 1212 } 1213 1214 return pluggable_ui('admin_help', $help_var, $ui, compact('help_var', 'width', 'height', 'class', 'inline')); 1215 } 1216 1217 /** 1218 * Renders inline help text. 1219 * 1220 * The help topic is the name of a string that can be found in txp_lang. 1221 * 1222 * The rendered link can be customised via a 'admin_help_field > {$help_var}' 1223 * pluggable UI callback event. 1224 * 1225 * @param string $help_var Help topic 1226 * @return string HTML 1227 */ 1228 1229 function fieldHelp($help_var) 1230 { 1231 if (!$help_var) { 1232 return ''; 1233 } 1234 1235 $help_text = gTxt($help_var); 1236 1237 // If rendered string is the same as the input string, either the l10n 1238 // doesn't exist or the string is missing from txp_lang. 1239 // Either way, no instruction text, no render. 1240 if ($help_var === $help_text) { 1241 return ''; 1242 } 1243 1244 $ui = n.tag($help_text, 'div', array('class' => 'txp-form-field-instructions')); 1245 1246 return pluggable_ui('admin_help_field', $help_var, $ui, compact('help_var')); 1247 } 1248 1249 /** 1250 * Renders the ubiquitous popup help button with a little less visual noise. 1251 * 1252 * The rendered link can be customised via a 'admin_help > {$help_var}' 1253 * pluggable UI callback event. 1254 * 1255 * @param string $help_var Help topic 1256 * @param int $width Popup window width 1257 * @param int $height Popup window height 1258 * @return string HTML 1259 */ 1260 1261 function popHelpSubtle($help_var, $width = 0, $height = 0) 1262 { 1263 return popHelp($help_var, $width, $height, 'pophelpsubtle'); 1264 } 1265 1266 /** 1267 * Renders a link that opens a popup tag area. 1268 * 1269 * @param string $var Tag name 1270 * @param string $text Link text 1271 * @param array $atts Attributes to add to the link 1272 * @return string HTML 1273 */ 1274 1275 function popTag($var, $text, $atts = array()) 1276 { 1277 $opts = array( 1278 'event' => 'tag', 1279 'tag_name' => $var, 1280 ) + $atts; 1281 1282 return href($text, $opts, array('class' => 'txp-tagbuilder-link')); 1283 } 1284 1285 /** 1286 * Renders an admin-side message text. 1287 * 1288 * @param string $thing Subject 1289 * @param string $thething Predicate (strong) 1290 * @param string $action Object 1291 * @return string HTML 1292 * @deprecated in 4.7.0 1293 */ 1294 1295 function messenger($thing, $thething = '', $action = '') 1296 { 1297 return gTxt($thing).' '.strong($thething).' '.gTxt($action); 1298 } 1299 1300 /** 1301 * Renders a multi-edit form listing editing methods. 1302 * 1303 * @param array $options array('value' => array( 'label' => '', 'html' => '' ),...) 1304 * @param string $event Event 1305 * @param string $step Step 1306 * @param int $page Page number 1307 * @param string $sort Column sorted by 1308 * @param string $dir Sorting direction 1309 * @param string $crit Search criterion 1310 * @param string $search_method Search method 1311 * @return string HTML 1312 * @example 1313 * echo form( 1314 * multi_edit(array( 1315 * 'feature' => array('label' => 'Feature', 'html' => yesnoRadio('is_featured', 1)), 1316 * 'delete' => array('label' => 'Delete'), 1317 * )) 1318 * ); 1319 */ 1320 1321 function multi_edit($options, $event = null, $step = null, $page = '', $sort = '', $dir = '', $crit = '', $search_method = '') 1322 { 1323 $html = $methods = array(); 1324 $methods[''] = gTxt('with_selected_option', array('{count}' => '0')); 1325 1326 if ($event === null) { 1327 global $event; 1328 } 1329 1330 if ($step === null) { 1331 $step = $event.'_multi_edit'; 1332 } 1333 1334 callback_event_ref($event.'_ui', 'multi_edit_options', 0, $options); 1335 1336 foreach ($options as $value => $option) { 1337 if (is_array($option)) { 1338 $methods[$value] = $option['label']; 1339 1340 if (isset($option['html'])) { 1341 $html[$value] = n.tag($option['html'], 'div', array( 1342 'class' => 'multi-option', 1343 'data-multi-option' => $value, 1344 )); 1345 } 1346 } else { 1347 $methods[$value] = $option; 1348 } 1349 } 1350 1351 return n.tag( 1352 selectInput('edit_method', $methods, ''). 1353 eInput($event). 1354 sInput($step). 1355 hInput('page', $page). 1356 ($sort ? hInput('sort', $sort).hInput('dir', $dir) : ''). 1357 ($crit !== '' ? hInput('crit', $crit).hInput('search_method', $search_method) : ''). 1358 join('', $html). 1359 fInput('submit', '', gTxt('go')), 1360 'div', 1361 array('class' => 'multi-edit') 1362 ); 1363 } 1364 1365 /** 1366 * Renders a widget to select various amounts to page lists by. 1367 * 1368 * The rendered options can be changed via a '{$event}_ui > pageby_values' 1369 * callback event. 1370 * 1371 * @param string $event Event 1372 * @param int $val Current setting 1373 * @param string|null $step Step 1374 * @return string HTML 1375 * @deprecated in 4.7.0 1376 */ 1377 1378 function pageby_form($event, $val, $step = null) 1379 { 1380 return Txp::get('\Textpattern\Admin\Paginator', $event, $step)->render($val); 1381 } 1382 1383 /** 1384 * Renders an upload form. 1385 * 1386 * The rendered form can be customised via the '{$event}_ui > upload_form' 1387 * pluggable UI callback event. 1388 * 1389 * @param string $label File name label. May be empty 1390 * @param string $pophelp Help item 1391 * @param string $step Step 1392 * @param string $event Event 1393 * @param string $id File id 1394 * @param int $max_file_size Maximum allowed file size 1395 * @param string $label_id HTML id attribute for the filename input element 1396 * @param string $class HTML class attribute for the form element 1397 * @param string|array $wraptag_val Tag to wrap the value / label in, or empty to omit 1398 * @param array $extra array('postinput' => $categories ...) 1399 * @param string|array $accept Comma separated list of allowed file types, or empty to omit 1400 * @return string HTML 1401 */ 1402 1403 function upload_form($label, $pophelp, $step, $event, $id = '', $max_file_size = 1000000, $label_id = '', $class = '', $wraptag_val = array('div', 'div'), $extra = null, $accept = '') 1404 { 1405 if (!$label_id) { 1406 $label_id = $event.'-upload'; 1407 } 1408 1409 if ($wraptag_val) { 1410 $wraptag_class = 'txp-form-field file-uploader'; 1411 } else { 1412 $wraptag_class = 'inline-file-uploader'; 1413 } 1414 1415 if ($multiple = (bool) preg_match('/^.+\[\]$/', $step)) { 1416 $step = substr($step, 0, -2); 1417 } 1418 1419 $name = 'thefile'.($multiple ? '[]' : ''); 1420 $argv = func_get_args(); 1421 1422 return pluggable_ui( 1423 $event.'_ui', 1424 'upload_form', 1425 n.tag( 1426 (!empty($max_file_size) ? hInput('MAX_FILE_SIZE', $max_file_size) : ''). 1427 eInput($event). 1428 sInput($step). 1429 hInput('id', $id). 1430 tInput(). 1431 inputLabel( 1432 $label_id, 1433 n.tag_void('input', array( 1434 'name' => $name, 1435 'type' => 'file', 1436 'required' => true, 1437 'id' => $label_id, 1438 'multiple' => $multiple, 1439 'accept' => $accept, 1440 )). 1441 (isset($extra['postinput']) ? $extra['postinput'] : ''). 1442 n.tag( 1443 fInput('reset', '', gTxt('reset')). 1444 fInput('submit', '', gTxt('upload')).n, 1445 'span', 1446 array('class' => 'inline-file-uploader-actions') 1447 ), 1448 $label, 1449 array($pophelp, 'instructions_'.$pophelp), 1450 $wraptag_class, 1451 $wraptag_val 1452 ). 1453 tag(null, 'progress', array( 1454 'class' => 'txp-upload-progress', 1455 'style' => 'display:none', 1456 )), 1457 'form', 1458 array( 1459 'class' => 'upload-form'.($class ? ' '.trim($class) : ''), 1460 'method' => 'post', 1461 'enctype' => 'multipart/form-data', 1462 'action' => "index.php?event=$event&step=$step", 1463 ) 1464 ), 1465 $argv 1466 ); 1467 } 1468 1469 /** 1470 * Renders an admin-side search form. 1471 * 1472 * @param string $event Event 1473 * @param string $step Step 1474 * @param string $crit Search criterion 1475 * @param array $methods Valid search methods 1476 * @param string $method Actual search method 1477 * @param string $default_method Default search method 1478 * @return string HTML 1479 */ 1480 1481 function search_form($event, $step, $crit, $methods, $method, $default_method) 1482 { 1483 $method = ($method) ? $method : $default_method; 1484 1485 return form( 1486 graf( 1487 tag(gTxt('search'), 'label', array('for' => $event.'-search')). 1488 selectInput('search_method', $methods, $method, '', '', $event.'-search'). 1489 fInput('text', 'crit', $crit, 'input-medium', '', '', INPUT_MEDIUM). 1490 eInput($event). 1491 sInput($step). 1492 fInput('submit', 'search', gTxt('go')) 1493 ), '', '', 'get', 'search-form' 1494 ); 1495 } 1496 1497 /** 1498 * Renders a dropdown for selecting Textfilter method preferences. 1499 * 1500 * @param string $name Element name 1501 * @param string $val Current value 1502 * @param string $id HTML id attribute for the select input element 1503 * @return string HTML 1504 */ 1505 1506 function pref_text($name, $val, $id = '') 1507 { 1508 $id = ($id) ? $id : $name; 1509 $vals = Txp::get('\Textpattern\Textfilter\Registry')->getMap(); 1510 1511 return selectInput($name, $vals, $val, '', '', $id); 1512 } 1513 1514 /** 1515 * Attaches a HTML fragment to a DOM node. 1516 * 1517 * @param string $id Target DOM node's id 1518 * @param string $content HTML fragment 1519 * @param string $noscript Noscript alternative 1520 * @param string $wraptag Wrapping HTML element 1521 * @param string $wraptagid Wrapping element's HTML id 1522 * @return string HTML/JS 1523 */ 1524 1525 function dom_attach($id, $content, $noscript = '', $wraptag = 'div', $wraptagid = '') 1526 { 1527 $id = escape_js($id); 1528 $content = escape_js($content); 1529 $wraptag = escape_js($wraptag); 1530 $wraptagid = escape_js($wraptagid); 1531 1532 $js = <<<EOF 1533 $(function () 1534 { 1535 $('#{$id}').append($('<{$wraptag} />').attr('id', '{$wraptagid}').html('{$content}')); 1536 }); 1537 EOF; 1538 1539 return script_js($js, (string) $noscript); 1540 } 1541 1542 /** 1543 * Renders a <:script> element. 1544 * 1545 * The $route parameter allows script_js() to be included in fixed page 1546 * locations (e.g. prior to the </body> tag) but to only render 1547 * its content if the event / step match. 1548 * 1549 * @param string $js JavaScript code 1550 * @param int|string $flags Flags TEXTPATTERN_SCRIPT_URL | TEXTPATTERN_SCRIPT_ATTACH_VERSION, or boolean or noscript alternative if a string 1551 * @param array $route Optional events/steps upon which to add the script 1552 * @return string HTML with embedded script element 1553 * @example 1554 * echo script_js('/js/script.js', TEXTPATTERN_SCRIPT_URL); 1555 */ 1556 1557 function script_js($js, $flags = '', $route = array()) 1558 { 1559 static $store = ''; 1560 global $event, $step; 1561 1562 $targetEvent = empty($route[0]) ? null : (is_array($route[0]) ? $route[0] : do_list_unique($route[0])); 1563 $targetStep = empty($route[1]) ? null : (is_array($route[1]) ? $route[1] : do_list_unique($route[1])); 1564 1565 if (($targetEvent === null || in_array($event, $targetEvent)) && ($targetStep === null || in_array($step, $targetStep))) { 1566 if (is_int($flags)) { 1567 if ($flags & TEXTPATTERN_SCRIPT_URL) { 1568 if ($flags & TEXTPATTERN_SCRIPT_ATTACH_VERSION && strpos(txp_version, '-dev') === false) { 1569 $ext = pathinfo($js, PATHINFO_EXTENSION); 1570 1571 if ($ext) { 1572 $js = substr($js, 0, (strlen($ext) + 1) * -1); 1573 $ext = '.'.$ext; 1574 } 1575 1576 $js .= '.v'.txp_version.$ext; 1577 } 1578 1579 return n.tag(null, 'script', array('src' => $js)); 1580 } 1581 } 1582 1583 $js = preg_replace('#<(/?)(script)#i', '\\x3c$1$2', $js); 1584 1585 if (is_bool($flags)) { 1586 if (!$flags) { 1587 $store .= n.$js; 1588 1589 return; 1590 } else { 1591 $js = $store.n.$js; 1592 $store = ''; 1593 } 1594 } 1595 1596 $js = trim($js); 1597 $out = $js ? n.tag(n.$js.n, 'script') : ''; 1598 1599 if ($flags && $flags !== true) { 1600 $out .= n.tag(n.trim($flags).n, 'noscript'); 1601 } 1602 1603 return $out; 1604 } 1605 1606 return ''; 1607 } 1608 1609 /** 1610 * Renders a checkbox to set/unset a browser cookie. 1611 * 1612 * @param string $classname Label text. The cookie's name will be derived from this value 1613 * @param bool $form Create as a stand-along <form> element 1614 * @return string HTML 1615 */ 1616 1617 function cookie_box($classname, $form = true) 1618 { 1619 $name = 'cb_'.$classname; 1620 $id = escape_js($name); 1621 $class = escape_js($classname); 1622 1623 if (cs('toggle_'.$classname)) { 1624 $value = 1; 1625 } else { 1626 $value = 0; 1627 } 1628 1629 $newvalue = 1 - $value; 1630 1631 $out = checkbox($name, 1, (bool) $value, 0, $name). 1632 n.tag(gTxt($classname), 'label', array('for' => $name)); 1633 1634 $js = <<<EOF 1635 $(function () 1636 { 1637 $('input') 1638 .filter(function () { 1639 if ($(this).attr('id') === '{$id}') { 1640 return true; 1641 } 1642 }) 1643 .change(function () { 1644 setClassRemember('{$class}', $newvalue); 1645 $(this).parents('form').submit(); 1646 }); 1647 }); 1648 EOF; 1649 1650 $out .= script_js($js); 1651 1652 if ($form) { 1653 if (serverSet('QUERY_STRING')) { 1654 $action = 'index.php?'.serverSet('QUERY_STRING'); 1655 } else { 1656 $action = 'index.php'; 1657 } 1658 1659 $out .= eInput(gps('event')).tInput(); 1660 1661 return tag($out, 'form', array( 1662 'class' => $name, 1663 'method' => 'post', 1664 'action' => $action, 1665 )); 1666 } 1667 1668 return $out; 1669 } 1670 1671 /** 1672 * Renders a <fieldset> element. 1673 * 1674 * @param string $content Enclosed content 1675 * @param string $legend Legend text 1676 * @param string $id HTML id attribute 1677 * @return string HTML 1678 */ 1679 1680 function fieldset($content, $legend = '', $id = '') 1681 { 1682 return tag(trim(tag($legend, 'legend').n.$content), 'fieldset', array('id' => $id)); 1683 } 1684 1685 /** 1686 * Renders a link element to hook up txpAsyncHref() with request parameters. 1687 * 1688 * See this function's JavaScript companion, txpAsyncHref(), in textpattern.js. 1689 * 1690 * @param string $item Link text 1691 * @param array $parms Request parameters; array keys are 'event', 'step', 'thing', 'property' 1692 * @param string|array $atts HTML attributes 1693 * @return string HTML 1694 * @since 4.5.0 1695 * @example 1696 * echo asyncHref('Disable', array( 1697 * 'event' => 'myEvent', 1698 * 'step' => 'myStep', 1699 * 'thing' => 'status', 1700 * 'property' => 'disable', 1701 * )); 1702 */ 1703 1704 function asyncHref($item, $parms, $atts = '') 1705 { 1706 global $event, $step; 1707 1708 $parms = lAtts(array( 1709 'event' => $event, 1710 'step' => $step, 1711 'thing' => '', 1712 'property' => '', 1713 ), $parms); 1714 1715 $class = $parms['step'].' async'; 1716 1717 if (is_array($atts)) { 1718 $atts['class'] = $class; 1719 } else { 1720 $atts .= ' class="'.txpspecialchars($class).'"'; 1721 } 1722 1723 return href($item, join_qs($parms), $atts); 1724 } 1725 1726 /** 1727 * Renders an array of items as a HTML list. 1728 * 1729 * This function is used for tag handler functions. Creates a HTML list markup 1730 * from an array of items. 1731 * 1732 * @param array $list 1733 * @param string $wraptag The HTML element 1734 * @param string $break The HTML break element 1735 * @param string $class Class applied to the wraptag 1736 * @param string $breakclass Class applied to break tag 1737 * @param string $atts HTML attributes applied to the wraptag 1738 * @param string $breakatts HTML attributes applied to the break tag 1739 * @param string $id HTML id applied to the wraptag 1740 * @return string HTML 1741 * @package HTML 1742 * @example 1743 * echo doWrap(array('item1', 'item2'), 'div', 'p'); 1744 */ 1745 1746 function doWrap($list, $wraptag, $break, $class = null, $breakclass = null, $atts = null, $breakatts = null, $html_id = null) 1747 { 1748 global $txp_atts; 1749 static $import = array('breakby', 'breakclass', 'wrapform'); 1750 1751 $list = is_array($list) ? array_filter($list, function ($v) { 1752 return $v !== false; 1753 }) : null; 1754 1755 if (!$list) { 1756 return ''; 1757 } 1758 1759 if (is_array($break)) { 1760 extract($break + array('break' => '')); 1761 } 1762 1763 foreach ($import as $global) { 1764 if (!isset($$global) && isset($txp_atts[$global])) { 1765 $$global = $txp_atts[$global]; 1766 } 1767 } 1768 1769 if ($html_id) { 1770 $atts .= ' id="'.txpspecialchars($html_id).'"'; 1771 } 1772 1773 if ($class) { 1774 $atts .= ' class="'.txpspecialchars($class).'"'; 1775 } 1776 1777 if ($breakclass) { 1778 $breakatts .= ' class="'.txpspecialchars($breakclass).'"'; 1779 } 1780 1781 if ($break && !empty($breakby)) { // array_merge to reindex 1782 $breakby = array_merge(array_filter(array_map('intval', do_list($breakby)))); 1783 1784 switch ($count = count($breakby)) { 1785 case 0: 1786 break; 1787 case 1: 1788 if ($breakby[0] > 0) { 1789 $breakby[0] == 1 or $newlist = array_chunk($list, $breakby[0]); 1790 break; 1791 } 1792 // no break 1793 default: 1794 $newlist = array(); 1795 1796 for ($i = 0; count($list); $i = ($i + 1)%$count) { 1797 $newlist[] = $breakby[$i] > 0 ? array_splice($list, 0, $breakby[$i]) : array_splice($list, $breakby[$i]); 1798 } 1799 1800 } 1801 1802 empty($newlist) or $list = array_map('implode', $newlist); 1803 } 1804 1805 if (isset($txp_atts['trim']) && $txp_atts['trim'] === true) { 1806 $list = array_map('trim', $list); 1807 } 1808 1809 if ($break === true) { 1810 switch (strtolower($wraptag)) { 1811 case 'ul': 1812 case 'ol': 1813 $break = 'li'; 1814 break; 1815 case 'p': 1816 $break = 'br'; 1817 break; 1818 case 'table': 1819 case 'tbody': 1820 case 'thead': 1821 case 'tfoot': 1822 $break = 'tr'; 1823 break; 1824 case 'tr': 1825 $break = 'td'; 1826 break; 1827 default: 1828 $break = n; 1829 } 1830 } 1831 1832 if (strpos($break, '<+>') !== false) { 1833 $content = array_reduce($list, function ($carry, $item) use ($break) { 1834 return $carry.str_replace('<+>', $item, $break); 1835 }); 1836 } 1837 // Non-enclosing breaks. 1838 elseif ($break === 'br' || $break === 'hr' || !preg_match('/^\w+$/', $break)) { 1839 if ($break === 'br' || $break === 'hr') { 1840 $break = "<$break $breakatts/>".n; 1841 } 1842 1843 $content = join($break, $list); 1844 } else { 1845 $content = "<{$break}{$breakatts}>".join("</$break>".n."<{$break}{$breakatts}>", $list)."</{$break}>"; 1846 } 1847 1848 if (!empty($wrapform)) { 1849 $content = str_replace('<+>', $content, parse_form($wrapform)); 1850 } 1851 1852 if (empty($wraptag)) { 1853 return $content; 1854 } else { 1855 return tag($content, $wraptag, $atts); 1856 } 1857 } 1858 1859 /** 1860 * Renders anything as a HTML tag. 1861 * 1862 * Used for tag handler functions. 1863 * 1864 * If $content is empty, renders a self-closing tag. 1865 * 1866 * @param string $content The wrapped item 1867 * @param string $tag The HTML tag 1868 * @param string $class HTML class 1869 * @param string $atts HTML attributes 1870 * @param string $id HTML id 1871 * @return string HTML 1872 * @package HTML 1873 * @example 1874 * echo doTag('', 'meta', '', 'name="description" content="Some content"'); 1875 */ 1876 1877 function doTag($content, $tag, $class = '', $atts = '', $id = '') 1878 { 1879 if (!$tag) { 1880 return $content; 1881 } 1882 1883 if ($id) { 1884 $atts .= ' id="'.txpspecialchars($id).'"'; 1885 } 1886 1887 if ($class) { 1888 $atts .= ' class="'.txpspecialchars($class).'"'; 1889 } 1890 1891 return (string)$content !== '' ? tag($content, $tag, $atts) : "<$tag $atts />"; 1892 } 1893 1894 /** 1895 * Renders a label. 1896 * 1897 * This function is mostly used for rendering headings in tag handler functions. 1898 * 1899 * If no $labeltag is given, label is separated from the content with 1900 * a <br>. 1901 * 1902 * @param string $label The label 1903 * @param string $labeltag The HTML element 1904 * @return string HTML 1905 * @package HTML 1906 * @example 1907 * echo doLabel('My label', 'h3'); 1908 */ 1909 1910 function doLabel($label = '', $labeltag = '') 1911 { 1912 if ($label) { 1913 return (empty($labeltag) ? $label.'<br />' : tag($label, $labeltag)); 1914 } 1915 1916 return ''; 1917 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
title