[ckan-changes] commit/ckan: 2 new changesets
Bitbucket
commits-noreply at bitbucket.org
Fri Oct 14 22:07:01 UTC 2011
2 new changesets in ckan:
http://bitbucket.org/okfn/ckan/changeset/b9913d5a9328/
changeset: b9913d5a9328
branch: feature-1368-ux-fixes
user: zephod
date: 2011-10-14 16:03:15
summary: [ux][s]: Deletions are confirmed with a dialog box.
affected #: 3 files (-1 bytes)
--- a/ckan/logic/schema.py Fri Oct 14 14:34:47 2011 +0100
+++ b/ckan/logic/schema.py Fri Oct 14 15:03:15 2011 +0100
@@ -40,7 +40,7 @@
'revistion_id': [ignore_missing, unicode],
'resource_group_id': [ignore],
'package_id': [ignore],
- 'url': [not_empty, unicode],#, URL(add_http=False)],
+ 'url': [ignore_empty, unicode],#, URL(add_http=False)],
'description': [ignore_missing, unicode],
'format': [ignore_missing, unicode],
'hash': [ignore_missing, unicode],
--- a/ckan/public/scripts/application.js Fri Oct 14 14:34:47 2011 +0100
+++ b/ckan/public/scripts/application.js Fri Oct 14 15:03:15 2011 +0100
@@ -520,15 +520,20 @@
var collection = this.collection;
var deleteResource = function(triggerEvent) {
if (triggerEvent) triggerEvent.preventDefault();
- collection.remove(resource);
+ confirmMessage = CKAN.Strings.deleteThisResourceQuestion;
+ resourceName = resource.attributes.name || CKAN.Strings.noNameBrackets;
+ confirmMessage = confirmMessage.replace('%name%', resourceName);
+ if (confirm(confirmMessage)) {
+ collection.remove(resource);
+ }
};
// == Inner Functions: Update the name as you type == //
var setName = function(newName) {
$link = $tr.find('.js-resource-edit-toggle');
- newName = newName || CKAN.Strings.noNameBrackets;
+ newName = newName || ('<em>'+CKAN.Strings.noNameBrackets+'</em>');
// Need to structurally modify the DOM to force a re-render of text
- $link.html('<span>'+newName+'</span>');
+ $link.html('<ema>'+newName+'</span>');
};
var nameBoxChanged = function(e) {
setName($(e.target).val());
--- a/ckan/templates/js_strings.html Fri Oct 14 14:34:47 2011 +0100
+++ b/ckan/templates/js_strings.html Fri Oct 14 15:03:15 2011 +0100
@@ -21,7 +21,8 @@
CKAN.Strings.addDataset = "${_('Add Dataset')}";
CKAN.Strings.youHaveUnsavedChanges = "${_('You have unsaved changes. Hit Save Changes at the bottom of the page to submit them.')}";
CKAN.Strings.loading = "${_('Loading...')}";
- CKAN.Strings.noNameBrackets = '<em>'+"${_('(no name)')}"+'</em>';
+ CKAN.Strings.noNameBrackets = "${_('(no name)')}";
+ CKAN.Strings.deleteThisResourceQuestion = "${_('Delete the resource \'%name%\'?')}"
/*
* Used in templates.js.
http://bitbucket.org/okfn/ckan/changeset/bd2128be8172/
changeset: bd2128be8172
branch: feature-1368-ux-fixes
user: zephod
date: 2011-10-15 00:06:51
summary: [ux][l]: Developed new URL slug editor. Repaired tests.
affected #: 21 files (-1 bytes)
--- a/ckan/config/routing.py Fri Oct 14 15:03:15 2011 +0100
+++ b/ckan/config/routing.py Fri Oct 14 23:06:51 2011 +0100
@@ -155,7 +155,7 @@
map.connect('/api/2/util/user/autocomplete', controller='api',
action='user_autocomplete')
- map.connect('/api/2/util/dataset/create_slug', controller='api', action='create_slug',
+ map.connect('/api/2/util/dataset/is_slug_valid', controller='api', action='is_slug_valid',
conditions=dict(method=['GET']))
map.connect('/api/2/util/tag/autocomplete', controller='api', action='tag_autocomplete',
conditions=dict(method=['GET']))
--- a/ckan/controllers/api.py Fri Oct 14 15:03:15 2011 +0100
+++ b/ckan/controllers/api.py Fri Oct 14 23:06:51 2011 +0100
@@ -9,7 +9,6 @@
import ckan.rating
from ckan.lib.search import query_for, QueryOptions, SearchIndexError, SearchError, DEFAULT_OPTIONS, convert_legacy_parameters_to_solr
from ckan.plugins import PluginImplementations, IGroupController
-from ckan.lib.munge import munge_title_to_name
from ckan.lib.navl.dictization_functions import DataError
from ckan.logic import get_action, check_access
from ckan.logic import NotFound, NotAuthorized, ValidationError
@@ -562,16 +561,10 @@
out = map(convert_to_dict, query.all())
return out
- def create_slug(self):
+ def is_slug_valid(self):
+ slug = request.params.get('slug') or ''
- title = request.params.get('title') or ''
- name = munge_title_to_name(title)
- if package_exists(name):
- valid = False
- else:
- valid = True
- #response.content_type = 'application/javascript'
- response_data = dict(name=name.replace('_', '-'), valid=valid)
+ response_data = dict(valid=not bool(package_exists(slug)))
return self._finish_ok(response_data)
def tag_autocomplete(self):
--- a/ckan/controllers/package_formalchemy.py Fri Oct 14 15:03:15 2011 +0100
+++ b/ckan/controllers/package_formalchemy.py Fri Oct 14 23:06:51 2011 +0100
@@ -20,8 +20,6 @@
def new(self):
c.error = ''
- api_url = config.get('ckan.api_url', '/').rstrip('/')
- c.package_create_slug_api_url = api_url+h.url_for(controller='api', action='create_slug')
is_admin = self.authorizer.is_sysadmin(c.user)
# Check access control for user to create a package.
try:
--- a/ckan/lib/munge.py Fri Oct 14 15:03:15 2011 +0100
+++ b/ckan/lib/munge.py Fri Oct 14 23:06:51 2011 +0100
@@ -7,33 +7,6 @@
from ckan import model
-def munge_title_to_name(name):
- '''Munge a title into a name.
- '''
- # remove foreign accents
- if isinstance(name, unicode):
- name = substitute_ascii_equivalents(name)
- # convert spaces and separators
- name = re.sub('[ .:/]', '-', name)
- # take out not-allowed characters
- name = re.sub('[^a-zA-Z0-9-_]', '', name).lower()
- # remove doubles
- name = re.sub('--', '-', name)
- # remove leading or trailing hyphens
- name = name.strip('-')
- # if longer than max_length, keep last word if a year
- max_length = model.PACKAGE_NAME_MAX_LENGTH - 5
- # (make length less than max, in case we need a few for '_' chars
- # to de-clash names.)
- if len(name) > max_length:
- year_match = re.match('.*?[_-]((?:\d{2,4}[-/])?\d{2,4})$', name)
- if year_match:
- year = year_match.groups()[0]
- name = '%s-%s' % (name[:(max_length-len(year)-1)], year)
- else:
- name = name[:max_length]
- return name
-
def munge_name(name):
'''Munges the name field in case it is not to spec.
'''
--- a/ckan/logic/validators.py Fri Oct 14 15:03:15 2011 +0100
+++ b/ckan/logic/validators.py Fri Oct 14 23:06:51 2011 +0100
@@ -111,7 +111,7 @@
query = query.filter(model.Package.id <> package_id)
result = query.first()
if result:
- errors[key].append(_('Dataset name already exists in database'))
+ errors[key].append(_('That URL is already in use.'))
def duplicate_extras_key(key, data, errors, context):
--- a/ckan/public/css/style.css Fri Oct 14 15:03:15 2011 +0100
+++ b/ckan/public/css/style.css Fri Oct 14 23:06:51 2011 +0100
@@ -885,18 +885,23 @@
display: none;
}
-body.package.new dt.name-label {
- font-size: 10px;
-}
-
body.package.new .instructions {
font-size: 10px;
}
-body.package.new input#name {
- font-size: 8px;
+body.package.new .url-suffix {
+ font-weight: bold;
+}
- background: #eee;
+body.package.new a.url-edit {
+ font-weight: normal;
+ margin-left: 10px;
+}
+body.package.new .url-input {
+ width: 250px;
+}
+body.package.new dd.name-field {
+ padding-top: 0.2em;
}
body.package.read #sidebar ul.tags,
@@ -929,12 +934,8 @@
/* = Controller-specific tweaks = */
/* ============================== */
-body.group.index #minornavigation {
- visibility: hidden;
-}
-
body.package.search #minornavigation {
- visibility: hidden;
+ display: none;
}
body.package.search #menusearch {
display: none;
@@ -943,7 +944,6 @@
body.index.home #minornavigation {
display: none;
}
-
body.index.home #sidebar {
display: none;
}
Binary file ckan/public/images/icons/pencil.png has changed
--- a/ckan/public/scripts/application.js Fri Oct 14 15:03:15 2011 +0100
+++ b/ckan/public/scripts/application.js Fri Oct 14 23:06:51 2011 +0100
@@ -26,7 +26,10 @@
var isDatasetNew = $('body.package.new').length > 0;
if (isDatasetNew) {
+ // Set up magic URL slug editor
+ CKAN.Utils.setupUrlEditor();
$('#save').val(CKAN.Strings.addDataset);
+ $("#title").focus();
}
// Buttons with href-action should navigate when clicked
@@ -71,6 +74,104 @@
};
+ my.bindInputChanges = function(input, callback) {
+ input.keyup(callback);
+ input.keydown(callback);
+ input.keypress(callback);
+ input.change(callback);
+ };
+
+ my.setupUrlEditor = function() {
+ // Page elements to hook onto
+ var titleInput = $('.js-title');
+ var urlText = $('.js-url-text');
+ var urlSuffix = $('.js-url-suffix');
+ var urlInput = $('.js-url-input');
+ var validMsg = $('.js-url-is-valid');
+
+ // Title api verifies package name availability
+ var api_url = '/api/2/util/dataset/is_slug_valid';
+ // (make length less than max, in case we need a few for '_' chars to de-clash slugs.)
+ var MAX_SLUG_LENGTH = 90;
+
+ var titleChanged = function() {
+ var lastTitle = "";
+ var regexToHyphen = [ new RegExp('[ .:/_]', 'g'),
+ new RegExp('[^a-zA-Z0-9-_]', 'g'),
+ new RegExp('-+', 'g')];
+ var regexToDelete = [ new RegExp('^-*', 'g'),
+ new RegExp('-*$', 'g')];
+
+ var titleToSlug = function(title) {
+ var slug = title;
+ $.each(regexToHyphen, function(idx,regex) { slug = slug.replace(regex, '-'); });
+ $.each(regexToDelete, function(idx,regex) { slug = slug.replace(regex, ''); });
+ slug = slug.toLowerCase();
+
+ if (slug.length<MAX_SLUG_LENGTH) {
+ slug=slug.substring(0,MAX_SLUG_LENGTH);
+ }
+ return slug;
+ };
+
+ // Called when the title changes
+ return function() {
+ var title = titleInput.val();
+ if (title == lastTitle) return;
+ lastTitle = title;
+
+ slug = titleToSlug(title);
+ urlInput.val(slug);
+ urlInput.change();
+ };
+ }();
+
+ var urlChanged = function() {
+ var timer = null;
+
+ var checkSlugValid = function(slug) {
+ $.ajax({
+ url: api_url,
+ data: 'slug=' + slug,
+ dataType: 'jsonp',
+ type: 'get',
+ jsonpCallback: 'callback',
+ success: function (data) {
+ if (data.valid) {
+ validMsg.html('<span style="font-weight: bold; color: #0c0">'+CKAN.Strings.datasetNameAvailable+'</span>');
+ } else {
+ validMsg.html('<span style="font-weight: bold; color: #c00">'+CKAN.Strings.datasetNameNotAvailable+'</span>');
+ }
+ }
+ });
+ }
+
+ return function() {
+ slug = urlInput.val();
+ urlSuffix.html('<span>'+slug+'</span>');
+ validMsg.html('<span style="color: #777;">'+CKAN.Strings.checking+'</span>');
+ if (timer) clearTimeout(timer);
+ timer = setTimeout(function () {
+ checkSlugValid(slug);
+ }, 200);
+ };
+ }();
+
+ // Hook title changes to the input box
+ my.bindInputChanges(titleInput, titleChanged);
+ my.bindInputChanges(urlInput, urlChanged);
+ // Set up the form
+ urlChanged();
+
+ $('.js-url-editlink').live('click',function(e) {
+ e.preventDefault();
+ $('.js-url-viewmode').hide();
+ $('.js-url-editmode').show();
+ urlInput.select();
+ urlInput.focus();
+ });
+ }
+
// Attach dataset autocompletion to provided elements
//
// Requires: jquery-ui autocomplete
@@ -286,116 +387,6 @@
});
};
- // Name slug generator for $name element using $title element
- //
- // Also does nice things like show errors if name not available etc
- //
- // Usage: CKAN.Utils.PackageSlugCreator.create($('#my-title'), $('#my-name'))
- my.PackageSlugCreator = (function() {
- // initialize function
- //
- // args: $title and $name input elements
- function SlugCreator($title, $name) {
- this.name_field = $name;
- this.title_field = $title;
- // Keep a variable where we can store whether the name field has been
- // directly modified by the user or not. If it has, we should no longer
- // fetch updates.
- this.name_changed = false;
- // url for slug api (we need api rather than do it ourself because we check if available)
- this.url = '/api/2/util/dataset/create_slug';
- // Add a new element where the validity of the dataset name can be displayed
- this.name_field.parent().append('<div id="dataset_name_valid_msg"></div>');
- this.title_field.blur(this.title_change_handler())
- this.title_field.keyup(this.title_change_handler())
- this.name_field.keyup(this.name_change_handler());
- this.name_field.blur(this.name_blur_handler());
- }
-
- SlugCreator.create = function($title, $name) {
- return new SlugCreator($title, $name);
- }
-
- SlugCreator.prototype.title_change_handler = function() {
- var self = this;
- return function() {
- if (!self.name_changed && self.title_field.val().replace(/^\s+|\s+$/g, '')) {
- self.update(self.title_field.val(), function(data) {self.name_field.val(data.name)});
- }
- }
- }
-
- SlugCreator.prototype.name_blur_handler = function() {
- var self = this;
- return function() {
- // Reset if the name is emptied
- if (!self.name_field.val().replace(/^\s+|\s+$/g, '')){
- self.name_changed = false;
- $('#dataset_name_valid_msg').html('');
- } else {
- self.update(self.name_field.val(), function(data) {
- self.name_field.val(data.name)
- });
- }
- };
- }
-
- SlugCreator.prototype.name_change_handler = function() {
- var self = this;
- return function() {
- // Reset if the name is emptied
- if (!self.name_field.val().replace(/^\s+|\s+$/g, '')){
- self.name_changed = false;
- $('#dataset_name_valid_msg').html('');
- } else {
- self.name_changed = true;
- self.update(self.name_field.val(), function(data) {
- if (self.name_field.val().length >= data.name) {
- self.name_field.val(data.name);
- }
- });
- }
- };
- }
-
- // Create a function for fetching the value and updating the result
- SlugCreator.prototype.perform_update = function(value, on_success){
- var self = this;
- $.ajax({
- url: self.url,
- data: 'title=' + value,
- dataType: 'jsonp',
- type: 'get',
- jsonpCallback: 'callback',
- success: function (data) {
- if (on_success) {
- on_success(data);
- }
- var valid_msg = $('#dataset_name_valid_msg');
- if (data.valid) {
- valid_msg.html('<span style="font-weight: bold; color: #0c0">'+CKAN.Strings.datasetNameAvailable+'</span>');
- } else {
- valid_msg.html('<span style="font-weight: bold; color: #c00">'+CKAN.Strings.datasetNameNotAvailable+'</span>');
- }
- }
- });
- }
-
- // We only want to perform the update if there hasn't been a change for say 200ms
- var timer = null;
- SlugCreator.prototype.update = function(value, on_success) {
- var self = this;
- if (this.timer) {
- clearTimeout(this.timer)
- };
- this.timer = setTimeout(function () {
- self.perform_update(value, on_success)
- }, 200);
- }
-
- return SlugCreator;
- })();
-
return my;
}(jQuery, CKAN.Utils || {});
@@ -545,10 +536,7 @@
}
var nameBox = $tr.find('input.js-resource-edit-name');
- nameBox.change(nameBoxChanged);
- nameBox.keydown(nameBoxChanged);
- nameBox.keyup(nameBoxChanged);
- nameBox.keypress(nameBoxChanged);
+ CKAN.Utils.bindInputChanges(nameBox,nameBoxChanged);
$tr.find('.js-resource-edit-toggle').click(toggleOpen);
$tr.find('.js-resource-edit-delete').click(deleteResource);
--- a/ckan/templates/group/index.html Fri Oct 14 15:03:15 2011 +0100
+++ b/ckan/templates/group/index.html Fri Oct 14 23:06:51 2011 +0100
@@ -7,14 +7,9 @@
<py:def function="page_heading">Groups of Datasets</py:def><div py:match="content">
-
${c.page.pager()}
${group_list_from_dict(c.page.items)}
${c.page.pager()}
-
- <py:choose test="">
-
- </py:choose></div><xi:include href="layout.html" />
--- a/ckan/templates/group/layout.html Fri Oct 14 15:03:15 2011 +0100
+++ b/ckan/templates/group/layout.html Fri Oct 14 23:06:51 2011 +0100
@@ -8,34 +8,34 @@
<py:match path="primarysidebar"><li class="widget-container boxed widget_text">
- <h3>Groups section</h3>
- <p i18n:msg="">Whilst tags are great at collecting datasets together, there are occasions when you want to restrict users from editing a collection. A <strong>group</strong> can be set-up to specify which users have permission to add or remove datasets from it.</p>
- <p>
- <span class="ckan_logged_in" style="display: none;" i18n:msg="">
- To create a new group, please first <a href="${h.url_for(controller='user',action='login', id=None)}">login</a>.
- </span>
- <span class="ckan_logged_out">
- <a href="${h.url_for(controller='group',action='new', id=None)}">Create a new group</a>
- </span>
- </p>
+ <h3>What Are Groups?</h3>
+ <span i18n:msg="">Whilst tags are great at collecting datasets together, there are occasions when you want to restrict users from editing a collection. A <strong>group</strong> can be set-up to specify which users have permission to add or remove datasets from it.</span></li></py:match>
- <py:match path="minornavigation" py:if="c.group">
- <ul class="tabbed">
- <li py:attrs="{'class':'current-tab'} if c.action=='read' else {}">${h.subnav_link(c, h.icon('group') + _('View'), controller='group', action='read', id=c.group.name)}</li>
- <li py:attrs="{'class':'current-tab'} if c.action=='edit' else {}" py:if="h.check_access('group_update',{'id':c.group.id})">
- ${h.subnav_link(c, h.icon('group_edit') + _('Edit'), controller='group', action='edit', id=c.group.name)}
- </li>
- <li py:attrs="{'class':'current-tab'} if c.action=='history' else {}">${h.subnav_link(c, h.icon('page_white_stack') + _('History'), controller='group', action='history', id=c.group.name)}</li>
- <li py:attrs="{'class':'current-tab'} if c.action=='authz' else {}" py:if="h.check_access('group_edit_permissions',{'id':c.group.id})">
- ${h.subnav_link(c, h.icon('lock') + _('Authorization'), controller='group', action='authz', id=c.group.name)}
- </li>
- <!-- li class="action">
- ${h.subnav_link(c, h.icon('atom_feed') + _('Subscribe'),
- controller='group', action='history', id=c.group.name, format='atom', days=7)}
- </li-->
- </ul>
+ <py:match path="minornavigation">
+ <ul py:if="c.group" class="tabbed">
+ <li py:attrs="{'class':'current-tab'} if c.action=='read' else {}">${h.subnav_link(c, h.icon('group') + _('View'), controller='group', action='read', id=c.group.name)}</li>
+ <li py:attrs="{'class':'current-tab'} if c.action=='edit' else {}" py:if="h.check_access('group_update',{'id':c.group.id})">
+ ${h.subnav_link(c, h.icon('group_edit') + _('Edit'), controller='group', action='edit', id=c.group.name)}
+ </li>
+ <li py:attrs="{'class':'current-tab'} if c.action=='history' else {}">${h.subnav_link(c, h.icon('page_white_stack') + _('History'), controller='group', action='history', id=c.group.name)}</li>
+ <li py:attrs="{'class':'current-tab'} if c.action=='authz' else {}" py:if="h.check_access('group_edit_permissions',{'id':c.group.id})">
+ ${h.subnav_link(c, h.icon('lock') + _('Authorization'), controller='group', action='authz', id=c.group.name)}
+ </li>
+ <!-- li class="action">
+ ${h.subnav_link(c, h.icon('atom_feed') + _('Subscribe'),
+ controller='group', action='history', id=c.group.name, format='atom', days=7)}
+ </li-->
+ </ul>
+ <ul py:if="not c.group" class="tabbed">
+ <li py:attrs="{'class':'current-tab'} if c.action=='index' else {}">
+ ${h.subnav_link(c, h.icon('group') + _('List Groups'), controller='group', action='index')}
+ </li>
+ <li py:attrs="{'class':'current-tab'} if c.action=='new' else {}">
+ ${h.subnav_link(c, h.icon('group_add') + _('Add a Group'), controller='group', action='new')}
+ </li>
+ </ul></py:match><xi:include href="../layout.html" />
--- a/ckan/templates/group/new.html Fri Oct 14 15:03:15 2011 +0100
+++ b/ckan/templates/group/new.html Fri Oct 14 23:06:51 2011 +0100
@@ -2,8 +2,8 @@
xmlns:xi="http://www.w3.org/2001/XInclude"
py:strip="">
- <py:def function="page_title">New - Groups</py:def>
- <py:def function="page_heading">New Group</py:def>
+ <py:def function="page_title">Add A Group</py:def>
+ <py:def function="page_heading">Add A Group</py:def><div py:match="content">
${Markup(c.form)}
--- a/ckan/templates/group/new_group_form.html Fri Oct 14 15:03:15 2011 +0100
+++ b/ckan/templates/group/new_group_form.html Fri Oct 14 23:06:51 2011 +0100
@@ -13,7 +13,6 @@
</div><fieldset>
- <legend>Details</legend><dl><dt><label class="field_opt" for="name">Name *</label></dt><dd><input id="name" name="name" type="text" value="${data.get('name', '')}"/></dd>
@@ -38,7 +37,7 @@
</fieldset><fieldset>
- <legend>Extras</legend>
+ <h3>Extras</h3><dl><py:with vars="extras = data.get('extras', [])"><py:for each="num, extra in enumerate(data.get('extras', []))">
@@ -63,7 +62,7 @@
</fieldset><fieldset>
- <legend>Datasets</legend>
+ <h3>Datasets</h3><dl py:if="data.get('packages')"><py:for each="num, package in enumerate(data.get('packages'))"><dt><input checked="checked" id="datasets__${num}__name" name="packages__${num}__name" type="checkbox" value="${package['name']}"/></dt>
@@ -76,9 +75,7 @@
</fieldset><fieldset>
- <legend>
- Add datasets
- </legend>
+ <h3>Add datasets</h3><dl><dt><label class="field_opt" for="packages__${len(data.get('packages', []))}__name">Dataset</label></dt><dd><input class="autocomplete-dataset" id="datasets__${len(data.get('packages', []))}__name" name="packages__${len(data.get('packages', []))}__name" type="text" /></dd>
--- a/ckan/templates/js_strings.html Fri Oct 14 15:03:15 2011 +0100
+++ b/ckan/templates/js_strings.html Fri Oct 14 23:06:51 2011 +0100
@@ -14,6 +14,7 @@
* Used in application.js.
*/
CKAN.Strings.helloWorld = "${_('Hello there, world!')}";
+ CKAN.Strings.checking = "${_('Checking...')}";
CKAN.Strings.datasetNameAvailable = "${_('This dataset name is available!')}";
CKAN.Strings.datasetNameNotAvailable = "${_('This dataset name is already used, please use a different name')}";
CKAN.Strings.bracketsNone = "${_('(none)')}";
--- a/ckan/templates/package/edit.html Fri Oct 14 15:03:15 2011 +0100
+++ b/ckan/templates/package/edit.html Fri Oct 14 23:06:51 2011 +0100
@@ -19,9 +19,9 @@
<ul class="edit-form-navigation"><!-- One button for each fieldset --><li><a href="#section-basic-information">Basic Information</a></li>
+ <li><a href="#section-further-information">Futher Information</a></li><li><a href="#section-resources">Resources</a></li>
- <li><a href="#section-groups">Groups</a></li>
- <li><a href="#section-detail">Details</a></li>
+ <li><a href="#section-groups">Groups & Tags</a></li><li><a href="#section-extras">Extras</a></li></ul></li>
--- a/ckan/templates/package/new.html Fri Oct 14 15:03:15 2011 +0100
+++ b/ckan/templates/package/new.html Fri Oct 14 23:06:51 2011 +0100
@@ -8,16 +8,6 @@
<py:def function="body_class">hide-sidebar</py:def>
- <py:def function="optional_footer">
- <!-- Auto-generate 'name' field -->
- <script type="text/javascript">
- jQuery(document).ready(function($) {
- CKAN.Utils.PackageSlugCreator.create($('#title'), $('#name'));
- $("#title").focus();
- });
- </script>
- </py:def>
-
<div py:match="content"><h3 py:if="c.error" class="form-errors">
Error: ${c.error}
--- a/ckan/templates/package/new_package_form.html Fri Oct 14 15:03:15 2011 +0100
+++ b/ckan/templates/package/new_package_form.html Fri Oct 14 23:06:51 2011 +0100
@@ -31,32 +31,47 @@
<dt class="title-label"><label class="field_opt" for="title">Title</label></dt><dd class="title-field"><input id="title"
- tabindex="1" name="title" type="text"
+ class="js-title"
+ name="title" type="text"
value="${data.get('title', '')}"
placeholder="${_('A short descriptive title for the dataset')}"
/></dd><dd class="title-instructions field_error" py:if="errors.get('title', '')">${errors.get('title', '')}</dd>
- <dt class="name-label"><label class="field_req" for="name">Slug *</label></dt>
- <dd class="name-field"><input id="name" tabindex="999" maxlength="100" name="name" type="text" value="${data.get('name', '')}" /></dd>
- <dd class="name-instructions instructions basic">A unique identifier used in urls. Renaming is possible but discouraged.</dd>
- <dd class="name-instructions hints">2+ characters, lowercase, using only 'a-z0-9' and '-_'</dd>
- <dd class="name-instructions field_error" py:if="errors.get('name', '')">${errors.get('name', '')}</dd>
+ <dt class="name-label"><label class="field_req" for="name">Url</label></dt>
+ <dd class="name-field">
+ <span class="url-text">http://thedatahub.org/dataset/<span class="js-url-viewmode js-url-suffix"> </span><a href="#" class="url-edit js-url-editlink js-url-viewmode">(edit)</a></span>
+ <input style="display: none;" id="name" maxlength="100" name="name" type="text" class="url-input js-url-editmode js-url-input" value="${data.get('name', '')}" />
+ <p class="js-url-is-valid"> </p>
+ </dd>
+ <dd style="display: none;" class="js-url-editmode name-instructions instructions basic">A unique identifier used in urls. Renaming is possible but discouraged.</dd>
+ <dd style="display: none;" class="js-url-editmode name-instructions hints">2+ characters, lowercase, using only 'a-z0-9' and '-_'</dd>
+ <dd style="display: none;" class="js-url-editmode name-instructions field_error" py:if="errors.get('name', '')">${errors.get('name', '')}</dd><dt class="homepage-label"><label class="field_opt" for="url">Home Page</label></dt>
- <dd class="homepage-field"><input id="url" tabindex="2" name="url" type="text" value="${data.get('url', '')}"/></dd>
+ <dd class="homepage-field"><input id="url" name="url" type="text" value="${data.get('url', '')}"/></dd><dd class="homepage-instructions instructions basic">The URL for the web page describing the data (not the data itself).</dd><dd class="homepage-instructions hints">e.g. http://www.example.com/growth-figures.html</dd><dd class="homepage-instructions field_error" py:if="errors.get('url', '')">${errors.get('url', '')}</dd>
+ <dt class="license-label"><label class="field_opt" for="license_id">License</label></dt>
+ <dd class="license-field">
+ <select id="license_id" name="license_id">
+ <py:for each="licence_desc, licence_id in c.licences">
+ <option value="${licence_id}" py:attrs="{'selected': 'selected' if data.get('license_id', '') == licence_id else None}" >${licence_desc}</option>
+ </py:for>
+ </select>
+ </dd>
+ <dd class="license-instructions instructions basic">The licence under which the dataset is released.</dd>
+
<dt class="description-label"><label class="field_opt" for="notes">Description</label></dt><dd class="description-field"><div class="markdown-editor"><ul class="button-row"><li><button class="pretty-button js-markdown-edit depressed">Edit</button></li><li><button class="pretty-button js-markdown-preview">Preview</button></li></ul>
- <textarea class="markdown-input" tabindex="3" name="notes" id="notes" placeholder="${_('Start with a summary sentence ...')}">${data.get('notes','')}</textarea>
+ <textarea class="markdown-input" name="notes" id="notes" placeholder="${_('Start with a summary sentence ...')}">${data.get('notes','')}</textarea><div class="markdown-preview" style="display: none;"></div><span class="hints">You can use <a href="http://daringfireball.net/projects/markdown/syntax" target="_blank">Markdown formatting</a> here.</span><!--
@@ -64,25 +79,6 @@
<dd class="instructions further">It is often displayed with the dataset title. In particular, it should start with a short sentence that describes the dataset succinctly, because the first few words alone may be used in some views of the datasets.</dd>
--></div></dd>
-
- <dt class="license-label"><label class="field_opt" for="license_id">License</label></dt>
- <dd class="license-field">
- <select id="license_id" tabindex="4" name="license_id">
- <py:for each="licence_desc, licence_id in c.licences">
- <option value="${licence_id}" py:attrs="{'selected': 'selected' if data.get('license_id', '') == licence_id else None}" >${licence_desc}</option>
- </py:for>
- </select>
- </dd>
- <dd class="license-instructions instructions basic">The licence under which the dataset is released.</dd>
-
- <dt class="tags-label"><label class="field_opt" for="tags">Tags</label></dt>
- <dd class="tags-field">
- <input class="long autocomplete-tag" tabindex="5" id="tag_string" name="tag_string" size="60" type="text"
- value="${data.get('tag_string') or ' '.join([tag['name'] for tag in data.get('tags', [])])}" />
- </dd>
- <dd class="tags-instructions instructions basic" i18n:msg="">Terms that may link this dataset to similar ones. For more information on conventions, see <a href="http://wiki.okfn.org/ckan/doc/faq#TagConventions">this wiki page</a>.</dd>
- <dd class="tags-instructions hints">e.g. pollution rivers water-quality</dd>
- <dd class="tags-instructions field_error" py:if="errors.get('tag_string', '')">${errors.get('tag_string', '')}</dd></dl></fieldset>
@@ -111,6 +107,7 @@
</fieldset><fieldset id="groups">
+ <h3>Groups</h3><dl><py:for each="num, group in enumerate(data.get('groups', []))"><?python
@@ -134,10 +131,21 @@
</py:for></select></dd>
- <dd py:if="not c.groups_available">Cannot add any groups.</dd>
+ <dd py:if="not c.groups_available"><em>Cannot add any groups.</em></dd>
+ </dl>
+ <h3>Tags</h3>
+ <dl>
+ <dt class="tags-label"><label class="field_opt" for="tags">Tags</label></dt>
+ <dd class="tags-field">
+ <input class="long autocomplete-tag" id="tag_string" name="tag_string" size="60" type="text"
+ value="${data.get('tag_string') or ' '.join([tag['name'] for tag in data.get('tags', [])])}" />
+ </dd>
+ <dd class="tags-instructions instructions basic" i18n:msg="">Terms that may link this dataset to similar ones. For more information on conventions, see <a href="http://wiki.okfn.org/ckan/doc/faq#TagConventions">this wiki page</a>.</dd>
+ <dd class="tags-instructions hints">e.g. pollution rivers water-quality</dd>
+ <dd class="tags-instructions field_error" py:if="errors.get('tag_string', '')">${errors.get('tag_string', '')}</dd></dl></fieldset>
-<fieldset id='detail'>
+<fieldset id='further-information'><dl><dt><label class="field_opt" for="author">Author</label></dt><dd><input id="author" name="author" type="text" value="${data.get('author', '')}" /></dd>
@@ -208,9 +216,9 @@
</div><div class="form-submit">
- <input id="save" tabindex="99" class="pretty-button primary" name="save" type="submit" value="${_('Save Changes')}" />
+ <input id="save" class="pretty-button primary" name="save" type="submit" value="${_('Save Changes')}" /><py:if test="c.pkg">
- <input id="cancel" tabindex="100" class="pretty-button href-action" name="cancel" type="reset" value="${_('Cancel')}" action="${h.url_for(controller='package', action='read', id=c.pkg.name)}" />
+ <input id="cancel" class="pretty-button href-action" name="cancel" type="reset" value="${_('Cancel')}" action="${h.url_for(controller='package', action='read', id=c.pkg.name)}" /></py:if><p i18n:msg="" class="hints"><strong>Important:</strong> By submitting content, you agree to release your contributions under the <a href="http://opendatacommons.org/licenses/odbl/1.0/">Open Database License</a>. Please <strong>refrain</strong> from editing this page if you are <strong>not</strong> happy to do this.
--- a/ckan/tests/functional/api/test_ajax.py Fri Oct 14 15:03:15 2011 +0100
+++ b/ckan/tests/functional/api/test_ajax.py Fri Oct 14 23:06:51 2011 +0100
@@ -14,15 +14,26 @@
def teardown(cls):
model.repo.rebuild_db()
- def test_package_create_slug(self):
+ def test_package_slug_valid(self):
+ CreateTestData.create()
response = self.app.get(
- url=url_for(controller='api', action='create_slug'),
+ url=url_for(controller='api', action='is_slug_valid'),
params={
- 'title': u'A New Title * With & Funny CHARacters',
+ 'slug': u'A New Title * With & Funny CHARacters',
},
status=200,
)
- assert_equal(response.body, '{"valid": true, "name": "a-new-title-with-funny-characters"}')
+ assert_equal(response.body, '{"valid": true}')
+ assert_equal(response.header('Content-Type'), 'application/json;charset=utf-8')
+
+ response = self.app.get(
+ url=url_for(controller='api', action='is_slug_valid'),
+ params={
+ 'slug': u'warandpeace',
+ },
+ status=200,
+ )
+ assert_equal(response.body, '{"valid": false}')
assert_equal(response.header('Content-Type'), 'application/json;charset=utf-8')
def test_tag_autocomplete(self):
--- a/ckan/tests/functional/test_group.py Fri Oct 14 15:03:15 2011 +0100
+++ b/ckan/tests/functional/test_group.py Fri Oct 14 23:06:51 2011 +0100
@@ -115,7 +115,7 @@
def test_new_page(self):
offset = url_for(controller='group', action='new')
res = self.app.get(offset, extra_environ={'REMOTE_USER': 'russianfan'})
- assert 'Create a new group' in res, res
+ assert 'Add A Group' in res, res
class TestEdit(FunctionalTestCase):
@@ -247,10 +247,10 @@
group_title = u'Test Title'
group_description = u'A Description'
- # Open 'new group' page
+ # Open 'Add A Group' page
offset = url_for(controller='group', action='new')
res = self.app.get(offset, status=200, extra_environ={'REMOTE_USER': 'russianfan'})
- assert 'New Group' in res, res
+ assert 'Add A Group' in res, res
fv = res.forms['group-edit']
assert fv[prefix+'name'].value == '', fv.fields
assert fv[prefix+'title'].value == ''
@@ -282,7 +282,7 @@
group_name = u'testgrp1'
offset = url_for(controller='group', action='new')
res = self.app.get(offset, status=200, extra_environ={'REMOTE_USER': 'russianfan'})
- assert 'New Group' in res, res
+ assert 'Add A Group' in res, res
fv = res.forms['group-edit']
assert fv[prefix+'name'].value == '', fv.fields
fv[prefix+'name'] = group_name
@@ -296,7 +296,7 @@
group_name = u'testgrp1'
offset = url_for(controller='group', action='new')
res = self.app.get(offset, status=200, extra_environ={'REMOTE_USER': 'russianfan'})
- assert 'New Group' in res, res
+ assert 'Add A Group' in res, res
fv = res.forms['group-edit']
assert fv[prefix+'name'].value == '', fv.fields
fv[prefix+'name'] = group_name
--- a/ckan/tests/functional/test_package.py Fri Oct 14 15:03:15 2011 +0100
+++ b/ckan/tests/functional/test_package.py Fri Oct 14 23:06:51 2011 +0100
@@ -1187,7 +1187,7 @@
fv[prefix+'title'] = pkgtitle
res = fv.submit('save')
assert 'Error' in res, res
- assert 'Dataset name already exists in database' in res, res
+ assert 'That URL is already in use.' in res, res
self._assert_form_errors(res)
def test_missing_fields(self):
--- a/ckan/tests/lib/test_dictization_schema.py Fri Oct 14 15:03:15 2011 +0100
+++ b/ckan/tests/lib/test_dictization_schema.py Fri Oct 14 23:06:51 2011 +0100
@@ -102,9 +102,9 @@
converted_data, errors = validate(data, default_package_schema(), context)
assert errors == {
- 'name': [u'Dataset name already exists in database'],
- 'resources': [{},
- {'url': [u'Missing value']}]
+ 'name': [u'That URL is already in use.'],
+ #'resources': [{}
+ # {'name': [u'That URL is already in use.']}]
}, pformat(errors)
data["id"] = package_id
@@ -112,14 +112,14 @@
converted_data, errors = validate(data, default_package_schema(), context)
assert errors == {
- 'resources': [{}, {'url': [u'Missing value']}]
+ #'resources': [{}, {'url': [u'Missing value']}]
}, pformat(errors)
data['name'] = '????jfaiofjioafjij'
converted_data, errors = validate(data, default_package_schema(), context)
assert errors == {
'name': [u'Name must be purely lowercase alphanumeric (ascii) characters and these symbols: -_'],
- 'resources': [{}, {'url': [u'Missing value']}]
+ #'resources': [{}, {'url': [u'Missing value']}]
},pformat(errors)
def test_2_group_schema(self):
--- a/ckan/tests/lib/test_munge.py Fri Oct 14 15:03:15 2011 +0100
+++ b/ckan/tests/lib/test_munge.py Fri Oct 14 23:06:51 2011 +0100
@@ -3,16 +3,6 @@
from ckan.lib.munge import munge_title_to_name, munge_name
class TestMunge:
- def test_munge_title_to_name(self):
- def test_munge(title, expected_munge):
- munge = munge_title_to_name(title)
- assert_equal(munge, expected_munge)
-
- test_munge('Adult participation in learning', 'adult-participation-in-learning')
- test_munge('Alcohol Profile: Alcohol-specific hospital admission, males', 'alcohol-profile-alcohol-specific-hospital-admission-males')
- test_munge('Age and limiting long-term illness by NS-SeC', 'age-and-limiting-long-term-illness-by-ns-sec')
- test_munge('Higher Education Statistics: HE qualifications obtained in the UK by level, mode of study, domicile, gender, class of first degree and subject area 2001/02', 'higher-education-statistics-he-qualifications-obtained-in-the-uk-by-level-mode-of-study-2001-02')
-
def test_munge_name(self):
def test_munge(title, expected_munge):
munge = munge_name(title)
Repository URL: https://bitbucket.org/okfn/ckan/
--
This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.
More information about the ckan-changes
mailing list