Skip to content

Commit

Permalink
Merge branch '1277-resource-reordering'
Browse files Browse the repository at this point in the history
  • Loading branch information
joetsoi committed Dec 9, 2013
2 parents 4de4839 + fac853a commit f3c6585
Show file tree
Hide file tree
Showing 13 changed files with 2,437 additions and 134 deletions.
44 changes: 44 additions & 0 deletions ckan/logic/action/update.py
Expand Up @@ -344,6 +344,50 @@ def package_update(context, data_dict):

return output

def package_resource_reorder(context, data_dict):
'''Reorder resources against datasets. If only partial resource ids are
supplied then these are assumed to be first and the other resources will
stay in their original order
:param id: the id or name of the package to update
:type id: string
:param order: a list of resource ids in the order needed
:type list: list
'''

id = _get_or_bust(data_dict, "id")
order = _get_or_bust(data_dict, "order")
if not isinstance(order, list):
raise ValidationError({'order': 'Must be a list of resource'})

if len(set(order)) != len(order):
raise ValidationError({'order': 'Must supply unique resource_ids'})

package_dict = _get_action('package_show')(context, {'id': id})
existing_resources = package_dict.get('resources', [])
ordered_resources = []

for resource_id in order:
for i in range(0, len(existing_resources)):
if existing_resources[i]['id'] == resource_id:
resource = existing_resources.pop(i)
ordered_resources.append(resource)
break
else:
raise ValidationError(
{'order':
'resource_id {id} can not be found'.format(id=resource_id)}
)

new_resources = ordered_resources + existing_resources
package_dict['resources'] = new_resources

_check_access('package_resource_reorder', context, package_dict)
_get_action('package_update')(context, package_dict)

return {'id': id, 'order': [resource['id'] for resource in new_resources]}


def _update_package_relationship(relationship, comment, context):
model = context['model']
api = context.get('api_version')
Expand Down
3 changes: 3 additions & 0 deletions ckan/logic/auth/update.py
Expand Up @@ -41,6 +41,9 @@ def package_update(context, data_dict):

return {'success': True}

def package_resource_reorder(context, data_dict):
## the action function runs package update so no need to run it twice
return {'success': True}

def resource_update(context, data_dict):
model = context['model']
Expand Down
41 changes: 41 additions & 0 deletions ckan/new_tests/logic/action/test_update.py
Expand Up @@ -344,3 +344,44 @@ def test_user_update_does_not_return_reset_key(self):

updated_user = helpers.call_action('user_update', **params)
assert 'reset_key' not in updated_user

def test_resource_reorder(self):
resource_urls = ["http://a.html", "http://b.html", "http://c.html"]
dataset = {"name": "basic",
"resources": [{'url': url} for url in resource_urls]
}

dataset = helpers.call_action('package_create', **dataset)
created_resource_urls = [resource['url'] for resource
in dataset['resources']]
assert created_resource_urls == resource_urls
mapping = dict((resource['url'], resource['id']) for resource
in dataset['resources'])

## This should put c.html at the front
reorder = {'id': dataset['id'], 'order':
[mapping["http://c.html"]]}

helpers.call_action('package_resource_reorder', **reorder)

dataset = helpers.call_action('package_show', id=dataset['id'])
reordered_resource_urls = [resource['url'] for resource
in dataset['resources']]

assert reordered_resource_urls == ["http://c.html",
"http://a.html",
"http://b.html"]

reorder = {'id': dataset['id'], 'order': [mapping["http://b.html"],
mapping["http://c.html"],
mapping["http://a.html"]]}

helpers.call_action('package_resource_reorder', **reorder)
dataset = helpers.call_action('package_show', id=dataset['id'])

reordered_resource_urls = [resource['url'] for resource
in dataset['resources']]

