.
*/
/**
* Images panel.
*
* @package Admin\Image
*/
use Textpattern\Validator\CategoryConstraint;
use Textpattern\Validator\Validator;
use Textpattern\Search\Filter;
if (!defined('txpinterface')) {
die('txpinterface is undefined.');
}
global $extensions;
$extensions = get_safe_image_types();
include txpath.'/lib/class.thumb.php';
if ($event == 'image') {
require_privs('image');
global $all_image_cats, $all_image_authors;
$all_image_cats = getTree('root', 'image');
$all_image_authors = the_privileged('image.edit.own', true);
$available_steps = array(
'image_list' => false,
'image_edit' => false,
'image_insert' => true,
'image_replace' => true,
'image_save' => true,
'thumbnail_insert' => true,
'image_change_pageby' => true,
'thumbnail_create' => true,
'thumbnail_delete' => true,
'image_multi_edit' => true,
);
if ($step && bouncer($step, $available_steps)) {
$step();
} else {
image_list();
}
}
/**
* The main panel listing all images.
*
* @param string|array $message The activity message
*/
function image_list($message = '')
{
global $file_max_upload_size, $txp_user, $event;
pagetop(gTxt('tab_image'), $message);
extract(gpsa(array(
'page',
'sort',
'dir',
'crit',
'search_method',
)));
if ($sort === '') {
$sort = get_pref('image_sort_column', 'id');
} else {
if (!in_array($sort, array('name', 'thumbnail', 'category', 'date', 'author'))) {
$sort = 'id';
}
set_pref('image_sort_column', $sort, 'image', PREF_HIDDEN, '', 0, PREF_PRIVATE);
}
if ($dir === '') {
$dir = get_pref('image_sort_dir', 'desc');
} else {
$dir = ($dir == 'asc') ? "asc" : "desc";
set_pref('image_sort_dir', $dir, 'image', PREF_HIDDEN, '', 0, PREF_PRIVATE);
}
switch ($sort) {
case 'name':
$sort_sql = "txp_image.name $dir";
break;
case 'thumbnail':
$sort_sql = "txp_image.thumbnail $dir, txp_image.id ASC";
break;
case 'category':
$sort_sql = "txp_category.title $dir, txp_image.id ASC";
break;
case 'date':
$sort_sql = "txp_image.date $dir, txp_image.id ASC";
break;
case 'author':
$sort_sql = "txp_users.RealName $dir, txp_image.id ASC";
break;
default:
$sort = 'id';
$sort_sql = "txp_image.id $dir";
break;
}
$switch_dir = ($dir == 'desc') ? 'asc' : 'desc';
$search = new Filter($event,
array(
'id' => array(
'column' => 'txp_image.id',
'label' => gTxt('id'),
'type' => 'integer',
),
'name' => array(
'column' => 'txp_image.name',
'label' => gTxt('name'),
),
'alt' => array(
'column' => 'txp_image.alt',
'label' => gTxt('alt_text'),
),
'caption' => array(
'column' => 'txp_image.caption',
'label' => gTxt('caption'),
),
'category' => array(
'column' => array('txp_image.category', 'txp_category.title'),
'label' => gTxt('category'),
),
'ext' => array(
'column' => 'txp_image.ext',
'label' => gTxt('extension'),
),
'author' => array(
'column' => array('txp_image.author', 'txp_users.RealName'),
'label' => gTxt('author'),
),
'thumbnail' => array(
'column' => array('txp_image.thumbnail'),
'label' => gTxt('thumbnail'),
'type' => 'boolean',
),
)
);
$alias_yes = '1, Yes';
$alias_no = '0, No';
$search->setAliases('thumbnail', array($alias_no, $alias_yes));
list($criteria, $crit, $search_method) = $search->getFilter(array('id' => array('can_list' => true)));
$search_render_options = array('placeholder' => 'search_images');
$sql_from =
safe_pfx_j('txp_image')."
LEFT JOIN ".safe_pfx_j('txp_category')." ON txp_category.name = txp_image.category AND txp_category.type = 'image'
LEFT JOIN ".safe_pfx_j('txp_users')." ON txp_users.name = txp_image.author";
if ($crit === '') {
$total = getCount('txp_image', $criteria);
} else {
$total = getThing("SELECT COUNT(*) FROM $sql_from WHERE $criteria");
}
$searchBlock =
n.tag(
$search->renderForm('image_list', $search_render_options),
'div', array(
'class' => 'txp-layout-4col-3span',
'id' => $event.'_control',
)
);
$createBlock = array();
if (!is_dir(IMPATH) or !is_writeable(IMPATH)) {
$createBlock[] =
graf(
span(null, array('class' => 'ui-icon ui-icon-alert')).' '.
gTxt('img_dir_not_writeable', array('{imgdir}' => IMPATH)),
array('class' => 'alert-block warning')
);
} elseif (has_privs('image.edit.own')) {
$categories = event_category_popup('image', '', 'image_category');
$createBlock[] =
n.tag(
n.upload_form('upload_image', 'upload_image', 'image_insert[]', 'image', '', $file_max_upload_size, '', 'async', '',
array('postinput' => ($categories
? n.tag(
n.tag(gTxt('category'), 'label', array('for' => 'image_category')).$categories.n,
'span',
array('class' => 'inline-file-uploader-actions'))
: ''
)),
'image/*'
),
'div', array('class' => 'txp-control-panel')
);
}
$createBlock = implode(n, $createBlock);
$contentBlock = '';
$paginator = new \Textpattern\Admin\Paginator();
$limit = $paginator->getLimit();
list($page, $offset, $numPages) = pager($total, $limit, $page);
if ($total < 1) {
$contentBlock .= graf(
span(null, array('class' => 'ui-icon ui-icon-info')).' '.
gTxt($crit === '' ? 'no_images_recorded' : 'no_results_found'),
array('class' => 'alert-block information')
);
} else {
$rs = safe_query(
"SELECT
txp_image.id,
txp_image.name,
txp_image.category,
txp_image.ext,
txp_image.w,
txp_image.h,
txp_image.alt,
txp_image.caption,
UNIX_TIMESTAMP(txp_image.date) AS uDate,
txp_image.author,
txp_image.thumbnail,
txp_image.thumb_w,
txp_image.thumb_h,
txp_users.RealName AS realname,
txp_category.Title AS category_title
FROM $sql_from WHERE $criteria ORDER BY $sort_sql LIMIT $offset, $limit"
);
$contentBlock .= pluggable_ui('image_ui', 'extend_controls', '', $rs);
if ($rs && numRows($rs)) {
$show_authors = !has_single_author('txp_image');
$contentBlock .= n.tag_start('form', array(
'class' => 'multi_edit_form',
'id' => 'images_form',
'name' => 'longform',
'method' => 'post',
'action' => 'index.php',
)).
n.tag_start('div', array(
'class' => 'txp-listtables',
'tabindex' => 0,
'aria-label' => gTxt('list'),
)).
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', 'image', true, $switch_dir, $crit, $search_method,
(('id' == $sort) ? "$dir " : '').'txp-list-col-id'
).
column_head(
'name', 'name', 'image', true, $switch_dir, $crit, $search_method,
(('name' == $sort) ? "$dir " : '').'txp-list-col-name'
).
column_head(
'date', 'date', 'image', true, $switch_dir, $crit, $search_method,
(('date' == $sort) ? "$dir " : '').'txp-list-col-created date'
).
column_head(
'thumbnail', 'thumbnail', 'image', true, $switch_dir, $crit, $search_method,
(('thumbnail' == $sort) ? "$dir " : '').'txp-list-col-thumbnail'
).
(has_privs('tag')
? hCell(
gTxt('tags'), '', ' class="txp-list-col-tag-build" scope="col"'
)
: ''
).
column_head(
'category', 'category', 'image', true, $switch_dir, $crit, $search_method,
(('category' == $sort) ? "$dir " : '').'txp-list-col-category category'
).
(
$show_authors
? column_head('author', 'author', 'image', 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);
$edit_url = array(
'event' => 'image',
'step' => 'image_edit',
'id' => $id,
'sort' => $sort,
'dir' => $dir,
'page' => $page,
'search_method' => $search_method,
'crit' => $crit,
);
$name = empty($name) ? 'unnamed' : txpspecialchars($name);
if ($thumbnail) {
if ($ext != '.swf') {
$thumbnail = '';
$thumbexists = 1;
} else {
$thumbnail = '';
$thumbexists = '';
}
} else {
$thumbnail = gTxt('no');
$thumbexists = '';
}
if ($ext != '.swf') {
$tagName = 'image';
$tag_url = array(
'id' => $id,
'ext' => $ext,
'w' => $w,
'h' => $h,
'alt' => urlencode($alt),
'caption' => urlencode($caption),
'step' => 'build',
);
$tagbuilder = popTag($tagName, 'Textile', array('type' => 'textile') + $tag_url).
sp.span('|', array('role' => 'separator')).
sp.popTag($tagName, 'Textpattern', array('type' => 'textpattern') + $tag_url).
sp.span('|', array('role' => 'separator')).
sp.popTag($tagName, 'HTML', array('type' => 'html') + $tag_url);
} else {
$tagbuilder = sp;
}
$validator->setConstraints(array(new CategoryConstraint($category, array('type' => 'image'))));
$vc = $validator->validate() ? '' : ' error';
if ($category) {
$category = span(txpspecialchars($category_title), array('title' => $category));
}
$can_view = has_privs('image.edit.own');
$can_edit = has_privs('image.edit') || ($author === $txp_user && $can_view);
$contentBlock .= tr(
td(
$can_edit ? fInput('checkbox', 'selected[]', $id) : ' ', '', 'txp-list-col-multi-edit'
).
hCell(
($can_view ? href($id, $edit_url, array('title' => gTxt('edit'))) : $id)
, '', array(
'class' => 'txp-list-col-id',
'scope' => 'row',
)
).
td(
($can_view ? href($name, $edit_url, ' title="'.gTxt('edit').'"') : $name), '', 'txp-list-col-name txp-contain'
).
td(
gTime($uDate), '', 'txp-list-col-created date'
).
td(
pluggable_ui('image_ui', 'thumbnail', ($can_edit ? href($thumbnail, $edit_url) : $thumbnail), $a), '', 'txp-list-col-thumbnail'.($thumbexists ? ' has-thumbnail' : '')
).
(has_privs('tag')
? td(
$tagbuilder, '', 'txp-list-col-tag-build'
)
: ''
).
td(
$category, '', 'txp-list-col-category category'.$vc
).
(
$show_authors
? td(span(txpspecialchars($realname), array('title' => $author)), '', 'txp-list-col-author name')
: ''
)
);
}
$contentBlock .= n.tag_end('tbody').
n.tag_end('table').
n.tag_end('div'). // End of .txp-listtables.
image_multiedit_form($page, $sort, $dir, $crit, $search_method).
tInput().
n.tag_end('form');
}
}
$pageBlock = $paginator->render().
nav_form($event, $page, $numPages, $sort, $dir, $crit, $search_method, $total, $limit);
$table = new \Textpattern\Admin\Table($event);
echo $table->render(compact('total', 'crit'), $searchBlock, $createBlock, $contentBlock, $pageBlock).
n.tag(
null,
'div', array(
'class' => 'txp-tagbuilder-content',
'id' => 'tagbuild_links',
'aria-label' => gTxt('tagbuilder'),
'title' => gTxt('tagbuilder'),
));
}
/**
* Renders a multi-edit form widget for images.
*
* @param int $page The page number
* @param string $sort The current sort value
* @param string $dir The current sort direction
* @param string $crit The current search criteria
* @param string $search_method The current search method
* @return string HTML
*/
function image_multiedit_form($page, $sort, $dir, $crit, $search_method)
{
global $all_image_cats, $all_image_authors;
$categories = $all_image_cats ? treeSelectInput('category', $all_image_cats, '') : '';
$authors = $all_image_authors ? selectInput('author', $all_image_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_image') || !has_privs('image.edit')) {
unset($methods['changeauthor']);
}
if (!has_privs('image.delete.own') && !has_privs('image.delete')) {
unset($methods['delete']);
}
return multi_edit($methods, 'image', 'image_multi_edit', $page, $sort, $dir, $crit, $search_method);
}
/**
* Processes multi-edit actions.
*/
function image_multi_edit()
{
global $txp_user, $all_image_cats, $all_image_authors;
// Empty entry to permit clearing the category.
$categories = array('');
foreach ($all_image_cats as $row) {
$categories[] = $row['name'];
}
$selected = ps('selected');
if (!$selected || !is_array($selected)) {
return image_list();
}
// Fetch and remove bogus (false) entries to prevent SQL syntax errors being thrown.
$selected = array_map('assert_int', $selected);
$selected = array_filter($selected);
$method = ps('edit_method');
$changed = array();
$key = '';
switch ($method) {
case 'delete':
return image_delete($selected);
break;
case 'changecategory':
$val = ps('category');
if (in_array($val, $categories)) {
$key = 'category';
}
break;
case 'changeauthor':
$val = ps('author');
if (has_privs('image.edit') && isset($all_image_authors[$val])) {
$key = 'author';
}
break;
default:
$key = '';
$val = '';
break;
}
if (!has_privs('image.edit')) {
if ($selected && has_privs('image.edit.own')) {
$selected = safe_column("id", 'txp_image', "id IN (".join(',', $selected).") AND author = '".doSlash($txp_user)."'");
} else {
$selected = array();
}
}
if ($selected && $key) {
foreach ($selected as $id) {
if (safe_update('txp_image', "$key = '".doSlash($val)."'", "id = '$id'")) {
$changed[] = $id;
}
}
}
if ($changed) {
update_lastmod('image_updated', $changed);
return image_list(gTxt('image_updated', array('{name}' => join(', ', $changed))));
}
return image_list();
}
/**
* Renders and outputs the image editor panel.
*
* @param string|array $message The activity message
* @param int $id The image ID
*/
function image_edit($message = '', $id = '')
{
global $file_max_upload_size, $txp_user, $event, $all_image_cats;
if (!$id) {
$id = gps('id');
}
$id = assert_int($id);
$rs = safe_row("*, UNIX_TIMESTAMP(date) AS uDate", 'txp_image', "id = '$id'");
if ($rs) {
extract($rs);
if (!has_privs('image.edit') && !has_privs('image.edit.own')) {
require_privs('image.edit');
return;
}
pagetop(gTxt('edit_image'), $message);
extract(gpsa(array(
'page',
'sort',
'dir',
'crit',
'search_method',
)));
if ($ext != '.swf') {
$aspect = ($h == $w) ? ' square' : (($h > $w) ? ' portrait' : ' landscape');
$img_info = $id.$ext.' ('.$w.' × '.$h.')';
$img = '