. */ /** * Links panel. * * @package Admin\Link */ use Textpattern\Validator\CategoryConstraint; use Textpattern\Validator\Validator; use Textpattern\Search\Filter; if (!defined('txpinterface')) { die('txpinterface is undefined.'); } if ($event == 'link') { require_privs('link'); global $vars; $vars = array('category', 'url', 'linkname', 'linksort', 'description', 'id'); global $all_link_cats, $all_link_authors; $all_link_cats = getTree('root', 'link'); $all_link_authors = the_privileged('link.edit.own'); $available_steps = array( 'link_list' => false, 'link_edit' => false, 'link_save' => true, 'link_change_pageby' => true, 'link_multi_edit' => true, ); if ($step && bouncer($step, $available_steps)) { $step(); } else { link_list(); } } /** * The main panel listing all links. * * @param string|array $message The activity message */ function link_list($message = '') { global $event, $step, $link_list_pageby, $txp_user; pagetop(gTxt('tab_link'), $message); extract(gpsa(array( 'page', 'sort', 'dir', 'crit', 'search_method', ))); if ($sort === '') { $sort = get_pref('link_sort_column', 'name'); } else { if (!in_array($sort, array('id', 'description', 'url', 'category', 'date', 'author'))) { $sort = 'name'; } set_pref('link_sort_column', $sort, 'link', 2, '', 0, PREF_PRIVATE); } if ($dir === '') { $dir = get_pref('link_sort_dir', 'asc'); } else { $dir = ($dir == 'desc') ? "desc" : "asc"; set_pref('link_sort_dir', $dir, 'link', 2, '', 0, PREF_PRIVATE); } switch ($sort) { case 'id': $sort_sql = "txp_link.id $dir"; break; case 'description': $sort_sql = "txp_link.description $dir, txp_link.id ASC"; break; case 'url': $sort_sql = "txp_link.url $dir, txp_link.id ASC"; break; case 'category': $sort_sql = "txp_category.title $dir, txp_link.id ASC"; break; case 'date': $sort_sql = "txp_link.date $dir, txp_link.id ASC"; break; case 'author': $sort_sql = "txp_users.RealName $dir, txp_link.id ASC"; break; default: $sort = 'name'; $sort_sql = "txp_link.linksort $dir, txp_link.id ASC"; break; } $switch_dir = ($dir == 'desc') ? 'asc' : 'desc'; $search = new Filter($event, array( 'id' => array( 'column' => 'txp_link.id', 'label' => gTxt('ID'), 'type' => 'integer', ), 'name' => array( 'column' => 'txp_link.linkname', 'label' => gTxt('link_name'), ), 'url' => array( 'column' => 'txp_link.url', 'label' => gTxt('url'), ), 'description' => array( 'column' => 'txp_link.description', 'label' => gTxt('description'), ), 'category' => array( 'column' => array('txp_link.category', 'txp_category.title'), 'label' => gTxt('link_category'), ), 'author' => array( 'column' => array('txp_link.author', 'txp_users.RealName'), 'label' => gTxt('author'), ), 'linksort' => array( 'column' => 'txp_link.linksort', 'label' => gTxt('sort_value'), ), ) ); list($criteria, $crit, $search_method) = $search->getFilter(array( 'id' => array('can_list' => true), )); $search_render_options = array( 'placeholder' => 'search_links', ); $sql_from = safe_pfx_j('txp_link')." LEFT JOIN ".safe_pfx_j('txp_category')." ON txp_category.name = txp_link.category AND txp_category.type = 'link' LEFT JOIN ".safe_pfx_j('txp_users')." ON txp_users.name = txp_link.author"; if ($criteria === 1) { $total = safe_count('txp_link', $criteria); } else { $total = getThing("SELECT COUNT(*) FROM $sql_from WHERE $criteria"); } echo n.'
'. n.tag( hed(gTxt('tab_link'), 1, array('class' => 'txp-heading')), 'div', array('class' => 'txp-layout-4col-alt') ); $searchBlock = n.tag( $search->renderForm('link_list', $search_render_options), 'div', array( 'class' => 'txp-layout-4col-3span', 'id' => $event.'_control', ) ); $createBlock = array(); if (has_privs('link.edit')) { $createBlock[] = n.tag( sLink('link', 'link_edit', gTxt('add_new_link'), 'txp-button'), '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') ); } else { echo $contentBlockStart. $createBlock. graf( span(null, array('class' => 'ui-icon ui-icon-info')).' '. gTxt('no_links_recorded'), array('class' => 'alert-block information') ); } echo n.tag_end('div'). // End of .txp-layout-1col. n.'
'; // End of .txp-layout.; return; } $limit = max($link_list_pageby, 15); list($page, $offset, $numPages) = pager($total, $limit, $page); echo $searchBlock.$contentBlockStart.$createBlock; $rs = safe_query( "SELECT txp_link.id, UNIX_TIMESTAMP(txp_link.date) AS uDate, txp_link.category, txp_link.url, txp_link.linkname, txp_link.description, txp_link.author, txp_users.RealName AS realname, txp_category.Title AS category_title FROM $sql_from WHERE $criteria ORDER BY $sort_sql LIMIT $offset, $limit" ); if ($rs && numRows($rs)) { $show_authors = !has_single_author('txp_link'); echo n.tag( toggle_box('links_detail'), 'div', array('class' => 'txp-list-options')). n.tag_start('form', array( 'class' => 'multi_edit_form', 'id' => 'links_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( 'ID', 'id', 'link', true, $switch_dir, $crit, $search_method, (('id' == $sort) ? "$dir " : '').'txp-list-col-id' ). column_head( 'link_name', 'name', 'link', true, $switch_dir, $crit, $search_method, (('name' == $sort) ? "$dir " : '').'txp-list-col-name' ). column_head( 'description', 'description', 'link', true, $switch_dir, $crit, $search_method, (('description' == $sort) ? "$dir " : '').'txp-list-col-description links_detail' ). column_head( 'link_category', 'category', 'link', true, $switch_dir, $crit, $search_method, (('category' == $sort) ? "$dir " : '').'txp-list-col-category category' ). column_head( 'url', 'url', 'link', true, $switch_dir, $crit, $search_method, (('url' == $sort) ? "$dir " : '').'txp-list-col-url' ). column_head( 'date', 'date', 'link', true, $switch_dir, $crit, $search_method, (('date' == $sort) ? "$dir " : '').'txp-list-col-created date links_detail' ). ( $show_authors ? column_head('author', 'author', 'link', true, $switch_dir, $crit, $search_method, (('author' == $sort) ? "$dir " : '').'txp-list-col-author name') : '' ) ). n.tag_end('thead'). n.tag_start('tbody'); $validator = new Validator(); while ($a = nextRow($rs)) { extract($a, EXTR_PREFIX_ALL, 'link'); $edit_url = array( 'event' => 'link', 'step' => 'link_edit', 'id' => $link_id, 'sort' => $sort, 'dir' => $dir, 'page' => $page, 'search_method' => $search_method, 'crit' => $crit, ); $validator->setConstraints(array(new CategoryConstraint($link_category, array('type' => 'link')))); $vc = $validator->validate() ? '' : ' error'; if ($link_category) { $link_category = span(txpspecialchars($link_category_title), array('title' => $link_category)); } $can_edit = has_privs('link.edit') || ($link_author === $txp_user && has_privs('link.edit.own')); $view_url = txpspecialchars($link_url); echo tr( td( fInput('checkbox', 'selected[]', $link_id), '', 'txp-list-col-multi-edit' ). hCell( ($can_edit ? href($link_id, $edit_url, ' title="'.gTxt('edit').'"') : $link_id), '', ' class="txp-list-col-id" scope="row"' ). td( ($can_edit ? href(txpspecialchars($link_linkname), $edit_url, ' title="'.gTxt('edit').'"') : txpspecialchars($link_linkname)), '', 'txp-list-col-name' ). td( txpspecialchars($link_description), '', 'txp-list-col-description links_detail' ). td( $link_category, '', 'txp-list-col-category category'.$vc ). td( href($view_url, $view_url, ' rel="external" target="_blank"'), '', 'txp-list-col-url' ). td( gTime($link_uDate), '', 'txp-list-col-created date links_detail' ). ( $show_authors ? td(span(txpspecialchars($link_realname), array('title' => $link_author)), '', 'txp-list-col-author name') : '' ) ); } echo n.tag_end('tbody'). n.tag_end('table'). n.tag_end('div'). link_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('link', $link_list_pageby). nav_form('link', $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 link editor panel. * * @param string|array $message The activity message */ function link_edit($message = '') { global $vars, $event, $step, $txp_user; pagetop(gTxt('tab_link'), $message); extract(array_map('assert_string', gpsa($vars))); $is_edit = ($id && $step == 'link_edit'); $rs = array(); if ($is_edit) { $id = assert_int($id); $rs = safe_row("*", 'txp_link', "id = $id"); if ($rs) { extract($rs); if (!has_privs('link.edit') && !($author === $txp_user && has_privs('link.edit.own'))) { link_list(gTxt('restricted_area')); return; } } } if (has_privs('link.edit') || has_privs('link.edit.own')) { $caption = gTxt(($is_edit) ? 'edit_link' : 'add_new_link'); echo form( hed($caption, 2). inputLabel( 'link_name', fInput('text', 'linkname', $linkname, '', '', '', INPUT_REGULAR, '', 'link_name'), 'title', '', array('class' => 'txp-form-field edit-link-name') ). inputLabel( 'link_sort', fInput('text', 'linksort', $linksort, 'input-medium', '', '', INPUT_MEDIUM, '', 'link_sort'), 'sort_value', 'link_sort', array('class' => 'txp-form-field edit-link-sort') ). // TODO: maybe use type="url" once browsers are less strict. inputLabel( 'link_url', fInput('text', 'url', $url, '', '', '', INPUT_REGULAR, '', 'link_url'), 'url', 'link_url', array('class' => 'txp-form-field edit-link-url') ). inputLabel( 'link_category', event_category_popup('link', $category, 'link_category'). n.eLink('category', 'list', '', '', gTxt('edit'), '', '', '', 'txp-option-link'), 'link_category', 'link_category', array('class' => 'txp-form-field edit-link-category') ). inputLabel( 'link_description', '', 'description', 'link_description', array('class' => 'txp-form-field txp-form-field-textarea edit-link-description') ). pluggable_ui('link_ui', 'extend_detail_form', '', $rs). graf( sLink('link', '', gTxt('cancel'), 'txp-button'). fInput('submit', '', gTxt('save'), 'publish'), array('class' => 'txp-edit-actions') ). eInput('link'). sInput('link_save'). hInput('id', $id). hInput('search_method', gps('search_method')). hInput('crit', gps('crit')), '', '', 'post', 'txp-edit', '', 'link_details'); } } /** * Legacy link category HTML select field. * * @param string $cat * @return string * @deprecated in 4.6.0 */ function linkcategory_popup($cat = '') { return event_category_popup('link', $cat, 'link_category'); } // ------------------------------------------------------------- function link_save() { global $vars, $txp_user; $varray = array_map('assert_string', gpsa($vars)); extract(doSlash($varray)); if ($id) { $id = $varray['id'] = assert_int($id); } if ($linkname === '' && $url === '' && $description === '') { link_list(array(gTxt('link_empty'), E_ERROR)); return; } $author = fetch('author', 'txp_link', 'id', $id); if (!has_privs('link.edit') && !($author === $txp_user && has_privs('link.edit.own'))) { link_list(gTxt('restricted_area')); return; } if (!$linksort) { $linksort = $linkname; } $constraints = array( 'category' => new CategoryConstraint($varray['category'], array('type' => 'link')), ); callback_event_ref('link_ui', 'validate_save', 0, $varray, $constraints); $validator = new Validator($constraints); if ($validator->validate()) { if ($id) { $ok = safe_update('txp_link', "category = '$category', url = '".trim($url)."', linkname = '$linkname', linksort = '$linksort', description = '$description', author = '".doSlash($txp_user)."'", "id = $id" ); } else { $ok = safe_insert('txp_link', "category = '$category', date = NOW(), url = '".trim($url)."', linkname = '$linkname', linksort = '$linksort', description = '$description', author = '".doSlash($txp_user)."'" ); if ($ok) { $GLOBALS['ID'] = $_POST['id'] = $ok; } } if ($ok) { // update lastmod due to link feeds update_lastmod('link_saved', compact('id', 'linkname', 'linksort', 'url', 'category', 'description')); $message = gTxt(($id ? 'link_updated' : 'link_created'), array('{name}' => doStrip($linkname))); } else { $message = array(gTxt('link_save_failed'), E_ERROR); } } else { $message = array(gTxt('link_save_failed'), E_ERROR); } link_list($message); } // ------------------------------------------------------------- function link_change_pageby() { event_change_pageby('link'); link_list(); } // ------------------------------------------------------------- function link_multiedit_form($page, $sort, $dir, $crit, $search_method) { global $all_link_cats, $all_link_authors; $categories = $all_link_cats ? treeSelectInput('category', $all_link_cats, '') : ''; $authors = $all_link_authors ? selectInput('author', $all_link_authors, '', true) : ''; $methods = array( 'changecategory' => array('label' => gTxt('changecategory'), 'html' => $categories), 'changeauthor' => array('label' => gTxt('changeauthor'), 'html' => $authors), 'delete' => gTxt('delete'), ); if (!$categories) { unset($methods['changecategory']); } if (has_single_author('txp_link') || !has_privs('link.edit')) { unset($methods['changeauthor']); } if (!has_privs('link.delete.own') && !has_privs('link.delete')) { unset($methods['delete']); } return multi_edit($methods, 'link', 'link_multi_edit', $page, $sort, $dir, $crit, $search_method); } // ------------------------------------------------------------- function link_multi_edit() { global $txp_user, $all_link_cats, $all_link_authors; // Empty entry to permit clearing the category $categories = array(''); foreach ($all_link_cats as $row) { $categories[] = $row['name']; } $selected = ps('selected'); if (!$selected or !is_array($selected)) { link_list(); return; } $selected = array_map('assert_int', $selected); $method = ps('edit_method'); $changed = array(); $key = ''; switch ($method) { case 'delete' : if (!has_privs('link.delete')) { if (has_privs('link.delete.own')) { $selected = safe_column("id", 'txp_link', "id IN (".join(',', $selected).") AND author = '".doSlash($txp_user)."'"); } else { $selected = array(); } } foreach ($selected as $id) { if (safe_delete('txp_link', "id = $id")) { $changed[] = $id; } } if ($changed) { callback_event('links_deleted', '', 0, $changed); } $key = ''; break; case 'changecategory': $val = ps('category'); if (in_array($val, $categories)) { $key = 'category'; } break; case 'changeauthor': $val = ps('author'); if (has_privs('link.edit') && in_array($val, $all_link_authors)) { $key = 'author'; } break; default: $key = ''; $val = ''; break; } if (!has_privs('link.edit')) { if (has_privs('link.edit.own')) { $selected = safe_column("id", 'txp_link', "id IN (".join(',', $selected).") AND author = '".doSlash($txp_user)."'"); } else { $selected = array(); } } if ($selected and $key) { foreach ($selected as $id) { if (safe_update('txp_link', "$key = '".doSlash($val)."'", "id = $id")) { $changed[] = $id; } } } if ($changed) { update_lastmod('link_updated', $changed); link_list(gTxt( ($method == 'delete' ? 'links_deleted' : 'link_updated'), array(($method == 'delete' ? '{list}' : '{name}') => join(', ', $changed)))); return; } link_list(); }