assert reordered_resource_urls == ["http://b.html",
"http://c.html",
"http://a.html"]
144 changes: 144 additions & 0 deletions ckan/public/base/javascript/modules/resource-reorder.js
@@ -0,0 +1,144 @@
/* Module for reordering resources
*/
this.ckan.module('resource-reorder', function($, _) {
return {
options: {
id: false,
i18n: {
label: _('Reorder resources'),
save: _('Save order'),
saving: _('Saving...'),
cancel: _('Cancel')
}
},
template: {
title: '<h1></h1>',
button: [
'<a href="javascript:;" class="btn">',
'<i class="icon-reorder"></i>',
'<span></span>',
'</a>'
].join('\n'),
form_actions: [
'<div class="form-actions">',
'<a href="javascript:;" class="cancel btn pull-left"></a>',
'<a href="javascript:;" class="save btn btn-primary"></a>',
'</div>'
].join('\n'),
handle: [
'<a href="javascript:;" class="handle">',
'<i class="icon-move"></i>',
'</a>'
].join('\n'),
saving: [
'<span class="saving muted m-right">',
'<i class="icon-spinner icon-spin"></i>',
'<span></span>',
'</span>'
].join('\n')
},
is_reordering: false,
cache: false,

initialize: function() {
jQuery.proxyAll(this, /_on/);

this.html_title = $(this.template.title)
.text(this.i18n('label'))
.insertBefore(this.el)
.hide();
var button = $(this.template.button)
.on('click', this._onHandleStartReorder)
.appendTo('.page_primary_action');
$('span', button).text(this.i18n('label'));

this.html_form_actions = $(this.template.form_actions)
.hide()
.insertAfter(this.el);
$('.save', this.html_form_actions)
.text(this.i18n('save'))
.on('click', this._onHandleSave);
$('.cancel', this.html_form_actions)
.text(this.i18n('cancel'))
.on('click', this._onHandleCancel);

this.html_handles = $(this.template.handle)
.hide()
.appendTo($('.resource-item', this.el));

this.html_saving = $(this.template.saving)
.hide()
.insertBefore($('.save', this.html_form_actions));
$('span', this.html_saving).text(this.i18n('saving'));

this.cache = this.el.html();

this.el
.sortable()
.sortable('disable');

},

_onHandleStartReorder: function() {
if (!this.is_reordering) {
this.html_form_actions
.add(this.html_handles)
.add(this.html_title)
.show();
this.el
.addClass('reordering')
.sortable('enable');
$('.page_primary_action').hide();
this.is_reordering = true;
}
},

_onHandleCancel: function() {
if (
this.is_reordering
&& !$('.cancel', this.html_form_actions).hasClass('disabled')
) {
this.reset();
this.is_reordering = false;
this.el.html(this.cache)
.sortable()
.sortable('disable');
this.html_handles = $('.handle', this.el);
}
},

_onHandleSave: function() {
if (!$('.save', this.html_form_actions).hasClass('disabled')) {
var module = this;
module.html_saving.show();
$('.save, .cancel', module.html_form_actions).addClass('disabled');
var order = [];
$('.resource-item', module.el).each(function() {
order.push($(this).data('id'));
});
module.sandbox.client.call('POST', 'package_resource_reorder', {
id: module.options.id,
order: order
}, function() {
module.html_saving.hide();
$('.save, .cancel', module.html_form_actions).removeClass('disabled');
module.cache = module.el.html();
module.reset();
module.is_reordering = false;
});
}
},

reset: function() {
this.html_form_actions
.add(this.html_handles)
.add(this.html_title)
.hide();
this.el
.removeClass('reordering')
.sortable('disable');
$('.page_primary_action').show();
}

};
});
1 change: 1 addition & 0 deletions ckan/public/base/javascript/resource.config
Expand Up @@ -32,6 +32,7 @@ ckan =
modules/table-selectable-rows.js
modules/resource-form.js
modules/resource-upload-field.js
modules/resource-reorder.js
modules/follow.js
modules/activity-stream.js
modules/dashboard.js
Expand Down
42 changes: 42 additions & 0 deletions ckan/public/base/less/dataset.less
Expand Up @@ -93,6 +93,48 @@
right: 10px;
}

.resource-list.reordering {
.resource-item {
border: 1px solid @moduleHeadingBorderColor;
margin-bottom: 10px;
cursor: move;
.handle {
display: block;
position: absolute;
color: @moduleHeadingActionTextColor;
left: -31px;
top: 50%;
margin-top: -15px;
width: 30px;
height: 30px;
line-height: 30px;
text-align: center;
border: 1px solid @moduleHeadingBorderColor;
border-width: 1px 0 1px 1px;
background-color: @moduleBackgroundColor;
.border-radius(20px 0 0 20px);
&:hover {
text-decoration: none;
}
}
&:hover .handle {
background-color: @layoutBackgroundColor;
}
&.ui-sortable-helper {
background-color: @layoutBackgroundColor;
border: 1px solid @layoutLinkColor;
.handle {
background-color: @layoutBackgroundColor;
border-color: @layoutLinkColor;
color: @navLinkColor;
}
}
}
}
.resource-item .handle {
display: none;
}

// Dataset Forms

// Tag List
Expand Down

0 comments on commit f3c6585

Please sign in to comment.