.
*/
/**
* Comments panel.
*
* @package Admin\Discuss
*/
use Textpattern\Validator\ChoiceConstraint;
use Textpattern\Validator\Validator;
use Textpattern\Search\Filter;
if (!defined('txpinterface')) {
die('txpinterface is undefined.');
}
if ($event == 'discuss') {
require_privs('discuss');
if (!get_pref('use_comments', 1)) {
require_privs();
}
$available_steps = array(
'discuss_save' => true,
'discuss_list' => false,
'discuss_edit' => false,
'discuss_multi_edit' => true,
'discuss_change_pageby' => true,
);
if ($step && bouncer($step, $available_steps)) {
$step();
} else {
discuss_list();
}
}
//-------------------------------------------------------------
function discuss_save()
{
$varray = array_map('assert_string', gpsa(array('email', 'name', 'web', 'message', 'ip')));
$varray = $varray + array_map('assert_int', gpsa(array('discussid', 'visible', 'parentid')));
extract(doSlash($varray));
$message = $varray['message'] = preg_replace('#<(/?txp:.+?)>#', '<$1>', $message);
$constraints = array(
'status' => new ChoiceConstraint($visible, array(
'choices' => array(SPAM, MODERATE, VISIBLE),
'message' => 'invalid_status',
)),
);
callback_event_ref('discuss_ui', 'validate_save', 0, $varray, $constraints);
$validator = new Validator($constraints);
if ($validator->validate() && safe_update('txp_discuss',
"email = '$email',
name = '$name',
web = '$web',
message = '$message',
visible = $visible",
"discussid = $discussid"
)) {
update_comments_count($parentid);
update_lastmod('discuss_saved', compact('discussid', 'email', 'name', 'web', 'message', 'ip', 'visible', 'parentid'));
$message = gTxt('comment_updated', array('{id}' => $discussid));
} else {
$message = array(gTxt('comment_save_failed'), E_ERROR);
}
discuss_list($message);
}
//-------------------------------------------------------------
function short_preview($message)
{
$message = strip_tags($message);
$offset = min(120, strlen($message));
if (strpos($message, ' ', $offset) !== false) {
$maxpos = strpos($message, ' ', $offset);
$message = substr($message, 0, $maxpos).'…';
}
return $message;
}
/**
* Outputs the main panel listing all comments.
*
* @param string|array $message The activity message
*/
function discuss_list($message = '')
{
global $event, $comment_list_pageby;
pagetop(gTxt('list_discussions'), $message);
extract(gpsa(array(
'sort',
'dir',
'page',
'crit',
'search_method',
)));
if ($sort === '') {
$sort = get_pref('discuss_sort_column', 'date');
} else {
if (!in_array($sort, array('id', 'ip', 'name', 'email', 'website', 'message', 'status', 'parent'))) {
$sort = 'date';
}
set_pref('discuss_sort_column', $sort, 'discuss', 2, '', 0, PREF_PRIVATE);
}
if ($dir === '') {
$dir = get_pref('discuss_sort_dir', 'desc');
} else {
$dir = ($dir == 'asc') ? "asc" : "desc";
set_pref('discuss_sort_dir', $dir, 'discuss', 2, '', 0, PREF_PRIVATE);
}
switch ($sort) {
case 'id':
$sort_sql = "txp_discuss.discussid $dir";
break;
case 'ip':
$sort_sql = "txp_discuss.ip $dir";
break;
case 'name':
$sort_sql = "txp_discuss.name $dir";
break;
case 'email':
$sort_sql = "txp_discuss.email $dir";
break;
case 'website':
$sort_sql = "txp_discuss.web $dir";
break;
case 'message':
$sort_sql = "txp_discuss.message $dir";
break;
case 'status':
$sort_sql = "txp_discuss.visible $dir";
break;
case 'parent':
$sort_sql = "txp_discuss.parentid $dir";
break;
default:
$sort = 'date';
$sort_sql = "txp_discuss.posted $dir";
break;
}
if ($sort != 'date') {
$sort_sql .= ", txp_discuss.posted ASC";
}
$switch_dir = ($dir == 'desc') ? 'asc' : 'desc';
$search = new Filter($event,
array(
'id' => array(
'column' => 'txp_discuss.discussid',
'label' => gTxt('ID'),
'type' => 'integer',
),
'parent' => array(
'column' => array('txp_discuss.parentid', 'textpattern.Title'),
'label' => gTxt('parent'),
),
'name' => array(
'column' => 'txp_discuss.name',
'label' => gTxt('name'),
),
'message' => array(
'column' => 'txp_discuss.message',
'label' => gTxt('message'),
),
'email' => array(
'column' => 'txp_discuss.email',
'label' => gTxt('email'),
),
'website' => array(
'column' => 'txp_discuss.web',
'label' => gTxt('website'),
),
'ip' => array(
'column' => 'txp_discuss.ip',
'label' => gTxt('IP'),
),
'visible' => array(
'column' => 'txp_discuss.visible',
'label' => gTxt('visible'),
'type' => 'numeric',
),
)
);
$alias_yes = VISIBLE.', Yes';
$alias_no = MODERATE.', No, Unmoderated, Pending';
$alias_spam = SPAM.', Spam';
$search->setAliases('visible', array(
VISIBLE => $alias_yes,
MODERATE => $alias_no,
SPAM => $alias_spam,
));
list($criteria, $crit, $search_method) = $search->getFilter(array(
'id' => array('can_list' => true),
));
$search_render_options = array(
'placeholder' => 'search_comments',
);
$sql_from =
safe_pfx_j('txp_discuss')."
left join ".safe_pfx_j('textpattern')." on txp_discuss.parentid = textpattern.ID";
$counts = getRows(
"SELECT txp_discuss.visible, COUNT(*) AS c
FROM ".safe_pfx_j('txp_discuss')."
LEFT JOIN ".safe_pfx_j('textpattern')."
ON txp_discuss.parentid = textpattern.ID
WHERE $criteria GROUP BY txp_discuss.visible"
);
$count[SPAM] = $count[MODERATE] = $count[VISIBLE] = 0;
if ($counts) {
foreach ($counts as $c) {
$count[$c['visible']] = $c['c'];
}
}
// Grand total comment count.
$total = $count[SPAM] + $count[MODERATE] + $count[VISIBLE];
echo n.'
'.
n.tag(
hed(gTxt('list_discussions'), 1, array('class' => 'txp-heading')),
'div', array('class' => 'txp-layout-4col-alt')
);
$searchBlock =
n.tag(
$search->renderForm('discuss_list', $search_render_options),
'div', array(
'class' => 'txp-layout-4col-3span',
'id' => $event.'_control',
)
);
$contentBlockStart = n.tag_start('div', array(
'class' => 'txp-layout-1col',
'id' => $event.'_container',
));
if ($total < 1) {
if ($criteria != 1) {
echo $searchBlock.
$contentBlockStart.
graf(
span(null, array('class' => 'ui-icon ui-icon-info')).' '.
gTxt('no_results_found'),
array('class' => 'alert-block information')
);
} else {
echo $contentBlockStart.
graf(
span(null, array('class' => 'ui-icon ui-icon-info')).' '.
gTxt('no_comments_recorded'),
array('class' => 'alert-block information')
);
}
echo n.tag_end('div'). // End of .txp-layout-1col.
n.'
'; // End of .txp-layout.
return;
}
if (!cs('toggle_show_spam')) {
$total = $count[MODERATE] + $count[VISIBLE];
$criteria = 'visible != '.intval(SPAM).' and '.$criteria;
}
$limit = max($comment_list_pageby, 15);
list($page, $offset, $numPages) = pager($total, $limit, $page);
echo $searchBlock.$contentBlockStart;
$rs = safe_query(
"SELECT
txp_discuss.discussid,
txp_discuss.parentid,
txp_discuss.name,
txp_discuss.email,
txp_discuss.web,
txp_discuss.ip,
txp_discuss.message,
txp_discuss.visible,
UNIX_TIMESTAMP(txp_discuss.posted) AS uPosted,
textpattern.ID AS thisid,
textpattern.Section AS section,
textpattern.url_title,
textpattern.Title AS title,
textpattern.Status,
UNIX_TIMESTAMP(textpattern.Posted) AS posted
FROM ".safe_pfx_j('txp_discuss')."
LEFT JOIN ".safe_pfx_j('textpattern')." ON txp_discuss.parentid = textpattern.ID
WHERE $criteria ORDER BY $sort_sql LIMIT $offset, $limit"
);
if ($rs) {
echo n.tag(
cookie_box('show_spam').
toggle_box('discuss_detail'),
'div', array('class' => 'txp-list-options')).
n.tag_start('form', array(
'class' => 'multi_edit_form',
'id' => 'discuss_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', 'discuss', true, $switch_dir, $crit, $search_method,
(('id' == $sort) ? "$dir " : '').'txp-list-col-id'
).
column_head(
'date', 'date', 'discuss', true, $switch_dir, $crit, $search_method,
(('date' == $sort) ? "$dir " : '').'txp-list-col-created date'
).
column_head(
'name', 'name', 'discuss', true, $switch_dir, $crit, $search_method,
(('name' == $sort) ? "$dir " : '').'txp-list-col-name'
).
column_head(
'message', 'message', 'discuss', true, $switch_dir, $crit, $search_method,
(('message' == $sort) ? "$dir " : 'txp-list-col-message')
).
column_head(
'email', 'email', 'discuss', true, $switch_dir, $crit, $search_method,
(('email' == $sort) ? "$dir " : '').'txp-list-col-email discuss_detail'
).
column_head(
'website', 'website', 'discuss', true, $switch_dir, $crit, $search_method,
(('website' == $sort) ? "$dir " : '').'txp-list-col-website discuss_detail'
).
column_head(
'IP', 'ip', 'discuss', true, $switch_dir, $crit, $search_method,
(('ip' == $sort) ? "$dir " : '').'txp-list-col-ip discuss_detail'
).
column_head(
'status', 'status', 'discuss', true, $switch_dir, $crit, $search_method,
(('status' == $sort) ? "$dir " : '').'txp-list-col-status'
).
column_head(
'parent', 'parent', 'discuss', true, $switch_dir, $crit, $search_method,
(('parent' == $sort) ? "$dir " : '').'txp-list-col-parent'
)
).
n.tag_end('thead');
include_once txpath.'/publish/taghandlers.php';
echo n.tag_start('tbody');
while ($a = nextRow($rs)) {
extract($a);
$parentid = assert_int($parentid);
$edit_url = array(
'event' => 'discuss',
'step' => 'discuss_edit',
'discussid' => $discussid,
'sort' => $sort,
'dir' => $dir,
'page' => $page,
'search_method' => $search_method,
'crit' => $crit,
);
$dmessage = ($visible == SPAM) ? short_preview($message) : $message;
switch ($visible) {
case VISIBLE:
$comment_status = gTxt('visible');
$row_class = 'visible';
break;
case SPAM:
$comment_status = gTxt('spam');
$row_class = 'spam';
break;
case MODERATE:
$comment_status = gTxt('unmoderated');
$row_class = 'moderate';
break;
default:
break;
}
if (empty($thisid)) {
$parent = gTxt('article_deleted').' ('.$parentid.')';
$view = '';
} else {
$parent_title = empty($title) ? ''.gTxt('untitled').'' : escape_title($title);
$parent = href($parent_title, '?event=article'.a.'step=edit'.a.'ID='.$parentid);
$view = $comment_status;
if ($visible == VISIBLE and in_array($Status, array(4, 5))) {
$view = href($comment_status, permlinkurl($a).'#c'.$discussid, ' title="'.gTxt('view').'"');
}
}
echo tr(
td(
fInput('checkbox', 'selected[]', $discussid), '', 'txp-list-col-multi-edit'
).
hCell(
href($discussid, $edit_url, ' title="'.gTxt('edit').'"'), '', ' class="txp-list-col-id" scope="row"'
).
td(
gTime($uPosted), '', 'txp-list-col-created date'
).
td(
txpspecialchars(soft_wrap($name, 15)), '', 'txp-list-col-name'
).
td(
short_preview($dmessage), '', 'txp-list-col-message'
).
td(
txpspecialchars(soft_wrap($email, 15)), '', 'txp-list-col-email discuss_detail'
).
td(
txpspecialchars(soft_wrap($web, 15)), '', 'txp-list-col-website discuss_detail'
).
td(
href(txpspecialchars($ip), 'https://whois.domaintools.com/'.rawurlencode($ip), array(
'rel' => 'external',
'target' => '_blank',
)), '', 'txp-list-col-ip discuss_detail'
).
td(
$view, '', 'txp-list-col-status'
).
td(
$parent, '', 'txp-list-col-parent'
), ' class="'.$row_class.'"'
);
}
if (empty($message)) {
echo n.tr(tda(gTxt('just_spam_results_found'), ' colspan="10"'));
}
echo n.tag_end('tbody').
n.tag_end('table').
n.tag_end('div'). // End of .txp-listtables.
discuss_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('discuss', $comment_list_pageby).
nav_form('discuss', $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 comment editor panel.
*/
function discuss_edit()
{
global $event;
pagetop(gTxt('edit_comment'));
extract(gpsa(array(
'discussid',
'sort',
'dir',
'page',
'crit',
'search_method',
)));
$discussid = assert_int($discussid);
$rs = safe_row("*, UNIX_TIMESTAMP(posted) AS uPosted", 'txp_discuss', "discussid = $discussid");
if ($rs) {
extract($rs);
$message = txpspecialchars($message);
$status_list = selectInput(
'visible',
array(
VISIBLE => gTxt('visible'),
SPAM => gTxt('spam'),
MODERATE => gTxt('unmoderated'),
),
$visible,
false,
'',
'status');
echo form(
hed(gTxt('edit_comment'), 2).
inputLabel(
'status',
$status_list,
'status', '', array('class' => 'txp-form-field edit-comment-status')
).
inputLabel(
'name',
fInput('text', 'name', $name, '', '', '', INPUT_REGULAR, '', 'name'),
'name', '', array('class' => 'txp-form-field edit-comment-name')
).
inputLabel(
'IP',
href(txpspecialchars($ip), 'https://whois.domaintools.com/'.rawurlencode($ip), array(
'rel' => 'external',
'target' => '_blank',
)),
'', '', array('class' => 'txp-form-field edit-comment-ip')
).
inputLabel(
'email',
fInput('email', 'email', $email, '', '', '', INPUT_REGULAR, '', 'email'),
'email', '', array('class' => 'txp-form-field edit-comment-email')
).
inputLabel(
'website',
fInput('text', 'web', $web, '', '', '', INPUT_REGULAR, '', 'website'),
'website', '', array('class' => 'txp-form-field edit-comment-website')
).
inputLabel(
'date',
safe_strftime('%d %b %Y %X',
$uPosted),
'', '', array('class' => 'txp-form-field edit-comment-date')
).
inputLabel(
'commentmessage',
'',
'message', '', array('class' => 'txp-form-field txp-form-field-textarea edit-comment-message')
).
graf(
sLink('discuss', '', gTxt('cancel'), 'txp-button').
fInput('submit', 'step', gTxt('save'), 'publish'),
array('class' => 'txp-edit-actions')
).
hInput('sort', $sort).
hInput('dir', $dir).
hInput('page', $page).
hInput('crit', $crit).
hInput('search_method', $search_method).
hInput('discussid', $discussid).
hInput('parentid', $parentid).
hInput('ip', $ip).
eInput('discuss').
sInput('discuss_save'),
'', '', 'post', 'txp-edit', '', 'discuss_edit_form');
} else {
echo graf(
span(null, array('class' => 'ui-icon ui-icon-info')).' '.
gTxt('comment_not_found'),
array('class' => 'alert-block information')
);
}
}
// -------------------------------------------------------------
function discuss_change_pageby()
{
event_change_pageby('comment');
discuss_list();
}
// -------------------------------------------------------------
function discuss_multiedit_form($page, $sort, $dir, $crit, $search_method)
{
$methods = array(
'visible' => gTxt('show'),
'unmoderated' => gTxt('hide_unmoderated'),
'spam' => gTxt('hide_spam'),
'delete' => gTxt('delete'),
);
return multi_edit($methods, 'discuss', 'discuss_multi_edit', $page, $sort, $dir, $crit, $search_method);
}
// -------------------------------------------------------------
function discuss_multi_edit()
{
// FIXME: this method needs some refactoring.
$selected = ps('selected');
$method = ps('edit_method');
$done = array();
if ($selected and is_array($selected)) {
// Get all articles for which we have to update the count.
foreach ($selected as $id) {
$ids[] = assert_int($id);
}
$parentids = safe_column("DISTINCT parentid", 'txp_discuss', "discussid IN (".implode(',', $ids).")");
$rs = safe_rows_start("*", 'txp_discuss', "discussid IN (".implode(',', $ids).")");
while ($row = nextRow($rs)) {
extract($row);
$id = assert_int($discussid);
$parentids[] = $parentid;
if ($method == 'delete') {
// Delete and, if successful, update comment count.
if (safe_delete('txp_discuss', "discussid = $id")) {
$done[] = $id;
}
callback_event('discuss_deleted', '', 0, $done);
} elseif ($method == 'spam') {
if (safe_update('txp_discuss',
"visible = ".SPAM,
"discussid = $id"
)) {
$done[] = $id;
}
} elseif ($method == 'unmoderated') {
if (safe_update('txp_discuss',
"visible = ".MODERATE,
"discussid = $id"
)) {
$done[] = $id;
}
} elseif ($method == 'visible') {
if (safe_update('txp_discuss',
"visible = ".VISIBLE,
"discussid = $id"
)) {
$done[] = $id;
}
}
}
$doneStr = join(', ', $done);
if ($doneStr) {
// Might as well clean up all comment counts while we're here.
clean_comment_counts($parentids);
$messages = array(
'delete' => gTxt('comments_deleted', array('{list}' => $doneStr)),
'spam' => gTxt('comments_marked_spam', array('{list}' => $doneStr)),
'unmoderated' => gTxt('comments_marked_unmoderated', array('{list}' => $doneStr)),
'visible' => gTxt('comments_marked_visible', array('{list}' => $doneStr)),
);
update_lastmod('discuss_updated', $done);
return discuss_list($messages[$method]);
}
}
return discuss_list();
}