. */ /** * Sections panel. * * @package Admin\Section */ use Textpattern\Search\Filter; if (!defined('txpinterface')) { die('txpinterface is undefined.'); } if ($event == 'section') { require_privs('section'); global $all_pages, $all_styles; $all_pages = safe_column("name", 'txp_page', "1 = 1 ORDER BY name"); $all_styles = safe_column("name", 'txp_css', "1 = 1 ORDER BY name"); $available_steps = array( 'section_change_pageby' => true, 'sec_section_list' => false, 'section_delete' => true, 'section_save' => true, 'section_edit' => false, 'section_multi_edit' => true, 'section_set_default' => true, 'section_toggle_option' => true, ); if ($step && bouncer($step, $available_steps)) { $step(); } else { sec_section_list(); } } /** * The main panel listing all sections. * * So-named to avoid clashing with the <txp:section_list /> tag. * * @param string|array $message The activity message */ function sec_section_list($message = '') { global $event, $section_list_pageby; pagetop(gTxt('tab_sections'), $message); extract(gpsa(array( 'page', 'sort', 'dir', 'crit', 'search_method', ))); if ($sort === '') { $sort = get_pref('section_sort_column', 'name'); } else { if (!in_array($sort, array('title', 'page', 'css', 'in_rss', 'on_frontpage', 'searchable', 'article_count'))) { $sort = 'name'; } set_pref('section_sort_column', $sort, 'section', 2, '', 0, PREF_PRIVATE); } if ($dir === '') { $dir = get_pref('section_sort_dir', 'desc'); } else { $dir = ($dir == 'asc') ? "asc" : "desc"; set_pref('section_sort_dir', $dir, 'section', 2, '', 0, PREF_PRIVATE); } switch ($sort) { case 'title': $sort_sql = "title $dir"; break; case 'page': $sort_sql = "page $dir"; break; case 'css': $sort_sql = "css $dir"; break; case 'in_rss': $sort_sql = "in_rss $dir"; break; case 'on_frontpage': $sort_sql = "on_frontpage $dir"; break; case 'searchable': $sort_sql = "searchable $dir"; break; case 'article_count': $sort_sql = "article_count $dir"; break; default: $sort_sql = "name $dir"; break; } $switch_dir = ($dir == 'desc') ? 'asc' : 'desc'; $search = new Filter($event, array( 'name' => array( 'column' => 'txp_section.name', 'label' => gTxt('name'), ), 'title' => array( 'column' => 'txp_section.title', 'label' => gTxt('title'), ), 'page' => array( 'column' => 'txp_section.page', 'label' => gTxt('page'), ), 'css' => array( 'column' => 'txp_section.css', 'label' => gTxt('css'), ), 'on_frontpage' => array( 'column' => 'txp_section.on_frontpage', 'label' => gTxt('on_front_page'), 'type' => 'boolean', ), 'in_rss' => array( 'column' => 'txp_section.in_rss', 'label' => gTxt('syndicate'), 'type' => 'boolean', ), 'searchable' => array( 'column' => 'txp_section.searchable', 'label' => gTxt('include_in_search'), 'type' => 'boolean', ), ) ); $alias_yes = '1, Yes'; $alias_no = '0, No'; $search->setAliases('on_frontpage', array($alias_no, $alias_yes)); $search->setAliases('in_rss', array($alias_no, $alias_yes)); $search->setAliases('searchable', array($alias_no, $alias_yes)); list($criteria, $crit, $search_method) = $search->getFilter(); $search_render_options = array( 'placeholder' => 'search_sections', ); $total = safe_count('txp_section', $criteria); echo n.'
'. n.tag( hed(gTxt('tab_sections'), 1, array('class' => 'txp-heading')), 'div', array('class' => 'txp-layout-4col-alt') ); $searchBlock = n.tag( $search->renderForm('sec_section', $search_render_options), 'div', array( 'class' => 'txp-layout-4col-3span', 'id' => $event.'_control', ) ); $createBlock = array(); if (has_privs('section.edit')) { $createBlock[] = n.tag( sLink('section', 'section_edit', gTxt('create_section'), 'txp-button'). n.tag_start('form', array( 'class' => 'async', 'id' => 'default_section_form', 'name' => 'default_section_form', 'method' => 'post', 'action' => 'index.php', )). tag(gTxt('default_write_section'), 'label', array('for' => 'default_section')). popHelp('section_default'). section_select_list(). eInput('section'). sInput('section_set_default'). n.tag_end('form'), 'div', array('class' => 'txp-control-panel') ); } $contentBlockStart = n.tag_start('div', array( 'class' => 'txp-layout-1col', 'id' => $event.'_container', )); $createBlock = implode(n, $createBlock); if ($total < 1) { if ($criteria != 1) { echo $searchBlock. $contentBlockStart. $createBlock. graf( span(null, array('class' => 'ui-icon ui-icon-info')).' '. gTxt('no_results_found'), array('class' => 'alert-block information') ). n.tag_end('div'). // End of .txp-layout-1col. n.'
'; // End of .txp-layout. } return; } $limit = max($section_list_pageby, 15); list($page, $offset, $numPages) = pager($total, $limit, $page); echo $searchBlock.$contentBlockStart.$createBlock; $rs = safe_rows_start( "*, (SELECT COUNT(*) FROM ".safe_pfx_j('textpattern')." WHERE textpattern.Section = txp_section.name) AS article_count", 'txp_section', "$criteria ORDER BY $sort_sql LIMIT $offset, $limit" ); if ($rs) { echo n.tag( toggle_box('section_detail'), 'div', array('class' => 'txp-list-options')). n.tag_start('form', array( 'class' => 'multi_edit_form', 'id' => 'section_form', 'name' => 'longform', 'method' => 'post', 'action' => 'index.php', )). n.tag_start('div', array('class' => 'txp-listtables')). n.tag_start('table', array('class' => 'txp-list')). n.tag_start('thead'). tr( hCell( fInput('checkbox', 'select_all', 0, '', '', '', '', '', 'select_all'), '', ' class="txp-list-col-multi-edit" scope="col" title="'.gTxt('toggle_all_selected').'"' ). column_head( 'name', 'name', 'section', true, $switch_dir, $crit, $search_method, (('name' == $sort) ? "$dir " : '').'txp-list-col-name' ). column_head( 'title', 'title', 'section', true, $switch_dir, $crit, $search_method, (('title' == $sort) ? "$dir " : '').'txp-list-col-title' ). column_head( 'page', 'page', 'section', true, $switch_dir, $crit, $search_method, (('page' == $sort) ? "$dir " : '').'txp-list-col-page' ). column_head( 'css', 'css', 'section', true, $switch_dir, $crit, $search_method, (('css' == $sort) ? "$dir " : '').'txp-list-col-style' ). column_head( 'on_front_page', 'on_frontpage', 'section', true, $switch_dir, $crit, $search_method, (('on_frontpage' == $sort) ? "$dir " : '').'txp-list-col-frontpage section_detail' ). column_head( 'syndicate', 'in_rss', 'section', true, $switch_dir, $crit, $search_method, (('in_rss' == $sort) ? "$dir " : '').'txp-list-col-syndicate section_detail' ). column_head( 'include_in_search', 'searchable', 'section', true, $switch_dir, $crit, $search_method, (('searchable' == $sort) ? "$dir " : '').'txp-list-col-searchable section_detail' ). column_head( 'articles', 'article_count', 'section', true, $switch_dir, $crit, $search_method, (('article_count' == $sort) ? "$dir " : '').'txp-list-col-article_count section_detail' ) ). n.tag_end('thead'). n.tag_start('tbody'); while ($a = nextRow($rs)) { extract($a, EXTR_PREFIX_ALL, 'sec'); $edit_url = array( 'event' => 'section', 'step' => 'section_edit', 'name' => $sec_name, 'sort' => $sort, 'dir' => $dir, 'page' => $page, 'search_method' => $search_method, 'crit' => $crit, ); if ($sec_name == 'default') { $articles = $sec_searchable = $sec_in_rss = $sec_on_frontpage = '-'; } else { $sec_on_frontpage = asyncHref(yes_no($sec_on_frontpage), array( 'step' => 'section_toggle_option', 'thing' => $sec_name, 'property' => 'on_frontpage', )); $sec_in_rss = asyncHref(yes_no($sec_in_rss), array( 'step' => 'section_toggle_option', 'thing' => $sec_name, 'property' => 'in_rss', )); $sec_searchable = asyncHref(yes_no($sec_searchable), array( 'step' => 'section_toggle_option', 'thing' => $sec_name, 'property' => 'searchable', )); if ($sec_article_count > 0) { $articles = href($sec_article_count, array( 'event' => 'list', 'search_method' => 'section', 'crit' => '"'.$sec_name.'"', ), array( 'title' => gTxt('article_count', array('{num}' => $sec_article_count)), )); } else { $articles = 0; } } $sec_page = href(txpspecialchars($sec_page), array( 'event' => 'page', 'name' => $sec_page, ), array('title' => gTxt('edit'))); $sec_css = href(txpspecialchars($sec_css), array( 'event' => 'css', 'name' => $sec_css, ), array('title' => gTxt('edit'))); echo tr( td( fInput('checkbox', 'selected[]', $sec_name), '', 'txp-list-col-multi-edit' ). hCell( href( txpspecialchars($sec_name), $edit_url, array('title' => gTxt('edit')) ). span( sp.span('|', array('role' => 'separator')). sp.href(gTxt('view'), pagelinkurl(array('s' => $sec_name))), array('class' => 'txp-option-link section_detail') ), '', array( 'class' => 'txp-list-col-name', 'scope' => 'row', ) ). td( txpspecialchars($sec_title), '', 'txp-list-col-title' ). td( $sec_page, '', 'txp-list-col-page' ). td( $sec_css, '', 'txp-list-col-style' ). td( $sec_on_frontpage, '', 'txp-list-col-frontpage section_detail' ). td( $sec_in_rss, '', 'txp-list-col-syndicate section_detail' ). td( $sec_searchable, '', 'txp-list-col-searchable section_detail' ). td( $articles, '', 'txp-list-col-article_count section_detail' ), array('id' => 'txp_section_'.$sec_name) ); } echo n.tag_end('tbody'). n.tag_end('table'). n.tag_end('div'). // End of .txp-listtables. section_multiedit_form($page, $sort, $dir, $crit, $search_method). tInput(). n.tag_end('form'). n.tag_start('div', array( 'class' => 'txp-navigation', 'id' => $event.'_navigation', )). pageby_form('section', $section_list_pageby). nav_form('section', $page, $numPages, $sort, $dir, $crit, $search_method, $total, $limit). n.tag_end('div'); } echo n.tag_end('div'). // End of .txp-layout-1col. n.''; // End of .txp-layout. } /** * Renders and outputs the section editor panel. */ function section_edit() { global $event, $step, $all_pages, $all_styles; require_privs('section.edit'); extract(gpsa(array( 'page', 'sort', 'dir', 'crit', 'search_method', 'name', ))); $is_edit = ($name && $step == 'section_edit'); $caption = gTxt('create_section'); $is_default_section = false; if ($is_edit) { $rs = safe_row( "*", 'txp_section', "name = '".doSlash($name)."'" ); if ($name == 'default') { $caption = gTxt('edit_default_section'); $is_default_section = true; } else { $caption = gTxt('edit_section'); } } else { // Pulls defaults for the new section from the 'default'. $rs = safe_row( "page, css, on_frontpage, in_rss, searchable", 'txp_section', "name = 'default'" ); if ($rs) { $rs['name'] = $rs['title'] = $rs['description'] = ''; } } if (!$rs) { sec_section_list(array(gTxt('unknown_section'), E_ERROR)); return; } extract($rs, EXTR_PREFIX_ALL, 'sec'); pagetop(gTxt('tab_sections')); $out = array(); $out[] = hed($caption, 2); if ($is_default_section) { $out[] = hInput('name', 'default'); } else { $out[] = inputLabel( 'section_name', fInput('text', 'name', $sec_name, '', '', '', INPUT_REGULAR, '', 'section_name'), 'section_name', '', array('class' => 'txp-form-field edit-section-name') ). inputLabel( 'section_title', fInput('text', 'title', $sec_title, '', '', '', INPUT_REGULAR, '', 'section_title'), 'section_longtitle', '', array('class' => 'txp-form-field edit-section-longtitle') ); } $out[] = inputLabel( 'section_page', selectInput('section_page', $all_pages, $sec_page, '', '', 'section_page'), 'uses_page', 'section_uses_page', array('class' => 'txp-form-field edit-section-uses-page') ). inputLabel( 'section_css', selectInput('css', $all_styles, $sec_css, '', '', 'section_css'), 'uses_style', 'section_uses_css', array('class' => 'txp-form-field edit-section-uses-css') ); if (!$is_default_section) { $out[] = inputLabel( 'on_front_page', yesnoradio('on_frontpage', $sec_on_frontpage, '', $sec_name), '', 'section_on_frontpage', array('class' => 'txp-form-field edit-section-on-frontpage') ). inputLabel( 'syndicate', yesnoradio('in_rss', $sec_in_rss, '', $sec_name), '', 'section_syndicate', array('class' => 'txp-form-field edit-section-syndicate') ). inputLabel( 'include_in_search', yesnoradio('searchable', $sec_searchable, '', $sec_name), '', 'section_searchable', array('class' => 'txp-form-field edit-section-searchable') ); } $out[] = inputLabel( 'section_description', '', 'description', 'section_description', array('class' => 'txp-form-field txp-form-field-textarea edit-section-description') ); $out[] = pluggable_ui('section_ui', 'extend_detail_form', '', $rs). graf( sLink('section', '', gTxt('cancel'), 'txp-button'). fInput('submit', '', gTxt('save'), 'publish'), array('class' => 'txp-edit-actions') ). eInput('section'). sInput('section_save'). hInput('old_name', $sec_name). hInput('search_method', $search_method). hInput('crit', $crit). hInput('page', $page). hInput('sort', $sort). hInput('dir', $dir); echo form(join('', $out), '', '', 'post', 'txp-edit', '', 'section_details'); } /** * Saves a section. */ function section_save() { $in = array_map('assert_string', psa(array( 'name', 'title', 'description', 'old_name', 'section_page', 'css', ))); if (empty($in['title'])) { $in['title'] = $in['name']; } // Prevent non-URL characters on section names. $in['name'] = strtolower(sanitizeForUrl($in['name'])); extract($in); $in = doSlash($in); extract($in, EXTR_PREFIX_ALL, 'safe'); if ($name != strtolower($old_name)) { if (safe_field("name", 'txp_section', "name = '$safe_name'")) { // Invalid input. Halt all further processing (e.g. plugin event // handlers). $message = array(gTxt('section_name_already_exists', array('{name}' => $name)), E_ERROR); // modal_halt($message); sec_section_list($message); return; } } $ok = false; if ($name == 'default') { $ok = safe_update('txp_section', "page = '$safe_section_page', css = '$safe_css', description = '$safe_description'", "name = 'default'"); } elseif ($name) { extract(array_map('assert_int', psa(array('on_frontpage', 'in_rss', 'searchable')))); if ($safe_old_name) { $ok = safe_update('txp_section', " name = '$safe_name', title = '$safe_title', page = '$safe_section_page', css = '$safe_css', description = '$safe_description', on_frontpage = $on_frontpage, in_rss = $in_rss, searchable = $searchable ", "name = '$safe_old_name'"); // Manually maintain referential integrity. if ($ok) { $ok = safe_update('textpattern', "Section = '$safe_name'", "Section = '$safe_old_name'"); } } else { $ok = safe_insert('txp_section', " name = '$safe_name', title = '$safe_title', page = '$safe_section_page', css = '$safe_css', description = '$safe_description', on_frontpage = $on_frontpage, in_rss = $in_rss, searchable = $searchable"); } } if ($ok) { update_lastmod('section_saved', compact('name', 'title', 'page', 'css', 'description', 'on_frontpage', 'in_rss', 'searchable')); } if ($ok) { sec_section_list(gTxt(($safe_old_name ? 'section_updated' : 'section_created'), array('{name}' => $name))); } else { sec_section_list(array(gTxt('section_save_failed'), E_ERROR)); } } /** * Changes and saves the pageby value. */ function section_change_pageby() { event_change_pageby('section'); sec_section_list(); } /** * Toggles section yes/no parameters. * * This function requires three HTTP POST parameters: 'column', 'value' and * 'name'. The 'value' is the new value, localised 'Yes' or 'No', * 'name' is the section and the 'column' is the altered setting, * either 'on_frontpage', 'in_rss' or 'searchable'. * * Outputs a text/plain response comprising the new displayable * value for the toggled parameter. */ function section_toggle_option() { extract(psa(array( 'property', 'value', 'thing', ))); $value = (int) ($value === gTxt('no')); if (in_array($property, array('on_frontpage', 'in_rss', 'searchable'))) { if (safe_update('txp_section', "$property = $value", "name = '".doSlash($thing)."'")) { echo yes_no($value); return; } } trigger_error(gTxt('section_save_failed'), E_USER_ERROR); } /** * Sets a section as the default. */ function section_set_default() { extract(psa(array( 'default_section', ))); $exists = safe_row("name", 'txp_section', "name = '".doSlash($default_section)."'"); if ($exists && set_pref('default_section', $default_section, 'section', PREF_HIDDEN)) { send_script_response(announce(gTxt('default_section_updated'))); return; } send_script_response(announce(gTxt('section_save_failed'), E_ERROR)); } /** * Renders a 'default_section' <select> input listing all sections. * * Used for changing the default section. * * @return string HTML */ function section_select_list() { $val = get_pref('default_section'); $sections = safe_rows("name, title", 'txp_section', "name != 'default' ORDER BY title, name"); $vals = array(); foreach ($sections as $row) { $vals[$row['name']] = $row['title']; } return selectInput('default_section', $vals, $val, false, true, 'default_section'); } /** * Processes delete actions sent using the multi-edit form. */ function section_delete() { $selectedList = ps('selected'); $selected = join(',', quote_list($selectedList)); $message = ''; $sections = safe_column( "name", 'txp_section', "name != 'default' AND name IN ($selected) AND name NOT IN (SELECT Section FROM ".safe_pfx('textpattern').")" ); $sectionsNotDeleted = array_diff($selectedList, $sections); if ($sections && safe_delete('txp_section', "name IN (".join(',', quote_list($sections)).")")) { callback_event('sections_deleted', '', 0, $sections); $message = gTxt('section_deleted', array('{name}' => join(', ', $sections))); } if ($sectionsNotDeleted) { $severity = ($message) ? E_WARNING : E_ERROR; $message = array(($message ? $message.n : '') . gTxt('section_delete_failure', array('{name}' => join(', ', $sectionsNotDeleted))), $severity); } sec_section_list($message); } /** * Renders a multi-edit form widget. * * @param int $page The page number * @param string $sort The current sorting value * @param string $dir The current sorting direction * @param string $crit The current search criteria * @param string $search_method The current search method * @return string HTML */ function section_multiedit_form($page, $sort, $dir, $crit, $search_method) { global $all_pages, $all_styles; $methods = array( 'changepage' => array( 'label' => gTxt('uses_page'), 'html' => selectInput('uses_page', $all_pages, '', false), ), 'changecss' => array( 'label' => gTxt('uses_style'), 'html' => selectInput('css', $all_styles, '', false), ), 'changeonfrontpage' => array( 'label' => gTxt('on_front_page'), 'html' => yesnoRadio('on_frontpage', 1), ), 'changesyndicate' => array( 'label' => gTxt('syndicate'), 'html' => yesnoRadio('in_rss', 1), ), 'changesearchable' => array( 'label' => gTxt('include_in_search'), 'html' => yesnoRadio('searchable', 1), ), 'delete' => gTxt('delete'), ); return multi_edit($methods, 'section', 'section_multi_edit', $page, $sort, $dir, $crit, $search_method); } /** * Processes multi-edit actions. */ function section_multi_edit() { global $txp_user, $all_pages, $all_styles; extract(psa(array( 'edit_method', 'selected', ))); if (!$selected || !is_array($selected)) { return sec_section_list(); } $key = $val = ''; switch ($edit_method) { case 'delete': return section_delete(); break; case 'changepage': $val = ps('uses_page'); if (in_array($val, $all_pages, true)) { $key = 'page'; } break; case 'changecss': $val = ps('css'); if (in_array($val, $all_styles, true)) { $key = 'css'; } break; case 'changeonfrontpage': $key = 'on_frontpage'; $val = (int) ps('on_frontpage'); break; case 'changesyndicate': $key = 'in_rss'; $val = (int) ps('in_rss'); break; case 'changesearchable': $key = 'searchable'; $val = (int) ps('searchable'); break; } $sections = safe_column( "name", 'txp_section', "name IN (".join(',', quote_list($selected)).")" ); if ($key && $sections) { if ( safe_update( 'txp_section', "$key = '".doSlash($val)."'", "name IN (".join(',', quote_list($sections)).")" ) ) { sec_section_list(gTxt('section_updated', array('{name}' => join(', ', $sections)))); return; } } sec_section_list(); }