Skip to content
Snippets Groups Projects
Commit 34676aca authored by Jared Hancock's avatar Jared Hancock Committed by Peter Rotich
Browse files

queue: Reuse column definitions

This allows for less work when designing new queues. The column design—the
data definition, annotations, and conditions, are saved with the column. The
heading, sort order, and width are set when the column is associated with a
queue. This means that changing the behavior of a particular column will
mean not having to redo work for each queue that uses the column.
parent 0991ba8e
Branches
Tags
No related merge requests found
Showing with 518 additions and 284 deletions
......@@ -133,7 +133,8 @@ class Bootstrap {
define('SEQUENCE_TABLE', $prefix.'sequence');
define('TRANSLATION_TABLE', $prefix.'translation');
define('QUEUE_TABLE', $prefix.'queue');
define('QUEUE_COLUMN_TABLE', $prefix.'queue_column');
define('COLUMN_TABLE', $prefix.'queue_column');
define('QUEUE_COLUMN_TABLE', $prefix.'queue_columns');
define('API_KEY_TABLE',$prefix.'api_key');
define('TIMEZONE_TABLE',$prefix.'timezone');
......
......@@ -190,4 +190,29 @@ class AdminAjaxAPI extends AjaxController {
include STAFFINC_DIR . 'templates/quick-add.tmpl.php';
}
function addQueueColumn($root='Ticket') {
global $ost, $thisstaff;
if (!$thisstaff)
Http::response(403, 'Agent login required');
if (!$thisstaff->isAdmin())
Http::response(403, 'Access denied');
$column = QueueColumn::create();
if ($_POST) {
$data_form = $column->getDataConfigForm($_POST);
if ($data_form->isValid()) {
$column->update($_POST, $root);
if ($column->save())
Http::response(201, $this->encode(array(
'id' => $column->getId(),
'name' => (string) $column->getName(),
), 'application/json'));
}
}
include STAFFINC_DIR . 'templates/queue-column-add.tmpl.php';
}
}
......@@ -212,19 +212,27 @@ class SearchAjaxAPI extends AjaxController {
)));
}
function editColumn($queue_id, $column) {
function editColumn($column_id) {
global $thisstaff;
if (!$thisstaff) {
Http::response(403, 'Agent login is required');
}
elseif (!($queue = CustomQueue::lookup($queue_id))) {
elseif (!($column = QueueColumn::lookup($column_id))) {
Http::response(404, 'No such queue');
}
$data_form = new QueueDataConfigForm($_POST);
include STAFFINC_DIR . 'templates/queue-column.tmpl.php';
if ($_POST) {
$data_form = $column->getDataConfigForm($_POST);
if ($data_form->isValid()) {
$column->update($_POST, 'Ticket');
if ($column->save())
Http::response(201, 'Successfully updated');
}
}
$root = 'Ticket';
include STAFFINC_DIR . 'templates/queue-column-edit.tmpl.php';
}
function previewQueue($id=false) {
......@@ -288,43 +296,4 @@ class SearchAjaxAPI extends AjaxController {
$id = $_GET['condition'];
include STAFFINC_DIR . 'templates/queue-column-condition-prop.tmpl.php';
}
function addColumn() {
global $thisstaff;
if (!$thisstaff) {
Http::response(403, 'Agent login is required');
}
elseif (!isset($_GET['field'])) {
Http::response(400, '`field` parameter is required');
}
$field = $_GET['field'];
// XXX: This method should receive a queue ID or queue root so that
// $field can be properly checked
$fields = SavedSearch::getSearchableFields('Ticket');
if (!isset($fields[$field])) {
Http::response(400, 'Not a supported field for this queue');
}
// Get the tabbed column configuration
list($label, $F) = $fields[$field];
$column = QueueColumn::create(array(
"id" => (int) $_GET['id'],
"heading" => _S($F->getLabel()),
"primary" => $field,
"width" => 100,
));
ob_start();
include STAFFINC_DIR . 'templates/queue-column.tmpl.php';
$config = ob_get_clean();
// Send back the goodies
Http::response(200, $this->encode(array(
'config' => $config,
'id' => $column->id,
'heading' => _S($F->getLabel()),
'width' => $column->getWidth(),
)), 'application/json');
}
}
......@@ -66,6 +66,7 @@ class Internationalization {
'role.yaml' => 'Role::__create',
'file.yaml' => 'AttachmentFile::__create',
'sequence.yaml' => 'Sequence::__create',
'queue_column.yaml' => 'QueueColumn::__create',
'queue.yaml' => 'CustomQueue::__create',
);
......
......@@ -21,7 +21,8 @@ class CustomQueue extends SavedSearch {
'select_related' => array('parent'),
'joins' => array(
'columns' => array(
'reverse' => 'QueueColumn.queue',
'reverse' => 'QueueColumnGlue.queue',
'broker' => 'QueueColumnListBroker',
),
'children' => array(
'reverse' => 'CustomQueue.parent',
......@@ -113,7 +114,7 @@ class CustomQueue extends SavedSearch {
// Apply column, annotations and conditions additions
foreach ($this->getColumns() as $C) {
$query = $C->mangleQuery($query);
$query = $C->mangleQuery($query, $this->getRoot());
}
return $query;
}
......@@ -145,21 +146,30 @@ class CustomQueue extends SavedSearch {
// Update queue columns (but without save)
if (isset($vars['columns'])) {
$new = $vars['columns'];
$order = array_keys($new);
foreach ($this->columns as $col) {
if (false === ($sort = array_search($col->id, $vars['columns']))) {
$key = $col->column_id;
if (!isset($vars['columns'][$key])) {
$this->columns->remove($col);
continue;
}
$col->set('sort', $sort+1);
$col->update($vars, $errors);
unset($new[$sort]);
$info = $vars['columns'][$key];
$col->set('sort', array_search($key, $order));
$col->set('heading', $info['heading']);
$col->set('width', $info['width']);
unset($new[$key]);
}
// Add new columns
foreach ($new as $sort=>$colid) {
$col = QueueColumn::create(array("id" => $colid, "queue" => $this));
$col->set('sort', $sort+1);
$col->update($vars, $errors);
$this->addColumn($col);
foreach ($new as $info) {
$glue = QueueColumnGlue::create(array(
'column_id' => $info['column_id'],
'sort' => array_search($info['column_id'], $order),
'heading' => $info['heading'],
'width' => $info['width'] ?: 100
));
$glue->queue = $this;
$this->columns->add(
QueueColumn::lookup($info['column_id']), $glue);
}
// Re-sort the in-memory columns array
$this->columns->sort(function($c) { return $c->sort; });
......@@ -193,6 +203,11 @@ class CustomQueue extends SavedSearch {
static function __create($vars) {
$q = static::create($vars);
$q->save();
foreach ($vars['columns'] as $info) {
$glue = QueueColumnGlue::create($info);
$glue->queue_id = $q->getId();
$glue->save();
}
return $q;
}
}
......@@ -628,14 +643,8 @@ extends ChoiceField {
class QueueColumn
extends VerySimpleModel {
static $meta = array(
'table' => QUEUE_COLUMN_TABLE,
'table' => COLUMN_TABLE,
'pk' => array('id'),
'ordering' => array('sort'),
'joins' => array(
'queue' => array(
'constraint' => array('queue_id' => 'SavedSearch.id'),
),
),
);
var $_annotations;
......@@ -645,22 +654,40 @@ extends VerySimpleModel {
return $this->id;
}
function getFilter() {
if ($this->filter)
return QueueColumnFilter::getInstance($this->filter);
}
function getName() {
return $this->name;
}
// These getters fetch data from the annotated overlay from the
// queue_column table
function getQueue() {
return $this->queue;
}
function getWidth() {
return $this->width ?: 100;
}
function getHeading() {
return $this->heading;
}
function getWidth() {
return $this->width ?: 100;
function getTranslateTag($subtag) {
return _H(sprintf('column.%s.%s.%s', $subtag, $this->queue_id, $this->id));
}
function getLocal($subtag) {
$tag = $this->getTranslateTag($subtag);
$T = CustomDataTranslation::translate($tag);
return $T != $tag ? $T : $this->get($subtag);
}
function getLocalHeading() {
return $this->getLocal('heading');
}
function getFilter() {
if ($this->filter)
return QueueColumnFilter::getInstance($this->filter);
}
function render($row) {
// Basic data
......@@ -732,9 +759,9 @@ extends VerySimpleModel {
return $field->addToQuery($query, $path);
}
function mangleQuery($query) {
function mangleQuery($query, $root=null) {
// Basic data
$fields = SavedSearch::getSearchableFields($this->getQueue()->getRoot());
$fields = SavedSearch::getSearchableFields($root ?: $this->getQueue()->getRoot());
if ($primary = $fields[$this->primary]) {
list(,$field) = $primary;
$query = $this->addToQuery($query, $field,
......@@ -763,7 +790,7 @@ extends VerySimpleModel {
}
function getDataConfigForm($source=false) {
return new QueueColDataConfigForm($source ?: $this->ht,
return new QueueColDataConfigForm($source ?: $this->__getDbFields(),
array('id' => $this->id));
}
......@@ -805,7 +832,13 @@ extends VerySimpleModel {
return $inst;
}
function update($vars) {
static function __create($vars) {
$c = static::create($vars);
$c->save();
return $c;
}
function update($vars, $root='Ticket') {
$form = $this->getDataConfigForm($vars);
foreach ($form->getClean() as $k=>$v)
$this->set($k, $v);
......@@ -833,7 +866,7 @@ extends VerySimpleModel {
continue;
// Determine the criteria
$name = $vars['condition_field'][$i];
$fields = SavedSearch::getSearchableFields($this->getQueue()->getRoot());
$fields = SavedSearch::getSearchableFields($root);
if (!isset($fields[$name]))
// No such field exists for this queue root type
continue;
......@@ -884,6 +917,48 @@ extends VerySimpleModel {
}
}
class QueueColumnGlue
extends VerySimpleModel {
static $meta = array(
'table' => QUEUE_COLUMN_TABLE,
'pk' => array('queue_id', 'column_id'),
'joins' => array(
'column' => array(
'constraint' => array('column_id' => 'QueueColumn.id'),
),
'queue' => array(
'constraint' => array('queue_id' => 'CustomQueue.id'),
),
),
'select_related' => array('column', 'queue'),
'ordering' => array('sort'),
);
}
class QueueColumnListBroker
extends InstrumentedList {
function __construct($fkey, $queryset=false) {
parent::__construct($fkey, $queryset);
$this->queryset->select_related('column');
}
function getOrBuild($modelClass, $fields, $cache=true) {
$m = parent::getOrBuild($modelClass, $fields, $cache);
if ($m && $modelClass === 'QueueColumnGlue') {
// Instead, yield the QueueColumn instance with the local fields
// inthe association table as annotations
$m = AnnotatedModel::wrap($m->column, $m, 'QueueColumn');
}
return $m;
}
function add($column, $glue=null) {
$glue = $glue ?: QueueColumnGlue::create();
$glue->column = $column;
parent::add(AnnotatedModel::wrap($column, $glue));
}
}
abstract class QueueColumnFilter {
static $registry;
......@@ -987,41 +1062,33 @@ QueueColumnFilter::register('TicketLinkWithPreviewFilter');
class QueueColDataConfigForm
extends AbstractForm {
function buildFields() {
return array(
'primary' => new DataSourceField(array(
'label' => __('Primary Data Source'),
'required' => true,
'configuration' => array(
'root' => 'Ticket',
),
'layout' => new GridFluidCell(6),
)),
'secondary' => new DataSourceField(array(
'label' => __('Secondary Data Source'),
'configuration' => array(
'root' => 'Ticket',
),
'layout' => new GridFluidCell(6),
)),
'heading' => new TextboxField(array(
'label' => __('Heading'),
function buildFields() {
return array(
'primary' => new DataSourceField(array(
'label' => __('Primary Data Source'),
'required' => true,
'layout' => new GridFluidCell(3),
'configuration' => array(
'root' => 'Ticket',
),
'layout' => new GridFluidCell(6),
)),
'secondary' => new DataSourceField(array(
'label' => __('Secondary Data Source'),
'configuration' => array(
'root' => 'Ticket',
),
'layout' => new GridFluidCell(6),
)),
'name' => new TextboxField(array(
'label' => __('Name'),
'required' => true,
'layout' => new GridFluidCell(4),
)),
'filter' => new ChoiceField(array(
'label' => __('Filter'),
'required' => false,
'choices' => QueueColumnFilter::getFilters(),
'layout' => new GridFluidCell(3),
)),
'width' => new TextboxField(array(
'label' => __('Width'),
'default' => 75,
'configuration' => array(
'validator' => 'number',
),
'layout' => new GridFluidCell(3),
'layout' => new GridFluidCell(4),
)),
'truncate' => new ChoiceField(array(
'label' => __('Text Overflow'),
......@@ -1031,7 +1098,7 @@ function buildFields() {
'clip' => __("Clip Text"),
),
'default' => 'wrap',
'layout' => new GridFluidCell(3),
'layout' => new GridFluidCell(4),
)),
);
}
......
......@@ -26,37 +26,6 @@
# 'T': Tickets
# 'A': Tasks
#
# Columns are not necessary and a default list is used if no columns are
# specified.
#
# columns: Array of column instances with these fields
# flags: (unused)
# sort: Manual sort order of the queue
# heading: Display name of the column header
# primary: Data source for the field
# secondary: Backup data source / default text
# width: Width weight of the column
# filter: What the field should link to
# 'link:ticket': Ticket
# 'link:user': User
# 'link:org': Organization
# 'link:ticketP': Ticket with hover preview
# truncate:
# 'wrap': Fold words on multiple lines
# annotations:
# c: Annotation class name
# p: Placement
# 'a': After column text
# 'b': Before column text
# '<': Float to start (left)
# '>': Float to end (right)
# conditions:
# crit: Criteria for the condiditon, in the form of [field, method, value]
# prop: Array of CSS properties to apply to the field
# 'font-weight':
# 'font-style':
# ...
# extra: (future use and for plugins)
---
- id: 1
title: Open
......@@ -64,6 +33,31 @@
sort: 1
root: T
config: '[["status__state","includes",{"open":"Open"}]]'
columns:
- column_id: 1
sort: 1
width: 75
heading: Ticket
- column_id: 10
sort: 2
width: 150
heading: Last Updated
- column_id: 3
sort: 3
width: 300
heading: Subject
- column_id: 4
sort: 4
width: 185
heading: From
- column_id: 5
sort: 5
width: 85
heading: Priority
- column_id: 8
sort: 6
width: 160
heading: Assigned To
- id: 2
title: Closed
......@@ -71,6 +65,31 @@
sort: 2
root: T
config: '[["status__state","includes",{"closed":"Closed"}]]'
columns:
- column_id: 1
sort: 1
width: 100
heading: Ticket
- column_id: 7
sort: 2
width: 150
heading: Date Closed
- column_id: 3
sort: 3
width: 300
heading: Subject
- column_id: 4
sort: 4
width: 185
heading: From
- column_id: 5
sort: 5
width: 85
heading: Priority
- column_id: 8
sort: 6
width: 160
heading: Closed By
- title: Unanswered
parent_id: 1
......@@ -78,13 +97,63 @@
root: T
sort: 1
config: '[["status__state","includes",{"open":"Open"}],["answered","nset",null]]'
columns:
- column_id: 1
sort: 1
width: 100
heading: Ticket
- column_id: 10
sort: 2
width: 150
heading: Last Update
- column_id: 3
sort: 3
width: 300
heading: Subject
- column_id: 4
sort: 4
width: 185
heading: From
- column_id: 5
sort: 5
width: 85
heading: Priority
- column_id: 8
sort: 6
width: 160
heading: Assigned To
- title: Unassigned
parent_id: 1
flags: 0x0b
root: T
sort: 2
config: '[["assignee","unassigned",null]]
config: '[["assignee","unassigned",null]]'
columns:
- column_id: 1
sort: 1
width: 100
heading: Ticket
- column_id: 10
sort: 2
width: 150
heading: Last Update
- column_id: 3
sort: 3
width: 300
heading: Subject
- column_id: 4
sort: 4
width: 185
heading: From
- column_id: 5
sort: 5
width: 85
heading: Priority
- column_id: 11
sort: 6
width: 160
heading: Department
- title: My Tickets
parent_id: 1
......@@ -92,3 +161,28 @@
root: T
sort: 4
config: '[["assignee","includes",{"M":"Me", "T":"One of my teams"}]]'
columns:
- column_id: 1
sort: 1
width: 100
heading: Ticket
- column_id: 10
sort: 2
width: 150
heading: Last Update
- column_id: 3
sort: 3
width: 300
heading: Subject
- column_id: 4
sort: 4
width: 185
heading: From
- column_id: 5
sort: 5
width: 85
heading: Priority
- column_id: 11
sort: 6
width: 160
heading: Department
......@@ -115,123 +115,68 @@ else {
</div>
<div class="hidden tab_content" id="columns">
<h2><?php echo __("Manage columns in this queue"); ?></h2>
<p><?php echo __("Add, remove, and customize the content of the columns in this queue using the options below. Click a column header to manage or resize it"); ?></p>
<div>
<i class="icon-plus-sign"></i>
<select id="add-column" data-next-id="0" onchange="javascript:
var $this = $(this),
selected = $this.find(':selected'),
nextId = $this.data('nextId'),
columns = $('#resizable-columns');
$.ajax({
url: 'ajax.php/queue/addColumn',
data: { field: selected.val(), id: nextId },
dataType: 'json',
success: function(json) {
var div = $('<div></div>')
.addClass('column-header ui-resizable')
.text(json.heading)
.attr({'data-id': nextId})
.data({colId: 'colconfig-'+nextId, width: json.width})
.append($('<i>')
.addClass('icon-ellipsis-vertical ui-resizable-handle ui-resizable-handle-e')
)
.append($('<input />')
.attr({type:'hidden', name:'columns[]'})
.val(nextId)
);
config = $('<div></div>')
.addClass('hidden column-configuration')
.attr('id', 'colconfig-' + nextId);
config.append($(json.config)).insertAfter(columns.append(div));
$this.data('nextId', nextId+1);
}
});
">
<option value=""><?php echo __('Add a column'); ?></option>
<?php foreach (SavedSearch::getSearchableFields('Ticket') as $path=>$f) {
list($label,) = $f; ?>
<option value="<?php echo $path; ?>"><?php echo $label; ?></option>
<table class="table two-column">
<tbody>
<tr class="header">
<th colspan="2">
<?php echo __("Manage columns in this queue"); ?>
<div><small><?php echo __(
"Add, remove, and customize the content of the columns in this queue using the options below. Click a column header to manage or resize it"); ?>
</small></div>
</th>
</tr>
<tr class="header">
<td><small><b><?php echo __('Column Name'); ?></b></small></td>
<td><small><b><?php echo __('Heading and Width'); ?></b></small></td>
</tr>
</tbody>
<tbody class="sortable-rows">
<tr id="column-template" class="hidden">
<td>
<i class="faded-more icon-sort"></i>
<input type="hidden" data-name="queue_id"
value="<?php echo $queue->getId(); ?>"/>
<input type="hidden" data-name="column_id" />
<span></span>
</td>
<td>
<input type="text" size="25" data-name="heading"
data-translate-tag="" />
<input type="text" size="5" data-name="width" />
<a class="action-button"
href="#" onclick="javascript:
var colid = $(this).closest('tr').find('[data-name=column_id]').val();
$.dialog('ajax.php/tickets/search/column/edit/' + colid, 201);
return false;
"><i class="icon-edit"></i> <?php echo __('Edit'); ?></a>
<a href="#" class="pull-right drop-column" title="<?php echo __('Delete');
?>"><i class="icon-trash"></i></a>
</td>
</tr>
</tbody>
<tbody>
<tr class="header">
<td colspan="2"></td>
</tr>
<tr>
<td colspan="2" id="append-column">
<i class="icon-plus-sign"></i>
<select id="add-column" data-quick-add="queue-column">
<option value=""><?php echo __('Add a column'); ?></option>
<?php foreach (QueueColumn::objects() as $C) { ?>
<option value="<?php echo $C->id; ?>"><?php echo
Format::htmlchars($C->name); ?></option>
<?php } ?>
</select>
<div id="resizable-columns">
<?php foreach ($queue->getColumns() as $column) {
$colid = $column->getId();
$maxcolid = max(@$maxcolid ?: 0, $colid);
echo sprintf('<div data-id="%1$s" data-col-id="colconfig-%1$s" class="column-header" '
.'data-width="%2$s">%3$s'
.'<i class="icon-ellipsis-vertical ui-resizable-handle ui-resizable-handle-e"></i>'
.'<input type="hidden" name="columns[]" value="%1$s"/>'
.'</div>',
$colid, $column->getWidth(), $column->getHeading(),
$column->sort ?: 1);
} ?>
</div>
<script>
$(function() {
$('#add-column').data('nextId', <?php echo $maxcolid+1; ?>);
var qq = setInterval(function() {
var total = 0,
container = $('#resizable-columns'),
width = container.width(),
w2px = 1.25,
columns = $('.column-header', container);
// Await computation of the <div>'s width
if (width)
clearInterval(qq);
columns.each(function() {
total += $(this).data('width') || 100;
});
container.data('w2px', w2px);
columns.each(function() {
// FIXME: jQuery will compensate for padding (40px)
$(this).width(w2px * ($(this).data('width') || 100) - 42);
});
}, 20);
});
</script>
<?php foreach ($queue->getColumns() as $column) {
$colid = $column->getId();
echo sprintf('<div class="hidden column-configuration" id="colconfig-%s">',
$colid);
include STAFFINC_DIR . 'templates/queue-column.tmpl.php';
echo '</div>';
} ?>
</div>
<option value="0" data-quick-add>&mdash; <?php echo __('Add New');?> &mdash;</option>
</select>
<button type="button" class="green button">
<?php echo __('Add'); ?>
</button>
</td>
</tr>
</tbody>
</table>
<script>
var aa = setInterval(function() {
var cols = $('#resizable-columns');
if (cols.length && cols.sortable)
clearInterval(aa);
cols.sortable({
containment: 'parent'
});
$('.column-header', cols).resizable({
handles: {'e' : '.ui-resizable-handle'},
grid: [ 20, 0 ],
maxHeight: 16,
minHeight: 16,
stop: function(event, ui) {
var w2px = ui.element.parent().data('w2px'),
width = ui.element.width() - 42;
ui.element.data('width', width / w2px);
// TODO: Update WIDTH text box in the data form
}
});
cols.click('.column-header', function(e) {
var $this = $(event.target);
$this.parent().children().removeClass('active');
$this.addClass('active');
$('.column-configuration', $this.closest('.tab_content')).hide();
$('#'+$this.data('colId')).fadeIn('fast');
});
}, 20);
</script>
</div>
<div class="hidden tab_content" id="preview-tab">
......@@ -263,3 +208,58 @@ else {
</p>
</form>
<script>
var addColumn = function(colid, info) {
if (!colid) return;
var copy = $('#column-template').clone(),
name_prefix = 'columns[' + colid + ']';
info['column_id'] = colid;
copy.find('input[data-name]').each(function() {
var $this = $(this),
name = $this.data('name');
if (info[name] !== undefined)
$this.val(info[name]);
$this.attr('name', name_prefix + '[' + name + ']');
});
copy.find('span').text(info['name']);
copy.attr('id', '').show().insertBefore($('#column-template'));
copy.removeClass('hidden');
if (info['trans'] !== undefined) {
var input = copy.find('input[data-translate-tag]')
.attr('data-translate-tag', info['trans']);
if ($.fn.translatable)
input.translatable();
// Else it will be made translatable when the JS library is loaded
}
copy.find('a.drop-column').click(function() {
$('<option>')
.attr('value', copy.find('input[data-name=column_id]').val())
.text(info.name)
.insertBefore($('#add-column')
.find('[data-quick-add]')
);
copy.fadeOut(function() { $(this).remove(); });
return false;
});
var selected = $('#add-column').find('option[value=' + colid + ']');
selected.remove();
};
$('#append-column').find('button').on('click', function() {
var selected = $('#add-column').find(':selected'),
id = parseInt(selected.val());
if (!id)
return;
addColumn(id, {name: selected.text(), heading: selected.text(), width: 100});
return false;
});
<?php foreach ($queue->columns as $C) {
echo sprintf('addColumn(%d, {name: %s, heading: %s, width: %d, trans: %s});',
$C->column_id, JsonDataEncoder::encode($C->name),
JsonDataEncoder::encode($C->heading), $C->width,
JsonDataEncoder::encode($C->getTranslateTag('heading')));
} ?>
</script>
<?php
/**
* Calling conventions
*
* $column - <QueueColumn> instance for this column
*/
$colid = 0;
?>
<h3 class="drag-handle"><?php echo __('Add Queue Column'); ?></h3>
<a class="close" href=""><i class="icon-remove-circle"></i></a>
<hr/>
<form method="post" action="#admin/quick-add/queue-column">
<?php
include 'queue-column.tmpl.php';
?>
<hr>
<p class="full-width">
<span class="buttons pull-left">
<input type="reset" value="<?php echo __('Reset'); ?>">
<input type="button" value="<?php echo __('Cancel'); ?>" class="close">
</span>
<span class="buttons pull-right">
<input type="submit" value="<?php echo __('Save'); ?>">
</span>
</p>
</form>
<?php
/**
* Calling conventions
*
* $column - <QueueColumn> instance for this column
*/
$colid = $column->getId();
?>
<h3 class="drag-handle"><?php echo __('Manage Queue Column'); ?> &mdash;
<?php echo $column->get('name') ?></h3>
<a class="close" href=""><i class="icon-remove-circle"></i></a>
<hr/>
<form method="post" action="#tickets/search/column/edit/<?php
echo $colid; ?>">
<?php
include 'queue-column.tmpl.php';
?>
<hr>
<p class="full-width">
<span class="buttons pull-left">
<input type="reset" value="<?php echo __('Reset'); ?>">
<input type="button" value="<?php echo __('Cancel'); ?>" class="close">
</span>
<span class="buttons pull-right">
<input type="submit" value="<?php echo __('Save'); ?>">
</span>
</p>
</form>
......@@ -3,29 +3,26 @@
* Calling conventions
*
* $column - <QueueColumn> instance for this column
* $root - <Class> name of queue root ('Ticket')
* $data_form - <QueueColDataConfigForm> instance, optional
*/
$colid = $column->getId();
$data_form = $column->getDataConfigForm($_POST);
$data_form = $data_form ?: $column->getDataConfigForm($_POST);
?>
<ul class="alt tabs">
<li class="active"><a href="#<?php echo $colid; ?>-data"><?php echo __('Data'); ?></a></li>
<li><a href="#<?php echo $colid; ?>-annotations"><?php echo __('Annotations'); ?></a></li>
<li><a href="#<?php echo $colid; ?>-conditions"><?php echo __('Conditions'); ?></a></li>
<a onclick="javascript:
$(this).closest('.column-configuration').hide();
$('#resizable-columns').find('div[data-id=<?php echo $colid; ?>]').hide()
.find('input[name^=columns]').remove();
" class="button red pull-right"><?php echo __("Delete Column"); ?></a>
<ul class="tabs">
<li class="active"><a href="#data"><?php echo __('Data'); ?></a></li>
<li><a href="#annotations"><?php echo __('Annotations'); ?></a></li>
<li><a href="#conditions"><?php echo __('Conditions'); ?></a></li>
</ul>
<div class="tab_content" id="<?php echo $colid; ?>-data">
<div class="tab_content" id="data">
<?php
print $data_form->asTable();
?>
</div>
<div class="hidden tab_content" data-col-id="<?php echo $colid; ?>"
id="<?php echo $colid; ?>-annotations" style="max-width: 400px">
id="annotations" style="max-width: 400px">
<div class="empty placeholder" style="margin-left: 20px">
<em><?php echo __('No annotations for this field'); ?></em>
</div>
......@@ -69,7 +66,7 @@ $data_form = $column->getDataConfigForm($_POST);
<script>
$(function() {
var addAnnotation = function(type, desc, icon, pos) {
var template = $('.annotation.template', '#<?php echo $colid; ?>-annotations'),
var template = $('.annotation.template', '#annotations'),
clone = template.clone().show().removeClass('template').insertBefore(template),
input = clone.find('[data-field=input]'),
colid = clone.closest('.tab_content').data('colId'),
......@@ -86,14 +83,14 @@ $data_form = $column->getDataConfigForm($_POST);
position.val(pos);
template.closest('.tab_content').find('.empty').hide();
};
$('select.add-annotation', '#<?php echo $colid; ?>-annotations').change(function() {
$('select.add-annotation', '#annotations').change(function() {
var selected = $(this).find(':selected');
addAnnotation(selected.val(), selected.text(), selected.data('icon'));
selected.prop('disabled', true);
});
$('#<?php echo $colid; ?>-annotations').click('a[data-field=delete]',
$('#annotations').click('a[data-field=delete]',
function() {
var tab = $('#<?php echo $colid; ?>-annotations');
var tab = $('#annotations');
if ($('.annotation', tab).length === 0)
tab.find('.empty').show();
});
......@@ -110,13 +107,13 @@ $data_form = $column->getDataConfigForm($_POST);
</div>
</div>
<div class="hidden tab_content" id="<?php echo $colid; ?>-conditions">
<div class="hidden tab_content" id="conditions">
<div style="margin: 0 20px"><?php echo __("Conditions are used to change the view of the data in a row based on some conditions of the data. For instance, a column might be shown bold if some condition is met.");
?></div>
<div class="conditions" style="margin: 20px; max-width: 400px">
<?php
if ($column->getConditions()) {
$fields = SavedSearch::getSearchableFields($column->getQueue()->getRoot());
$fields = SavedSearch::getSearchableFields($root);
foreach ($column->getConditions() as $i=>$condition) {
$id = QueueColumnCondition::getUid();
list($label, $field) = $condition->getField();
......
......@@ -122,7 +122,7 @@ if ($canManageTickets) { ?>
}
foreach ($columns as $C) {
echo sprintf('<th width="%s">%s</th>', $C->getWidth(),
Format::htmlchars($C->getHeading()));
Format::htmlchars($C->getLocalHeading()));
} ?>
</tr>
</thead>
......@@ -145,8 +145,8 @@ foreach ($tickets as $T) {
</tbody>
<tfoot>
<tr>
<td colspan="7">
<?php if ($total && $canManageTickets) {
<td colspan="<?php echo count($columns)+1; ?>">
<?php if ($count && $canManageTickets) {
echo __('Select');?>:&nbsp;
<a id="selectAll" href="#ckb"><?php echo __('All');?></a>&nbsp;&nbsp;
<a id="selectNone" href="#ckb"><?php echo __('None');?></a>&nbsp;&nbsp;
......
......@@ -16,13 +16,10 @@ ALTER TABLE `%TABLE_PREFIX%queue`
DROP TABLE IF EXISTS `%TABLE_PREFIX%queue_column`;
CREATE TABLE `%TABLE_PREFIX%queue_column` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`queue_id` int(10) unsigned NOT NULL,
`flags` int(10) unsigned NOT NULL DEFAULT '0',
`sort` int(10) unsigned NOT NULL DEFAULT '0',
`heading` varchar(64) NOT NULL DEFAULT '',
`name` varchar(64) NOT NULL DEFAULT '',
`primary` varchar(64) NOT NULL DEFAULT '',
`secondary` varchar(64) DEFAULT NULL,
`width` int(10) unsigned DEFAULT NULL,
`filter` varchar(32) DEFAULT NULL,
`truncate` varchar(16) DEFAULT NULL,
`annotations` text,
......@@ -31,3 +28,12 @@ CREATE TABLE `%TABLE_PREFIX%queue_column` (
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `%TABLE_PREFIX%queue_columns`;
CREATE TABLE `%TABLE_PREFIX%queue_columns` (
`queue_id` int(11) unsigned NOT NULL,
`column_id` int(11) unsigned NOT NULL,
`sort` int(10) unsigned NOT NULL DEFAULT '1',
`heading` varchar(64) DEFAULT NULL,
`width` int(10) unsigned NOT NULL DEFAULT '100',
PRIMARY KEY (`queue_id`, `column_id`)
) DEFAULT CHARSET=utf8;
......@@ -174,7 +174,11 @@ $dispatcher = patterns('',
url_post('^/(?P<id>\d+)$', 'saveSearch'),
url_delete('^/(?P<id>\d+)$', 'deleteSearch'),
url_post('^/create$', 'createSearch'),
url_get('^/field/(?P<id>[\w_!:]+)$', 'addField')
url_get('^/field/(?P<id>[\w_!:]+)$', 'addField'),
url('^/column/edit/(?P<id>\d+)$', 'editColumn'),
url_post('^(?P<id>\d+)/delete$', 'deleteQueues'),
url_post('^(?P<id>\d+)/disable$', 'disableQueues'),
url_post('^(?P<id>\d+)/enable$', 'undisableQueues')
))
)),
url('^/tasks/', patterns('ajax.tasks.php:TasksAjaxAPI',
......@@ -242,7 +246,8 @@ $dispatcher = patterns('',
url('^/department$', 'addDepartment'),
url('^/team$', 'addTeam'),
url('^/role$', 'addRole'),
url('^/staff$', 'addStaff')
url('^/staff$', 'addStaff'),
url('^/queue-column$', 'addQueueColumn')
)),
url_get('^/role/(?P<id>\d+)/perms', 'getRolePerms')
)),
......
......@@ -827,7 +827,7 @@ DROP TABLE IF EXISTS `%TABLE_PREFIX%queue`;
CREATE TABLE `%TABLE_PREFIX%queue` (
`id` int(11) unsigned not null auto_increment,
`parent_id` int(11) unsigned not null default 0,
`columns_id` int(11) unsigned,
`columns_id` int(11) unsigned default null,
`flags` int(11) unsigned not null default 0,
`staff_id` int(11) unsigned not null default 0,
`sort` int(11) unsigned not null default 0,
......@@ -844,13 +844,10 @@ CREATE TABLE `%TABLE_PREFIX%queue` (
DROP TABLE IF EXISTS `%TABLE_PREFIX%queue_column`;
CREATE TABLE `%TABLE_PREFIX%queue_column` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`queue_id` int(10) unsigned NOT NULL,
`flags` int(10) unsigned NOT NULL DEFAULT '0',
`sort` int(10) unsigned NOT NULL DEFAULT '0',
`heading` varchar(64) NOT NULL DEFAULT '',
`name` varchar(64) NOT NULL DEFAULT '',
`primary` varchar(64) NOT NULL DEFAULT '',
`secondary` varchar(64) DEFAULT NULL,
`width` int(10) unsigned DEFAULT NULL,
`filter` varchar(32) DEFAULT NULL,
`truncate` varchar(16) DEFAULT NULL,
`annotations` text,
......@@ -859,6 +856,16 @@ CREATE TABLE `%TABLE_PREFIX%queue_column` (
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `%TABLE_PREFIX%queue_columns`;
CREATE TABLE `%TABLE_PREFIX%queue_columns` (
`queue_id` int(11) unsigned NOT NULL,
`column_id` int(11) unsigned NOT NULL,
`sort` int(10) unsigned NOT NULL DEFAULT '1',
`heading` varchar(64) DEFAULT NULL,
`width` int(10) unsigned NOT NULL DEFAULT '100',
PRIMARY KEY (`queue_id`, `column_id`)
) DEFAULT CHARSET=utf8;
DROP TABLE IF EXISTS `%TABLE_PREFIX%translation`;
CREATE TABLE `%TABLE_PREFIX%translation` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment