t('status'),
'options' => array(
'status-1' => t('Published'),
'status-0' => t('Not published'),
'promote-1' => t('Promoted'),
'promote-0' => t('Not promoted'),
'sticky-1' => t('Sticky'),
'sticky-0' => t('Not sticky'),
'translate-0' => t('Up to date translation'),
'translate-1' => t('Outdated translation'),
),
);
$filters['type'] = array(
'title' => t('type'),
'options' => translation_overview_node_types(),
);
// The taxonomy filter
if ($taxonomy = module_invoke('taxonomy', 'form_all', 1)) {
$filters['category'] = array('title' => t('category'), 'options' => $taxonomy);
}
return $filters;
}
/**
* Return form for node administration filters.
*
* This is a fork of node_filter_form().
*/
function translation_overview_filter_form() {
// We reuse a bunch of the node.module's stuff.
module_load_include('inc', 'node', 'node.admin');
$session = &$_SESSION['translation_overview_filter'];
$session = is_array($session) ? $session : array();
$filters = translation_overview_node_filters();
$i = 0;
$form['filters'] = array(
'#type' => 'fieldset',
'#title' => t('Show only items where'),
'#theme' => 'node_filters',
);
$form['#submit'][] = 'translation_overview_filter_form_submit';
foreach ($session as $filter) {
list($type, $value) = $filter;
if ($type == 'category') {
// Load term name from DB rather than search and parse options array.
$value = module_invoke('taxonomy', 'get_term', $value);
$value = $value->name;
}
else {
$value = $filters[$type]['options'][$value];
}
if ($i++) {
$form['filters']['current'][] = array('#value' => t('and where %a is %b', array('%a' => $filters[$type]['title'], '%b' => $value)));
}
else {
$form['filters']['current'][] = array('#value' => t('%a is %b', array('%a' => $filters[$type]['title'], '%b' => $value)));
}
if (in_array($type, array('type', 'language'))) {
// Remove the option if it is already being filtered on.
unset($filters[$type]);
}
}
foreach ($filters as $key => $filter) {
$names[$key] = $filter['title'];
$form['filters']['status'][$key] = array('#type' => 'select', '#options' => $filter['options']);
}
$form['filters']['filter'] = array('#type' => 'radios', '#options' => $names, '#default_value' => 'status');
$form['filters']['buttons']['submit'] = array('#type' => 'submit', '#value' => (count($session) ? t('Refine') : t('Filter')));
if (count($session)) {
$form['filters']['buttons']['undo'] = array('#type' => 'submit', '#value' => t('Undo'));
$form['filters']['buttons']['reset'] = array('#type' => 'submit', '#value' => t('Reset'));
}
drupal_add_js('misc/form.js', 'core');
return $form;
}
/**
* Process result from node administration filter form.
*
* This is a fork of node_filter_form().
*/
function translation_overview_filter_form_submit($form, &$form_state) {
$filters = translation_overview_node_filters();
switch ($form_state['values']['op']) {
case t('Filter'):
case t('Refine'):
if (isset($form_state['values']['filter'])) {
$filter = $form_state['values']['filter'];
// Flatten the options array to accommodate hierarchical/nested options.
$flat_options = form_options_flatten($filters[$filter]['options']);
if (isset($flat_options[$form_state['values'][$filter]])) {
$_SESSION['translation_overview_filter'][] = array($filter, $form_state['values'][$filter]);
}
}
break;
case t('Undo'):
array_pop($_SESSION['translation_overview_filter']);
break;
case t('Reset'):
$_SESSION['translation_overview_filter'] = array();
break;
}
}
/**
* Build the where clause for a filtered query.
*
* This is a fork of node_filter_form_submit().
*/
function translation_overview_build_filter_query() {
$filters = translation_overview_node_filters();
// Build query
$join = $where = $args = array();
if (!$_SESSION['translation_overview_filter']) {
$_SESSION['translation_overview_filter'] = array();
}
foreach ($_SESSION['translation_overview_filter'] as $index => $filter) {
list($key, $value) = $filter;
switch ($key) {
case 'status':
// Note: no exploitable hole as $key/$value have already been checked when submitted
list($key, $value) = explode('-', $value, 2);
$where['status'] = 'n.'. $key .' = %d';
break;
case 'category':
$table = "tn$index";
$where[$table] = "$table.tid = %d";
$join[$table] = "INNER JOIN {term_node} $table ON n.nid = $table.nid ";
break;
case 'type':
$where['type'] = "n.type = '%s'";
break;
}
$args[] = $value;
}
// Make sure we limit it to translation enabled types.
if (!isset($where['type'])) {
$types = array_keys(translation_overview_node_types());
$where[] = 'n.type IN ('. db_placeholders($types, 'varchar') .')';
$args = array_merge($args, $types);
}
return array('where' => $where, 'join' => $join, 'args' => $args);
}
/**
* Translation overview page.
*
* Current assumptions:
* - translated node's status is synchronized via the i18n_sync module.
*/
function translation_overview_overview_page() {
drupal_add_css(drupal_get_path('module', 'translation_overview') .'/translation_overview.css');
$rows_per_page = 30;
// Bail if there are no translatable nodes
if (count(translation_overview_node_types()) == 0) {
drupal_set_message(t('There are no translateable node types on this site.'), 'error');
return '';
}
// Get a list of the enabled languages
$languages = array();
foreach (language_list() as $key => $language) {
if ($language->enabled) {
$languages[$key] = $language->language;
}
}
// Sort the list so the order matches the menu items.
asort($languages);
$header = array(
array('field' => 'n.title', 'data' => t('Title'), 'sort' => 'asc'),
array('field' => 'n.type', 'data' => t('Type')),
array('field' => 'n.created', 'data' => t('Created')),
);
foreach ($languages as $key => $language_name) {
$header[] = array('data' => str_replace('-', '
', $language_name), 'class' => 'translation-overview-lang');
}
$query = translation_overview_build_filter_query();
$sql = "SELECT n.nid, n.title, n.type, n.tnid, n.status, n.created, n.language, n.translate
FROM {node} n ". implode(' ', $query['join']) ." WHERE (n.nid = n.tnid OR n.tnid = 0) AND n.language <> '' AND n.language IS NOT NULL
AND ". implode(' AND ', $query['where']) . tablesort_sql($header);
$rows = array();
$result = pager_query(db_rewrite_sql($sql), $rows_per_page, 0, NULL, $query['args']);
while ($node = db_fetch_object($result)) {
// Shorten down the title so the table isn't too wide.
$title = $node->title;
if (drupal_strlen($title) > 20) {
$title = drupal_substr($title, 0, 15) .'...';
}
$row = array(
array('data' => l($title, 'node/'. $node->nid, array('attributes' => array('title' => $node->title)))),
array('data' => check_plain($node->type)),
array('data' => format_date($node->created, 'custom', 'j M Y')),
);
// Load the node's translations and then fill in the table with the status.
$translations = (array) translation_node_get_translations($node->tnid);
foreach ($languages as $language) {
$translation_nid = empty($translations[$language]->nid) ? NULL : $translations[$language]->nid;
$row[$language] = array(
'data' => translation_overview_translation_link($node, $translation_nid, $language, FALSE),
'class' => 'translation-overview-status',
);
}
$rows[] = $row;
}
return drupal_get_form('translation_overview_filter_form')
. theme('table', $header, $rows) . theme('pager', NULL, $rows_per_page);
}
function translation_overview_language_page($language) {
$rows_per_page = 30;
$types = array_keys(translation_overview_node_types());
// Bail if there are no translatable nodes
if (count($types) == 0) {
drupal_set_message(t('There are no translateable node types on this site.'), 'error');
return '';
}
$header = array(
array('field' => 'n.title', 'data' => t('Title')),
array('field' => 'n.type', 'data' => t('Type')),
array('field' => 'n.status', 'data' => t('Status')),
array('field' => 'n.language', 'data' => t('Language')),
array('field' => 'n.created', 'data' => t('Created')),
array('field' => 'translation_status', 'data' => t('Translated'), 'sort' => 'asc'),
);
// We want to sort the nodes by the status so we have to resort to this SQL
// CASE statement.
// NOTE: the '0 AS i18n' is a little hack to prevent i18n from re-writing our
// query.
$rows = array();
$query = translation_overview_build_filter_query();
$sql = "SELECT n.nid, n.title, n.type, n.language, n.status, n.created, n.tnid, nt.nid AS t_nid, CASE
WHEN n.language = '%s' THEN '4 LOCAL'
WHEN nt.translate = 0 THEN '3 DONE'
WHEN nt.tnid IS NULL THEN '2 MISS'
WHEN n.nid = n.tnid THEN '1 OLD'
ELSE 'unknown' END AS translation_status, 0 AS i18n
FROM {node} n LEFT JOIN {node} nt ON n.nid = nt.tnid AND nt.language = '%s' ". implode(' ', $query['join']) ."
WHERE (n.nid = n.tnid OR n.tnid = 0) AND n.language <> '' AND n.language IS NOT NULL
AND ". implode(' AND ', $query['where']) . tablesort_sql($header);
// We need to put the language that we use as an argument early in the query
// at the beginning of the arguments list.
array_unshift($query['args'], $language);
array_unshift($query['args'], $language);
$rows = array();
$result = pager_query(db_rewrite_sql($sql), $rows_per_page, 0, NULL, $query['args']);
while ($node = db_fetch_object($result)) {
// Shorten down the title
$title = $node->title;
if (drupal_strlen($title) > 25) {
$title = drupal_substr($title, 0, 20) .'...';
}
$rows[] = array(
l($title, 'node/'. $node->nid, array('attributes' => array('title' => $node->title))),
check_plain($node->type),
empty($node->status) ? t('Unpublished') : t('Published'),
$node->language,
format_date($node->created, 'custom', 'j M Y'),
translation_overview_translation_link($node, $node->t_nid, $language, TRUE),
);
}
return drupal_get_form('translation_overview_filter_form')
. theme('table', $header, $rows) . theme('pager', NULL, $rows_per_page);
}