[ckan-changes] commit/ckan: 3 new changesets
Bitbucket
commits-noreply at bitbucket.org
Mon Oct 17 11:06:42 UTC 2011
3 new changesets in ckan:
http://bitbucket.org/okfn/ckan/changeset/43b3b729469b/
changeset: 43b3b729469b
branch: release-v1.5
user: dread
date: 2011-10-17 12:33:04
summary: [transplant] from branch feature-1368-ux-fixes from b2169 to 8090d.
affected #: 14 files (-1 bytes)
--- a/ckan/public/css/forms.css Mon Oct 17 10:00:49 2011 +0100
+++ b/ckan/public/css/forms.css Mon Oct 17 11:33:04 2011 +0100
@@ -117,37 +117,6 @@
color: #555;
font-size: 90%;
}
-
-form table thead th.form-label {
- width: 23%;
- display: none;
-}
-form table thead th.form-value {
- width: 27%;
- display: none;
-}
-form table tbody tr td.form-label,
-form table tbody tr td.form-value {
- font-size: 0.92em;
- line-height: 1.5em;
- padding-top: 0;
- padding-bottom: 4px;
- white-space: nowrap;
- background: inherit;
-}
-form table tbody tr td.form-label {
- text-align: right;
- font-weight: bold;
- padding-right: 4px;
- vertical-align: middle;
-}
-form table tbody tr td.form-value {
- padding-left: 4px;
- border-left: 1px dashed #aaa;
-}
-form table td.form-value input {
- width: 100%;
-}
label.has-errors, label.fieldWithErrors {
font-weight: bold;
--- a/ckan/public/css/style.css Mon Oct 17 10:00:49 2011 +0100
+++ b/ckan/public/css/style.css Mon Oct 17 11:33:04 2011 +0100
@@ -791,6 +791,10 @@
padding: 0 5px 5px 10px;
width: 32em;
}
+div.markdown-editor .button-row {
+ padding-right: 40px;
+ text-align: center;
+}
div.markdown-preview {
background: white;
@@ -807,31 +811,6 @@
}
-table.resource-table tbody tr td.resource-expand-link,
-table.resource-table tbody tr td.resource-is-changed {
- vertical-align: top;
-}
-
-td.resource-expanded {
- padding: 8px 0 8px 0;
-}
-td.resource-expand-link {
- padding-top: 6px;
- text-align: center;
-}
-th.resource-expand-link,
-td.resource-expand-link,
-th.resource-is-changed,
-td.resource-is-changed {
- text-align: center;
- padding: 8px 0 0 0;
- width: 20px;
-}
-
-div.inner table {
- margin-bottom: 0;
-}
-
/* ====================== */
/* = Add Resources Page = */
/* ====================== */
@@ -840,7 +819,7 @@
width: 60%;
}
-button.delete-resource {
+button.done-editing {
float: right;
}
@@ -859,6 +838,9 @@
div.resource-add-subpane {
margin-top: 10px;
}
+div.resource-add .fileinfo {
+ margin: 7px 0;
+}
/* ==================== */
@@ -917,6 +899,12 @@
background: #eee;
}
+body.package.read #sidebar ul.tags,
+body.package.read #sidebar ul.groups {
+ margin-bottom: 10px;
+}
+
+
.success .new-dataset {
font-size: 150%;
}
@@ -924,45 +912,17 @@
font-weight: bold;
}
-/* =============================== */
-/* = Mini-Tabs [Markdown Editor] = */
-/* =============================== */
-
-ul.tabs {
+/* ============= */
+/* = Mini-Tabs = */
+/* ============= */
+ul.button-row {
margin-bottom: 5px;
}
-
-ul.tabs li {
+ul.button-row li {
display: inline;
+ margin-right: 10px;
}
-ul.tabs li a {
- display: inline-block;
- padding: 2px 8px;
- margin-right: 10px;
- font-size: 10px;
- font-weight: bold;
- text-decoration: none;
- color: #666;
- border: 1px solid #DDD;
- border-color: #DDD;
- border-right-color: #BBB;
- border-bottom-color: #BBB;
- -webkit-border-radius: 10px;
- -moz-border-radius: 10px;
- border-top-left-radius: 10px 10px;
- border-top-right-radius: 10px 10px;
- border-bottom-right-radius: 10px 10px;
- border-bottom-left-radius: 10px 10px;
-}
-
-ul.tabs li a.selected {
- color: #333;
- background: white;
- border-color: #BBB;
- border-right-color: #DDD;
- border-bottom-color: #DDD;
-}
/* ============================== */
@@ -1149,6 +1109,7 @@
-moz-transition: 0.1s linear all;
transition: 0.1s linear all;
}
+.pretty-button.depressed,
.pretty-button:hover {
background-position: 0 -15px;
color: #333;
@@ -1216,6 +1177,7 @@
opacity: 0.65;
cursor: default;
}
+.pretty-button.depressed,
.pretty-button:active {
-webkit-box-shadow: inset 0 3px 7px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.05);
-moz-box-shadow: inset 0 3px 7px rgba(0, 0, 0, 0.1), 0 1px 2px rgba(0, 0, 0, 0.05);
@@ -1240,3 +1202,98 @@
color: #000;
}
+/* ============================= */
+/* = Inline resource edit form = */
+/* ============================= */
+
+
+
+/*
+table.resource-table tbody tr td.resource-delete-link {
+ vertical-align: top;
+}
+
+td.resource-expanded {
+ padding: 8px 0 8px 0;
+}
+.resource-expand-link,
+.resource-delete-link {
+ padding-top: 6px;
+ text-align: center;
+ width: 20px;
+}
+div.inner {
+ padding-right: 10px;
+}
+div.inner table {
+ margin-bottom: 0;
+}
+*/
+.resource-table tr {
+ overflow: hidden;
+}
+th.resource-edit-delete,
+td.resource-edit-delete {
+ /* Override screen.css */
+ padding: 0;
+ width: 40px;
+}
+td.resource-edit-delete img {
+ padding: 8px;
+}
+a.resource-edit-expand {
+ background-image: url('/images/icons/arrow-closed.gif');
+ padding-left: 13px;
+ background-position: left center;
+ background-repeat: no-repeat;
+}
+td.resource-edit {
+ padding: 10px;
+ vertical-align: top;
+}
+.resource-edit-expanded {
+ overflow: hidden;
+ margin: 0;
+}
+.resource-edit-expanded table {
+ /* Override screen.css */
+ margin: 10px 0;
+}
+table.resource-table td.resource-edit tbody td {
+ /* Override alternating background */
+ background: transparent;
+}
+td.resource-edit-delete {
+ vertical-align: top;
+ text-align: center;
+}
+td.resource-edit input {
+ /* Override forms.css */
+ width: 100%;
+}
+th.resource-edit-label {
+ width: 23%;
+ display: none;
+}
+th.resource-edit-value {
+ width: 27%;
+ display: none;
+}
+td.resource-edit-label,
+td.resource-edit-value {
+ font-size: 0.92em;
+ line-height: 1.5em;
+ padding-top: 0;
+ padding-bottom: 4px;
+ background: inherit;
+}
+td.resource-edit-label {
+ text-align: right;
+ font-weight: bold;
+ padding-right: 4px;
+ vertical-align: middle;
+}
+td.resource-edit-value {
+ padding-left: 4px;
+ border-left: 1px dashed #aaa;
+}
--- a/ckan/public/scripts/application.js Mon Oct 17 10:00:49 2011 +0100
+++ b/ckan/public/scripts/application.js Mon Oct 17 11:33:04 2011 +0100
@@ -7,7 +7,7 @@
$('input.autocomplete-format').live('keyup', function(){
CKAN.Utils.setupFormatAutocomplete($(this));
});
- CKAN.Utils.setupMarkdownEditor($('.markdown-editor .tabs a, .markdown-editor .markdown-preview'));
+ CKAN.Utils.setupMarkdownEditor($('.markdown-editor'));
// set up ckan js
var config = {
endpoint: '/'
@@ -40,7 +40,7 @@
// Selectively enable the upload button
var storageEnabled = $.inArray('storage',CKAN.plugins)>=0;
if (storageEnabled) {
- $('div.resource-add li.upload-file').show();
+ $('li.js-upload-file').show();
}
// Set up hashtag nagivigation
@@ -48,7 +48,7 @@
var _dataset = new CKAN.Model.Dataset(preload_dataset);
var $el=$('form#dataset-edit');
- var view=new CKAN.View.DatasetEdit({
+ var view=new CKAN.View.DatasetEditForm({
model: _dataset,
el: $el
});
@@ -60,6 +60,17 @@
var CKAN = CKAN || {};
CKAN.Utils = function($, my) {
+
+ my.flashMessage = function(msg, category) {
+ if (!category) {
+ category = 'info';
+ }
+ var messageDiv = $('<div />').html(msg).addClass(category).hide();
+ $('.flash-messages').append(messageDiv);
+ messageDiv.show(1200);
+
+ };
+
// Attach dataset autocompletion to provided elements
//
// Requires: jquery-ui autocomplete
@@ -215,20 +226,20 @@
});
};
- my.setupMarkdownEditor = function(elements) {
+ my.setupMarkdownEditor = function(markdownEditor) {
// Markdown editor hooks
- elements.live('click', function(e) {
+ markdownEditor.find('button, div.markdown-preview').live('click', function(e) {
e.preventDefault();
- var $el = $(e.target);
- var action = $el.attr('action') || 'write';
+ var $target = $(e.target);
+ console.log('clicked');
// Extract neighbouring elements
- var div=$el.closest('.markdown-editor')
- div.find('.tabs a').removeClass('selected');
- div.find('.tabs a[action='+action+']').addClass('selected');
- var textarea = div.find('.markdown-input');
- var preview = div.find('.markdown-preview');
+ var markdownEditor=$target.closest('.markdown-editor')
+ markdownEditor.find('button').removeClass('depressed');
+ var textarea = markdownEditor.find('.markdown-input');
+ var preview = markdownEditor.find('.markdown-preview');
// Toggle the preview
- if (action=='preview') {
+ if ($target.is('.js-markdown-preview')) {
+ $target.addClass('depressed');
raw_markdown=textarea.val();
preview.html("<em>"+CKAN.Strings.loading+"<em>");
$.post("/api/util/markdown", { q: raw_markdown },
@@ -239,6 +250,7 @@
textarea.hide();
preview.show();
} else {
+ markdownEditor.find('.js-markdown-edit').addClass('depressed');
textarea.show();
preview.hide();
textarea.focus();
@@ -384,76 +396,69 @@
return SlugCreator;
})();
-
return my;
}(jQuery, CKAN.Utils || {});
-CKAN.View.DatasetEdit = Backbone.View.extend({
+CKAN.View.DatasetEditForm = Backbone.View.extend({
initialize: function() {
- _.bindAll(this, 'render');
+ var resources = this.model.get('resources');
+ var $form = this.el;
- var boundToUnload = false;
- this.el.change(function() {
- if (!boundToUnload) {
- boundToUnload = true;
- window.onbeforeunload = function () {
- return CKAN.Strings.youHaveUnsavedChanges;
- };
+ var changesMade = function() {
+ var boundToUnload = false;
+ return function() {
+ if (!boundToUnload) {
+ CKAN.Utils.flashMessage(CKAN.Strings.youHaveUnsavedChanges,'notice');
+ boundToUnload = true;
+ window.onbeforeunload = function () {
+ return CKAN.Strings.youHaveUnsavedChanges;
+ };
+ }
+ }
+ }();
+
+ $form.find('input').live('change', function(e) {
+ $target = $(e.target);
+ // Entering text in the 'add' box does not represent a change
+ if ($target.closest('.resource-add').length==0) {
+ changesMade();
}
});
- this.el.submit(function() {
+ resources.bind('add', changesMade);
+ resources.bind('remove', changesMade);
+
+ $form.submit(function() {
// Don't stop us leaving
window.onbeforeunload = null;
});
- // Tabbed view for adding resources
- var $el=this.el.find('.resource-add');
- this.addView=new CKAN.View.ResourceAdd({
- collection: this.model.get('resources'),
+ // Table for editing resources
+ var $el = this.el.find('.resource-table.edit');
+ this.resourceList=new CKAN.View.ResourceEditList({
+ collection: resources,
el: $el
});
- // Table for editing resources
- var $el=this.el.find('.resource-table.edit');
- this.resourceList=new CKAN.View.ResourceEditList({
- collection: this.model.get('resources'),
+ // Tabbed view for adding resources
+ var $el = this.el.find('.resource-add');
+ this.addView=new CKAN.View.ResourceAddTabs({
+ collection: resources,
el: $el
});
- this.render();
- },
-
-
- render: function() {
this.addView.render();
this.resourceList.render();
},
-
- events: {
- }
-
});
CKAN.View.ResourceEditList = Backbone.View.extend({
initialize: function() {
- _.bindAll(this, 'render', 'addRow');
- this.collection.bind('add', this.addRow);
- },
-
- render: function() {
- var self = this;
-
- // Have to trash entire content; some stuff was there on page load
- this.el.find('tbody').empty();
- this.collection.each(this.addRow);
-
- if (this.collection.isEmpty()) {
- $tr = $('<tr />').addClass('table-empty');
- $tr.html('<td></td><td colspan="4">'+CKAN.Strings.bracketsNone+'</td>');
- this.el.find('tbody').append($tr);
- }
+ _.bindAll(this, 'addResource', 'removeResource');
+ this.collection.bind('add', this.addResource);
+ this.collection.bind('remove', this.removeResource);
+ this.collection.each(this.addResource);
},
nextIndex: function() {
@@ -468,127 +473,93 @@
return maxId+1;
},
- addRow: function(resource) {
- // Strip placeholder row
- this.el.find('tr.table-empty').remove();
+ addResource: function(resource) {
+ var position = this.nextIndex();
+ // Create a row from the template
+ var $tr = $('<tr />');
+ $tr.html($.tmpl(
+ CKAN.Templates.resourceEntry,
+ { resource: resource.toTemplateJSON(),
+ num: position
+ }
+ ));
+ $tr.find('.js-resource-edit-expanded').hide();
+ this.el.find('tbody.resource-table').append($tr);
+ resource.view_tr = $tr;
- // TODO tidy up so the view creates its own elements
- var $tr = $('<tr />');
+ // == Inner Function: Toggle the expanded options set == //
+ var toggleOpen = function(triggerEvent) {
+ if (triggerEvent) triggerEvent.preventDefault();
+ var animTime = 350;
+ var expandedTable = $tr.find('.js-resource-edit-expanded');
+ var finalHeight = expandedTable.height();
+ var icon = 'closed';
- // Captured by an inner function
- var self = this;
+ if (expandedTable.is(':visible')) {
+ expandedTable.animate(
+ {height:0},
+ animTime,
+ function() {
+ expandedTable.height(finalHeight);
+ expandedTable.hide();
+ }
+ );
+ }
+ else {
+ expandedTable.show();
+ expandedTable.height(0);
+ // Transition to its true height
+ expandedTable.animate({height:finalHeight}, animTime);
+ $tr.find('.js-resource-edit-name').focus();
+ icon = 'open';
+ }
+ $tr.find('.js-resource-edit-toggle').css("background-image", "url('/images/icons/arrow-"+icon+".gif')");
+ };
- this.el.find('tbody.resource-table').append($tr);
- var _view = new CKAN.View.ResourceEdit({
- model: resource,
- el: $tr,
- position: this.nextIndex(),
- deleteResource: function() {
- // Passing down a capture to remove the resource
- $tr.remove();
-
- self.collection.remove(resource);
- if (self.collection.isEmpty()) {
- self.render();
- }
- }
- });
- _view.render();
+ // == Inner Function: Delete the row == //
+ var collection = this.collection;
+ var deleteResource = function(triggerEvent) {
+ if (triggerEvent) triggerEvent.preventDefault();
+ 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;
+ $link.html(newName);
+ };
+ var nameBoxChanged = function(e) {
+ setName($(e.target).val());
+ }
+
+ // Trigger animation
+ if (resource.isNew()) {
+ toggleOpen();
+ }
+
+ var nameBox = $tr.find('input.js-resource-edit-name');
+ nameBox.change(nameBoxChanged);
+ nameBox.keydown(nameBoxChanged);
+ nameBox.keyup(nameBoxChanged);
+ nameBox.keypress(nameBoxChanged);
+
+ $tr.find('.js-resource-edit-toggle').click(toggleOpen);
+ $tr.find('.js-resource-edit-delete').click(deleteResource);
+ // Initialise name
+ setName(resource.attributes.name);
},
- events: {
- }
+ removeResource: function(resource) {
+ if (resource.view_tr) {
+ resource.view_tr.remove();
+ delete resource.view_tr;
+ }
+ },
});
-CKAN.View.ResourceEdit = Backbone.View.extend({
- initialize: function() {
- _.bindAll(this, 'render', 'toggleExpanded');
- var self = this;
- this.model.bind('change', function() { self.hasChanged=true; });
- this.model.bind('change', this.render());
- this.position = this.options.position;
- this.expanded = this.model.isNew();
- this.hasChanged = this.model.isNew();
- this.animate = this.model.isNew();
- },
-
- render: function() {
- var tmplData = {
- resource: this.model.toTemplateJSON(),
- num: this.position
- };
- var $newRow = $.tmpl(CKAN.Templates.resourceEntry, tmplData);
- this.el.html($newRow);
-
- if (this.expanded) {
- this.el.find('a.resource-expand-link').hide();
- this.el.find('.resource-summary').hide();
- if (this.animate) {
- this.el.find('.resource-expanded .inner').hide();
- this.el.find('.resource-expanded .inner').show('slow');
- }
- }
- else {
- this.el.find('a.resource-collapse-link').hide();
- this.el.find('.resource-expanded').hide();
- }
-
- if (!this.hasChanged) {
- this.el.find('img.resource-is-changed').hide();
- }
- this.animate = false;
- },
-
- events: {
- 'click a.resource-expand-link': 'toggleExpanded',
- 'click a.resource-collapse-link': 'toggleExpanded',
- 'click .delete-resource': 'clickDelete'
- },
-
- clickDelete: function(e) {
- e.preventDefault();
- this.options.deleteResource();
- },
-
- saveData: function() {
- this.model.set(this.getData(), {
- error: function(model, error) {
- var msg = CKAN.Strings.failedToSave;
- msg += JSON.stringify(error);
- alert(msg);
- }
- });
- return false;
- },
-
- getData: function() {
- var _data = $(this.el).find('input').serializeArray();
- modelData = {};
- $.each(_data, function(idx, value) {
- modelData[value.name.split('__')[2]] = value.value
- });
- return modelData;
- },
-
- toggleExpanded: function(e) {
- e.preventDefault();
-
- this.expanded = !this.expanded;
- this.animate = true;
- // Closing the form; update the model fields
- if (!this.expanded) {
- this.saveData();
- // Model might not have changed
- this.render();
- } else {
- this.render();
- }
- }
-
-});
-
-CKAN.View.ResourceAdd = Backbone.View.extend({
+CKAN.View.ResourceAddTabs = Backbone.View.extend({
initialize: function() {
_.bindAll(this, 'render', 'addNewResource', 'reset');
},
@@ -597,12 +568,12 @@
},
events: {
- 'click .action-resource-tab': 'clickAdd',
+ 'click button': 'clickButton',
'click input[name=reset]': 'reset'
},
reset: function() {
- this.el.find('.tabs a').removeClass('selected');
+ this.el.find('button').removeClass('depressed');
if (this.subView != null) {
this.subView.remove();
this.subView = null;
@@ -610,40 +581,43 @@
return false;
},
- clickAdd: function(e) {
+ clickButton: function(e) {
e.preventDefault();
+ var $target = $(e.target);
+
+ if ($target.is('.depressed')) {
+ this.reset();
+ }
+ else {
+ this.reset();
+ $target.addClass('depressed');
- this.reset();
+ var $subPane = $('<div />').addClass('resource-add-subpane');
+ this.el.append($subPane);
- var action = $(e.target).attr('action');
- this.el.find('.tabs a').removeClass('selected');
- this.el.find('.tabs a[action='+action+']').addClass('selected');
+ var tempResource = new CKAN.Model.Resource({});
- var $subPane = $('<div />').addClass('resource-add-subpane');
- this.el.append($subPane);
-
- var tempResource = new CKAN.Model.Resource({});
-
- tempResource.bind('change', this.addNewResource);
- // Open sub-pane
- if (action=='upload-file') {
- this.subView = new CKAN.View.ResourceUpload({
- el: $subPane,
- model: tempResource,
- // TODO: horrible reverse depedency ...
- client: CKAN.UI.workspace.client
- });
+ tempResource.bind('change', this.addNewResource);
+ // Open sub-pane
+ if ($target.is('.js-upload-file')) {
+ this.subView = new CKAN.View.ResourceUpload({
+ el: $subPane,
+ model: tempResource,
+ // TODO: horrible reverse depedency ...
+ client: CKAN.UI.workspace.client
+ });
+ }
+ else if ($target.is('.js-link-file') || $target.is('.js-link-api')) {
+ this.subView = new CKAN.View.ResourceAddLink({
+ el: $subPane,
+ model: tempResource,
+ mode: ($target.is('.js-link-file'))? 'file' : 'api',
+ // TODO: horrible reverse depedency ...
+ client: CKAN.UI.workspace.client
+ });
+ }
+ this.subView.render();
}
- else if (action=='link-file' || action=='link-api') {
- this.subView = new CKAN.View.ResourceAddLink({
- el: $subPane,
- model: tempResource,
- mode: (action=='link-file')? 'file' : 'api',
- // TODO: horrible reverse depedency ...
- client: CKAN.UI.workspace.client
- });
- }
- this.subView.render();
},
addNewResource: function(tempResource) {
--- a/ckan/public/scripts/templates.js Mon Oct 17 10:00:49 2011 +0100
+++ b/ckan/public/scripts/templates.js Mon Oct 17 11:33:04 2011 +0100
@@ -1,6 +1,6 @@
CKAN.Templates.resourceAddLinkFile = ' \
- <form class="resource-add" action=""> \
+ <form class="resource-add"> \
<dl> \
<dt> \
<label class="field_opt" for="url"> \
@@ -18,7 +18,7 @@
';
CKAN.Templates.resourceAddLinkApi = ' \
- <form class="resource-add" action=""> \
+ <form class="resource-add"> \
<dl> \
<dt> \
<label class="field_opt" for="url"> \
@@ -50,7 +50,8 @@
</dt> \
<dd> \
<input type="file" name="file" /> \
- <span class="fileinfo"></span> \
+ <br /> \
+ <div class="fileinfo"></div> \
<input id="upload" name="upload" type="submit" class="pretty-button primary" value="'+CKAN.Strings.add+'" /> \
<input id="reset" name="reset" type="reset" class="pretty-button" value="'+CKAN.Strings.cancel+'" /> \
</dd> \
@@ -64,44 +65,26 @@
CKAN.Templates.resourceEntry = ' \
- <td class="resource-expand-link"> \
- <a class="resource-expand-link" href="#"><img src="/images/icons/edit-expand.png" /></a> \
- <a class="resource-collapse-link" href="#"><img src="/images/icons/edit-collapse.png" /></a> \
- </td> \
- <td class="resource-summary resource-url"> \
- ${resource.url} \
- </td> \
- <td class="resource-summary resource-name"> \
- ${resource.name} \
- </td> \
- <td class="resource-summary resource-format"> \
- ${resource.format} \
- </td> \
- <td class="resource-expanded" colspan="3"> \
- <div class="inner"> \
+ <td class="resource-edit"> \
+ <a class="resource-edit-expand js-resource-edit-toggle" href="#">${resource.name}</a>\
+ <div class="resource-edit-expanded js-resource-edit-expanded"> \
<table> \
- <thead> \
- <th class="form-label"></th> \
- <th class="form-value"></th> \
- <th class="form-label"></th> \
- <th class="form-value"></th> \
- </thead> \
<tbody> \
<tr> \
- <td class="form-label">'+CKAN.Strings.name+'</td> \
- <td class="form-value" colspan="3"> \
- <input name="resources__${num}__name" type="text" value="${resource.name}" class="long" /> \
+ <td class="resource-edit-label">'+CKAN.Strings.name+'</td> \
+ <td class="resource-edit-value" colspan="3"> \
+ <input class="js-resource-edit-name" name="resources__${num}__name" type="text" value="${resource.name}" class="long" /> \
</td> \
</tr> \
<tr> \
- <td class="form-label">'+CKAN.Strings.description+'</td> \
- <td class="form-value" colspan="3"> \
+ <td class="resource-edit-label">'+CKAN.Strings.description+'</td> \
+ <td class="resource-edit-value" colspan="3"> \
<input name="resources__${num}__description" type="text" value="${resource.description}" class="long" /> \
</td> \
</tr> \
<tr> \
- <td class="form-label">'+CKAN.Strings.url+'</td> \
- <td class="form-value" colspan="3"> \
+ <td class="resource-edit-label">'+CKAN.Strings.url+'</td> \
+ <td class="resource-edit-value" colspan="3"> \
{{if resource.resource_type=="file.upload"}} \
${resource.url} \
<input name="resources__${num}__url" type="hidden" value="${resource.url}" /> \
@@ -112,59 +95,60 @@
</td> \
</tr> \
<tr> \
- <td class="form-label">'+CKAN.Strings.format+'</td> \
- <td class="form-value"> \
+ <td class="resource-edit-label">'+CKAN.Strings.format+'</td> \
+ <td class="resource-edit-value"> \
<input name="resources__${num}__format" type="text" value="${resource.format}" class="long autocomplete-format" /> \
</td> \
- <td class="form-label">'+CKAN.Strings.resourceType+'</td> \
- <td class="form-value"> \
- {{if resource.resource_type=="file.upload"}} \
- ${resource.resource_type} \
- <input name="resources__${num}__resource_type" type="hidden" value="${resource.resource_type}" /> \
- {{/if}} \
- {{if resource.resource_type!="file.upload"}} \
- <input name="resources__${num}__resource_type" type="text" value="${resource.resource_type}" /> \
- {{/if}} \
+ <td class="resource-edit-label">'+CKAN.Strings.resourceType+'</td> \
+ <td class="resource-edit-value"> \
+ {{if resource.resource_type=="file.upload"}} \
+ ${resource.resource_type} \
+ <input name="resources__${num}__resource_type" type="hidden" value="${resource.resource_type}" /> \
+ {{/if}} \
+ {{if resource.resource_type!="file.upload"}} \
+ <input name="resources__${num}__resource_type" type="text" value="${resource.resource_type}" /> \
+ {{/if}} \
</td> \
</tr> \
<tr> \
- <td class="form-label">'+CKAN.Strings.sizeBracketsBytes+'</td> \
- <td class="form-value"> \
+ <td class="resource-edit-label">'+CKAN.Strings.sizeBracketsBytes+'</td> \
+ <td class="resource-edit-value"> \
<input name="resources__${num}__size" type="text" value="${resource.size}" class="long" /> \
</td> \
- <td class="form-label">'+CKAN.Strings.mimetype+'</td> \
- <td class="form-value"> \
+ <td class="resource-edit-label">'+CKAN.Strings.mimetype+'</td> \
+ <td class="resource-edit-value"> \
<input name="resources__${num}__mimetype" type="text" value="${resource.mimetype}" /> \
</td> \
</tr> \
<tr> \
- <td class="form-label">'+CKAN.Strings.lastModified+'</td> \
- <td class="form-value"> \
+ <td class="resource-edit-label">'+CKAN.Strings.lastModified+'</td> \
+ <td class="resource-edit-value"> \
<input name="resources__${num}__last_modified" type="text" value="${resource.last_modified}" /> \
</td> \
- <td class="form-label">'+CKAN.Strings.mimetypeInner+'</td> \
- <td class="form-value"> \
+ <td class="resource-edit-label">'+CKAN.Strings.mimetypeInner+'</td> \
+ <td class="resource-edit-value"> \
<input name="resources__${num}__mimetype_inner" type="text" value="${resource.mimetype_inner}" /> \
</td> \
</tr> \
<tr> \
- <td class="form-label">'+CKAN.Strings.hash+'</td> \
- <td class="form-value"> \
+ <td class="resource-edit-label">'+CKAN.Strings.hash+'</td> \
+ <td class="resource-edit-value" colspan="3"> \
${resource.hash || "Unknown"} \
<input name="resources__${num}__hash" type="hidden" value="${resource.hash}" /> \
</td> \
- <td class="form-label">'+CKAN.Strings.id+'</td> \
- <td class="form-value"> \
+ </tr> \
+ <tr> \
+ <td class="resource-edit-label">'+CKAN.Strings.id+'</td> \
+ <td class="resource-edit-value" colspan="3"> \
${resource.id} \
<input name="resources__${num}__id" type="hidden" value="${resource.id}" /> \
</td> \
</tr> \
</tbody> \
</table> \
- <button class="delete-resource pretty-button danger">'+CKAN.Strings.deleteResource+'</button> \
</div> \
</td> \
- <td class="resource-is-changed"> \
- <img src="/images/icons/add.png" title="'+CKAN.Strings.resourceHasUnsavedChanges+'" class="resource-is-changed" /> \
+ <td class="resource-edit-delete"> \
+ <a class="resource-edit-delete js-resource-edit-delete" href="#"><img src="/images/icons/delete.png" /></a> \
</td> \
';
--- a/ckan/templates/group/history.html Mon Oct 17 10:00:49 2011 +0100
+++ b/ckan/templates/group/history.html Mon Oct 17 11:33:04 2011 +0100
@@ -11,13 +11,13 @@
<h3>
Revisions
- <p class="atom-feed-link group-history-link">
+ <!--p class="atom-feed-link group-history-link"><a
href="${url(controller='group', action='history', id=c.group_dict['name'], format='atom', days=7)}"
title="${g.site_title} - Group History - ${c.group_dict['name']}"
>
Subscribe »</a>
- </p>
+ </p--></h3><form id="group-revisions" action="diff" method="post"
xmlns:py="http://genshi.edgewall.org/"
--- a/ckan/templates/group/layout.html Mon Oct 17 10:00:49 2011 +0100
+++ b/ckan/templates/group/layout.html Mon Oct 17 11:33:04 2011 +0100
@@ -31,10 +31,10 @@
<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">
+ <!-- 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>
+ </li--></ul></py:match>
--- a/ckan/templates/js_strings.html Mon Oct 17 10:00:49 2011 +0100
+++ b/ckan/templates/js_strings.html Mon Oct 17 11:33:04 2011 +0100
@@ -21,6 +21,7 @@
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>';
/*
* Used in templates.js.
@@ -41,7 +42,7 @@
CKAN.Strings.mimetypeInner = "${_('Mimetype (Inner)')}";
CKAN.Strings.hash = "${_('Hash')}";
CKAN.Strings.id = "${_('ID')}";
- CKAN.Strings.deleteResource = "${_('Delete Resource')}";
+ CKAN.Strings.doneEditing = "${_('Done')}";
CKAN.Strings.resourceHasUnsavedChanges = "${_('This resource has unsaved changes.')}";
--- a/ckan/templates/layout_base.html Mon Oct 17 10:00:49 2011 +0100
+++ b/ckan/templates/layout_base.html Mon Oct 17 11:33:04 2011 +0100
@@ -81,7 +81,7 @@
</header></div><py:with vars="messages = list(h._flash.pop_messages())">
- <div class="class container" py:if="len(messages)">
+ <div class="flash-messages container"><div class="${m.category}" py:for="m in messages">
${h.literal(m)}
</div>
@@ -215,7 +215,6 @@
<!-- TODO should not be necessary; we use AJAX to produce consistent previews --><script type="text/javascript" src="${g.site_url}/scripts/vendor/jquery.fileupload/20110801/jquery.iframe-transport.js"></script><script type="text/javascript" src="${g.site_url}/scripts/vendor/jquery.fileupload/20110801/jquery.fileupload.js"></script>
- <script src="https://raw.github.com/okfn/ckanjs/master/pkg/ckanjs.js"></script><script type="text/javascript" src="${g.site_url}/scripts/ckanjs.js"></script><!-- Translated js strings live inside an html template. --><xi:include href="js_strings.html" />
--- a/ckan/templates/package/history.html Mon Oct 17 10:00:49 2011 +0100
+++ b/ckan/templates/package/history.html Mon Oct 17 11:33:04 2011 +0100
@@ -7,7 +7,7 @@
<py:def function="page_heading">History: ${c.pkg.title or c.pkg.name}</py:def><!-- Sidebar -->
- <py:match path="primarysidebar">
+ <!--py:match path="primarysidebar"><li class="widget-container widget_text"><h4>Updates</h4><p class="atom-feed-link dataset-history-link">
@@ -18,6 +18,7 @@
</p></li></py:match>
+ --><div py:match="content" class="dataset"><h3>Revisions</h3>
--- a/ckan/templates/package/layout.html Mon Oct 17 10:00:49 2011 +0100
+++ b/ckan/templates/package/layout.html Mon Oct 17 11:33:04 2011 +0100
@@ -16,10 +16,10 @@
<li py:attrs="{'class':'current-tab'} if c.action=='authz' else {}" py:if="h.check_access('package_edit_permissions',{'id':c.pkg.id})">
${h.subnav_link(c, h.icon('lock') + _('Authorization'), controller='package', action='authz', id=c.pkg.name)}
</li>
- <li class="action">
+ <!--li class="action">
${h.subnav_link(c, h.icon('atom_feed') + _('Subscribe'),
controller='package', action='history', id=c.pkg.name, format='atom', days=7)}
- </li>
+ </li--></ul></py:if></py:match>
--- a/ckan/templates/package/new_package_form.html Mon Oct 17 10:00:49 2011 +0100
+++ b/ckan/templates/package/new_package_form.html Mon Oct 17 11:33:04 2011 +0100
@@ -52,9 +52,9 @@
<dt class="description-label"><label class="field_opt" for="notes">Description</label></dt><dd class="description-field"><div class="markdown-editor">
- <ul class="tabs">
- <li><a href="#" action="write" class="selected">Write</a></li>
- <li><a href="#" action="preview">Preview</a></li>
+ <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><div class="markdown-preview" style="display: none;"></div>
@@ -91,54 +91,21 @@
<table class="resource-table edit"><thead><tr>
- <th class="resource-expand-link"></th>
- <th class="field_req resource-url">URL</th>
- <th class="field_opt resource-description">Name</th>
- <th class="field_opt resource-format">Format</th>
- <th class="field_opt resource-is-changed"></th>
+ <th class="field_req resource-url">Resource</th>
+ <th class="resource-delete-link"></th></tr></thead><tbody class="resource-table">
- <py:for each="num, res in enumerate(data.get('resources', []))">
- <tr class="resource-summary">
- <td class="resource-expand-link">
- <a class="resource-expand-link" href="#"><img src="/images/icons/edit-expand.png" /></a>
- <a class="resource-expand-link" href="#" style="display: none;"><img src="/images/icons/edit-collapse.png" /></a>
- </td>
- <td class="resource-summary resource-url">
- ${res.get('url', '')}
- </td>
- <td class="resource-summary resource-name">
- ${res.get('name', '')}
- </td>
- <td class="resource-summary resource-format">
- ${res.get('format', '')}
- </td>
- <td class="resource-expanded" colspan="3" style="display: none;">
- <!-- Replaced with a javascript template -->
- <input name="resources__${num}__url" type="text" value="${res.get('url', '')}" />
- <input name="resources__${num}__format" type="text" value="${res.get('format', '')}" />
- <input name="resources__${num}__description" type="text" value="${res.get('description', '')}" />
- <input name="resources__${num}__hash" type="text" value="${res.get('hash', '')}" />
- <input name="resources__${num}__id" type="hidden" value="${res.get('id', '')}" />
- </td>
- <td class="resource-is-changed">
- </td>
- </tr>
- </py:for>
- <py:if test="not len(data.get('resources', []))">
- <tr class="table-empty"><td colspan="5">(none)</td></tr>
- </py:if></tbody></table><div class="resource-add">
- <ul class="tabs">
+ <ul class="button-row"><li><h4>Add a resource:</h4></li>
- <li><a href="#" action="link-file" class="action-resource-tab">Link to a file</a></li>
- <li><a href="#" action="link-api" class="action-resource-tab">Link to an API</a></li>
- <li class="upload-file" style="display:none;"><a href="#" action="upload-file" class="action-resource-tab">Upload a file</a></li>
+ <li><button class="pretty-button js-link-file">Link to a file</button></li>
+ <li><button class="pretty-button js-link-api">Link to an API</button></li>
+ <li class="js-upload-file" style="display:none;"><button class="pretty-button js-upload-file">Upload a file</button></li></ul></div></fieldset>
--- a/ckan/templates/package/read.html Mon Oct 17 10:00:49 2011 +0100
+++ b/ckan/templates/package/read.html Mon Oct 17 11:33:04 2011 +0100
@@ -49,14 +49,11 @@
<li><h3>Tags</h3>
${tag_list(c.pkg_dict.get('tags', ''))}
- <span class="widget_action" py:if="h.check_access('package_update',{'id':c.pkg.id})">
- ${h.subnav_link(c, 'add tags »', controller='package', action='edit', id=c.pkg.name)}
- </span></li><li><h3>Groups</h3><py:if test="c.pkg.groups">
- <ul>
+ <ul class="groups"><li py:for="group in sorted(c.pkg.groups, key=lambda g: g.display_name)"><a href="${h.url_for(controller='group', action='read', id=group.name)}">${group.display_name}</a></li>
@@ -66,11 +63,6 @@
<py:if test="not c.pkg.groups">
Groups are collections of dataset maintained by users of ${g.site_title}. This dataset has not been added to any groups yet.
</py:if>
-
-
- <p class="widget_action" py:if="h.check_access('package_update',{'id':c.pkg.id})">
- ${h.subnav_link(c, 'add to a group »', controller='package', action='edit', id=c.pkg.name)}
- </p></p></li><li py:if="h.check_access('package_update',{'id':c.pkg.id})">
--- a/ckan/templates/revision/list.html Mon Oct 17 10:00:49 2011 +0100
+++ b/ckan/templates/revision/list.html Mon Oct 17 11:33:04 2011 +0100
@@ -9,10 +9,10 @@
<ul class="tabbed"><li class="current-tab">
${h.subnav_link(c,_('Home'), controller='revision', action='index')}</li>
- <li class="action">
+ <!--li class="action">
${h.subnav_link(c, h.icon('atom_feed') + _('Subscribe'),
controller='revision', action='index', format='atom', days=1)}
- </li>
+ </li--></ul></py:match>
--- a/ckan/templates/user/edit_user_form.html Mon Oct 17 10:00:49 2011 +0100
+++ b/ckan/templates/user/edit_user_form.html Mon Oct 17 11:33:04 2011 +0100
@@ -25,9 +25,9 @@
<dt><label for="about">About:</label></dt><dd class="description-field"><div class="markdown-editor">
- <ul class="tabs">
- <li><a href="#" action="write" class="selected">Write</a></li>
- <li><a href="#" action="preview">Preview</a></li>
+ <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="about" id="about" placeholder="${_('A little about you...')}">${data.get('about','')}</textarea><div class="markdown-preview" style="display: none;"></div>
http://bitbucket.org/okfn/ckan/changeset/c51386c9b26b/
changeset: c51386c9b26b
branch: release-v1.5
user: dread
date: 2011-10-17 12:33:48
summary: [transplant] from branch feature-1368-ux-fixes from 5f095 to 95376. All of that branch should be in here now.
affected #: 31 files (-1 bytes)
--- a/ckan/config/routing.py Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/config/routing.py Mon Oct 17 11:33:48 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/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 Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/controllers/api.py Mon Oct 17 11:33:48 2011 +0100
@@ -9,12 +9,11 @@
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
from ckan.lib.jsonp import jsonpify
-from ckan.forms.common import package_exists
+from ckan.forms.common import package_exists, group_exists
log = logging.getLogger(__name__)
@@ -562,17 +561,17 @@
out = map(convert_to_dict, query.all())
return out
- def create_slug(self):
-
- 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)
- return self._finish_ok(response_data)
+ def is_slug_valid(self):
+ slug = request.params.get('slug') or ''
+ slugtype = request.params.get('type') or ''
+ if slugtype==u'package':
+ response_data = dict(valid=not bool(package_exists(slug)))
+ return self._finish_ok(response_data)
+ if slugtype==u'group':
+ response_data = dict(valid=not bool(group_exists(slug)))
+ return self._finish_ok(response_data)
+ return self._finish_bad_request(gettext('Bad slug type: %s') % slugtype)
+
def tag_autocomplete(self):
q = request.params.get('incomplete', '')
--- a/ckan/controllers/group.py Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/controllers/group.py Mon Oct 17 11:33:48 2011 +0100
@@ -79,6 +79,12 @@
abort(404, _('Group not found'))
except NotAuthorized:
abort(401, _('Unauthorized to read group %s') % id)
+ try:
+ description_formatted = ckan.misc.MarkdownFormat().to_html(group.get('description',''))
+ c.description_formatted = genshi.HTML(description_formatted)
+ except Exception, e:
+ error_msg = "<span class='inline-warning'>%s</span>" % _("Cannot render description")
+ c.description_formatted = genshi.HTML(error_msg)
try:
@@ -140,13 +146,14 @@
old_data, errors = validate(old_data, schema, context=context)
data = data or old_data
-
except NotFound:
abort(404, _('Group not found'))
except NotAuthorized:
abort(401, _('Unauthorized to read group %s') % '')
group = context.get("group")
+ c.group = group
+
try:
check_access('group_update',context)
@@ -208,6 +215,7 @@
context = {'model':model,'user':c.user or c.author, 'group':group}
check_access('group_edit_permissions',context)
c.authz_editable = True
+ c.group = context['group']
except NotAuthorized:
c.authz_editable = False
if not c.authz_editable:
--- a/ckan/controllers/package_formalchemy.py Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/controllers/package_formalchemy.py Mon Oct 17 11:33:48 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/forms/common.py Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/forms/common.py Mon Oct 17 11:33:48 2011 +0100
@@ -39,6 +39,11 @@
if pkg != field.parent.model:
raise formalchemy.ValidationError(_('Dataset name already exists in database'))
+def group_exists(val):
+ if model.Session.query(model.Group).autoflush(False).filter_by(name=val).count():
+ return True
+ return False
+
def group_name_validator(val, field=None):
name_validator(val, field)
# we disable autoflush here since may get used in dataset preview
--- a/ckan/lib/munge.py Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/lib/munge.py Mon Oct 17 11:33:48 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/schema.py Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/logic/schema.py Mon Oct 17 11:33:48 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/logic/validators.py Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/logic/validators.py Mon Oct 17 11:33:48 2011 +0100
@@ -93,7 +93,7 @@
if len(val) < 2:
raise Invalid(_('Name must be at least %s characters long') % 2)
if not name_match.match(val):
- raise Invalid(_('Name must be purely lowercase alphanumeric '
+ raise Invalid(_('Url must be purely lowercase alphanumeric '
'(ascii) characters and these symbols: -_'))
return val
@@ -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 Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/public/css/style.css Mon Oct 17 11:33:48 2011 +0100
@@ -183,10 +183,13 @@
#minornavigation li.current-tab {
background: #000;
background-color: #fff;
- border: 1px solid #aaa;
+ border: 1px solid #777;
+ border-bottom: 1px solid #ccc;
+ border-right: 1px solid #ccc;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
border-radius: 5px;
+
}
#minornavigation li.current-tab a,
#minornavigation li.current-tab a:hover,
@@ -885,18 +888,19 @@
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;
-
- background: #eee;
+a.url-edit {
+ font-weight: normal;
+ margin-left: 10px;
+}
+dd.name-field {
+ padding-top: 0.2em;
+}
+dd.name-field p {
+ margin-bottom: 4px;
}
body.package.read #sidebar ul.tags,
@@ -904,6 +908,10 @@
margin-bottom: 10px;
}
+input.url-input {
+ width: 250px;
+}
+
.success .new-dataset {
font-size: 150%;
@@ -912,6 +920,23 @@
font-weight: bold;
}
+
+/* ================== */
+/* = Add Group Page = */
+/* ================== */
+
+body.group.new fieldset#extras,
+body.group.new fieldset#datasets {
+ display: none;
+}
+body.group.new .description-label,
+body.group.new .description-field,
+body.group.new .state-label,
+body.group.new .state-field {
+ display: none;
+}
+
+
/* ============= */
/* = Mini-Tabs = */
/* ============= */
@@ -929,12 +954,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 +964,6 @@
body.index.home #minornavigation {
display: none;
}
-
body.index.home #sidebar {
display: none;
}
@@ -1067,10 +1087,10 @@
body.admin .actions input {
margin: 0;
}
-body.admin.authz form {
+body.authz form {
margin-bottom: 30px;
}
-body.admin.authz form button {
+body.authz form button {
width: 120px;
float: right;
}
@@ -1206,30 +1226,15 @@
/* = Inline resource edit form = */
/* ============================= */
-
-
-/*
-table.resource-table tbody tr td.resource-delete-link {
- vertical-align: top;
+.resource-table-edit tbody tr td,
+.resource-table-view tbody tr td {
+ /* Constrain structure against overflow */
+ max-width: 200px;
+ overflow: hidden;
+ white-space: nowrap;
}
-td.resource-expanded {
- padding: 8px 0 8px 0;
-}
-.resource-expand-link,
-.resource-delete-link {
- padding-top: 6px;
- text-align: center;
- width: 20px;
-}
-div.inner {
- padding-right: 10px;
-}
-div.inner table {
- margin-bottom: 0;
-}
-*/
-.resource-table tr {
+.resource-table-edit tr {
overflow: hidden;
}
th.resource-edit-delete,
@@ -1259,7 +1264,7 @@
/* Override screen.css */
margin: 10px 0;
}
-table.resource-table td.resource-edit tbody td {
+table.resource-table-edit td.resource-edit tbody td {
/* Override alternating background */
background: transparent;
}
--- a/ckan/public/scripts/application.js Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/public/scripts/application.js Mon Oct 17 11:33:48 2011 +0100
@@ -26,7 +26,17 @@
var isDatasetNew = $('body.package.new').length > 0;
if (isDatasetNew) {
+ // Set up magic URL slug editor
+ CKAN.Utils.setupUrlEditor('package');
$('#save').val(CKAN.Strings.addDataset);
+ $("#title").focus();
+ }
+ var isGroupNew = $('body.group.new').length > 0;
+ if (isGroupNew) {
+ // Set up magic URL slug editor
+ CKAN.Utils.setupUrlEditor('group');
+ $('#save').val(CKAN.Strings.addGroup);
+ $("#title").focus();
}
// Buttons with href-action should navigate when clicked
@@ -37,6 +47,7 @@
var isDatasetEdit = $('body.package.edit').length > 0;
if (isDatasetEdit) {
+ CKAN.Utils.setupUrlEditor('package',readOnly=true);
// Selectively enable the upload button
var storageEnabled = $.inArray('storage',CKAN.plugins)>=0;
if (storageEnabled) {
@@ -54,6 +65,10 @@
});
view.render();
}
+ var isGroupEdit = $('body.group.edit').length > 0;
+ if (isGroupEdit) {
+ CKAN.Utils.setupUrlEditor('group',readOnly=true);
+ }
});
}(jQuery));
@@ -71,6 +86,111 @@
};
+ my.bindInputChanges = function(input, callback) {
+ input.keyup(callback);
+ input.keydown(callback);
+ input.keypress(callback);
+ input.change(callback);
+ };
+
+ my.setupUrlEditor = function(slugType,readOnly) {
+ // 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');
+
+ var api_url = '/api/2/util/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: 'type='+slugType+'&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.urlIsAvailable+'</span>');
+ } else {
+ validMsg.html('<span style="font-weight: bold; color: #c00">'+CKAN.Strings.urlIsNotAvailable+'</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);
+ };
+ }();
+
+ if (readOnly) {
+ slug = urlInput.val();
+ urlSuffix.html('<span>'+slug+'</span>');
+ }
+ else {
+ var editLink = $('.js-url-editlink');
+ editLink.show();
+ // Hook title changes to the input box
+ my.bindInputChanges(titleInput, titleChanged);
+ my.bindInputChanges(urlInput, urlChanged);
+ // Set up the form
+ urlChanged();
+
+ 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
@@ -231,7 +351,6 @@
markdownEditor.find('button, div.markdown-preview').live('click', function(e) {
e.preventDefault();
var $target = $(e.target);
- console.log('clicked');
// Extract neighbouring elements
var markdownEditor=$target.closest('.markdown-editor')
markdownEditor.find('button').removeClass('depressed');
@@ -286,116 +405,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 || {});
@@ -434,7 +443,7 @@
});
// Table for editing resources
- var $el = this.el.find('.resource-table.edit');
+ var $el = this.el.find('.js-resource-editor');
this.resourceList=new CKAN.View.ResourceEditList({
collection: resources,
el: $el
@@ -484,7 +493,7 @@
}
));
$tr.find('.js-resource-edit-expanded').hide();
- this.el.find('tbody.resource-table').append($tr);
+ this.el.append($tr);
resource.view_tr = $tr;
// == Inner Function: Toggle the expanded options set == //
@@ -520,14 +529,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;
- $link.html(newName);
+ newName = newName || ('<em>'+CKAN.Strings.noNameBrackets+'</em>');
+ // Need to structurally modify the DOM to force a re-render of text
+ $link.html('<ema>'+newName+'</span>');
};
var nameBoxChanged = function(e) {
setName($(e.target).val());
@@ -539,10 +554,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/authz.html Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/templates/group/authz.html Mon Oct 17 11:33:48 2011 +0100
@@ -2,24 +2,25 @@
xmlns:xi="http://www.w3.org/2001/XInclude"
py:strip="">
- <py:def function="page_title">${c.grouptitle or c.groupname} - Authorization - Groups</py:def>
- <py:def function="page_heading">Authorization: ${c.grouptitle or c.groupname}</py:def>
-
+ <py:def function="page_title">Authorization: ${c.group.display_name}</py:def>
+ <py:def function="page_heading">Authorization: ${c.group.display_name}</py:def>
+
<div py:match="content"><h3>Update Existing Roles</h3><form id="theform" method="POST">
${authz_form_table('theform', c.roles, c.users, c.user_role_dict)}
- <button type="submit" name="save">
- Save
+ <button type="submit" name="save" class="pretty-button primary">
+ Save Changes
</button>
+ <div class="clear"></div></form><h3>Add Roles for Any User</h3>
-
<form id="addform" method="POST">
${authz_add_table(c.roles)}
- <button type="submit" name="add"> Add </button>
+ <button type="submit" name="add" class="pretty-button primary">Add Role</button>
+ <div class="clear"></div></form><hr/>
@@ -28,16 +29,16 @@
<form id="authzgroup_form" method="POST">
${authz_form_group_table('authzgroup_form', c.roles, c.authz_groups, c.authz_groups_role_dict)}
- <button type="submit" name="authz_save">
- Save
- </button>
+ <button type="submit" name="authz_save" class="pretty-button primary">Save Changes</button>
+ <div class="clear"></div></form><h3>Add Roles for Any Authorization Group</h3><form id="authzgroup_addform" method="POST">
${authz_add_group_table(c.roles)}
- <button type="submit" name="authz_add"> Add </button>
+ <button type="submit" name="authz_add" class="pretty-button primary">Add Role</button>
+ <div class="clear"></div></form></div>
--- a/ckan/templates/group/edit.html Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/templates/group/edit.html Mon Oct 17 11:33:48 2011 +0100
@@ -2,8 +2,8 @@
xmlns:xi="http://www.w3.org/2001/XInclude"
py:strip="">
- <py:def function="page_title">${c.grouptitle or c.groupname} - Edit - Groups</py:def>
- <py:def function="page_heading">Edit: ${c.grouptitle or c.groupname}</py:def>
+ <py:def function="page_title">Edit: ${c.group.display_name}</py:def>
+ <py:def function="page_heading">Edit: ${c.group.display_name}</py:def><div py:match="content">
--- a/ckan/templates/group/history.html Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/templates/group/history.html Mon Oct 17 11:33:48 2011 +0100
@@ -2,13 +2,10 @@
xmlns:xi="http://www.w3.org/2001/XInclude"
py:strip="">
- <py:def function="page_title">${c.group_dict['display_name']} - Groups - History</py:def>
+ <py:def function="page_title">History: ${c.group.display_name}</py:def>
+ <py:def function="page_heading">History: ${c.group.display_name}</py:def><div py:match="content" class="group">
- <h2 class="head">
- ${c.group_dict['display_name']}
- </h2>
-
<h3>
Revisions
<!--p class="atom-feed-link group-history-link">
@@ -49,7 +46,7 @@
</tr></py:for></table>
- ${h.submit('diff', _('Compare »'))}
+ <input type="submit" name="diff" value="${_('Compare »')}" class="pretty-button primary" /></form></div><!-- content -->
--- a/ckan/templates/group/index.html Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/templates/group/index.html Mon Oct 17 11:33:48 2011 +0100
@@ -5,16 +5,19 @@
<py:def function="page_title">Groups of Datasets</py:def><py:def function="page_heading">Groups of Datasets</py:def>
+
+ <py:match path="primarysidebar">
+ <li class="widget-container boxed widget_text">
+ <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>
+
<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 Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/templates/group/layout.html Mon Oct 17 11:33:48 2011 +0100
@@ -6,36 +6,29 @@
py:strip=""
>
- <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>
- </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 Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/templates/group/new.html Mon Oct 17 11:33:48 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 Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/templates/group/new_group_form.html Mon Oct 17 11:33:48 2011 +0100
@@ -12,23 +12,34 @@
</ul></div>
-<fieldset>
- <legend>Details</legend>
+<fieldset id="basic-information"><dl>
- <dt><label class="field_opt" for="name">Name *</label></dt>
- <dd><input id="name" name="name" type="text" value="${data.get('name', '')}"/></dd>
- <dd class="instructions basic"><br/><strong>Unique identifier</strong> for group.<br/>2+ chars, lowercase, using only 'a-z0-9' and '-_'</dd>
+ <dt><label class="field_opt" for="name">Title</label></dt>
+ <dd><input class="js-title" id="title" name="title" type="text" value="${data.get('title', '')}"/></dd>
+
+ <dt><label class="field_opt" for="title">Url</label></dt>
+ <dd class="name-field">
+ <span class="url-text">${g.site_url+h.url_for(controller='group',id=None)+'/'}<span class="js-url-viewmode js-url-suffix"> </span><a style="display: none;" 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 instructions basic">2+ chars, lowercase, using only 'a-z0-9' and '-_'</dd><dd class="field_error" py:if="errors.get('name', '')">${errors.get('name', '')}</dd>
- <dt><label class="field_opt" for="title">Title</label></dt>
- <dd><input id="title" name="title" type="text" value="${data.get('title', '')}"/></dd>
- <dd class="field_error" py:if="errors.get('title', '')">${errors.get('title', '')}</dd>
+ <dt class="description-label"><label class="field_opt" for="title">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" name="description" id="notes" placeholder="${_('Start with a summary sentence ...')}">${data.get('description','')}</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>
+ </div></dd>
- <dt><label class="field_opt" for="title">Description</label></dt>
- <dd><textarea cols="60" id="description" name="description" rows="15">${data.get('description', '')}</textarea></dd>
- <dt py:if="c.is_sysadmin or c.auth_for_change_state"><label class="field_opt" for="state">State</label></dt>
- <dd py:if="c.is_sysadmin or c.auth_for_change_state">
+ <dt class="state-label" py:if="c.is_sysadmin or c.auth_for_change_state"><label class="field_opt" for="state">State</label></dt>
+ <dd class="state-field" py:if="c.is_sysadmin or c.auth_for_change_state"><select id="state" name="state" ><option py:attrs="{'selected': 'selected' if data.get('state') == 'active' else None}" value="active">active</option><option py:attrs="{'selected': 'selected' if data.get('state') == 'deleted' else None}" value="deleted">deleted</option>
@@ -37,8 +48,8 @@
</dl></fieldset>
-<fieldset>
- <legend>Extras</legend>
+<fieldset id="extras">
+ <h3>Extras</h3><dl><py:with vars="extras = data.get('extras', [])"><py:for each="num, extra in enumerate(data.get('extras', []))">
@@ -62,8 +73,8 @@
</dl></fieldset>
-<fieldset>
- <legend>Datasets</legend>
+<fieldset id="datasets">
+ <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>
@@ -73,18 +84,18 @@
</py:for></dl><p py:if="not data.get('packages')">There are no datasets currently in this group.</p>
-</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></dl></fieldset>
- <br />
- <input id="save" name="save" type="submit" value="Save" />
+<div class="form-submit">
+ <input id="save" class="pretty-button primary" name="save" type="submit" value="${_('Save Changes')}" />
+ <py:if test="c.group">
+ <input id="cancel" class="pretty-button href-action" name="cancel" type="reset" value="${_('Cancel')}" action="${h.url_for(controller='group', action='read', id=c.group.name)}" />
+ </py:if>
+</div></form>
--- a/ckan/templates/group/read.html Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/templates/group/read.html Mon Oct 17 11:33:48 2011 +0100
@@ -3,21 +3,12 @@
xmlns:xi="http://www.w3.org/2001/XInclude"
py:strip="">
- <py:def function="page_title">${c.group.display_name} - Groups</py:def>
+ <py:def function="page_title">${c.group.display_name}</py:def>
+ <py:def function="page_heading">${c.group.display_name}</py:def>
- <py:def function="page_heading">
- <py:if test="not c.group.title">
- <em>No Title</em>
- </py:if>
- ${c.group.title}
- </py:def>
-
<py:match path="primarysidebar"><li class="widget-container widget_text">
- <div class="description">
- ${c.group_description_formatted}
- </div><ul class="property-list"><py:if test="c.group_admins"><li>
@@ -32,6 +23,9 @@
</py:match><py:match path="content">
+ <div class="notes" py:if="str(c.group_description_formatted).strip()">
+ ${c.group_description_formatted}
+ </div><h3>Datasets:</h3><p i18n:msg="item_count">There are ${c.page.item_count} datasets in this group.</p>
${c.page.pager()}
--- a/ckan/templates/js_strings.html Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/templates/js_strings.html Mon Oct 17 11:33:48 2011 +0100
@@ -14,14 +14,17 @@
* Used in application.js.
*/
CKAN.Strings.helloWorld = "${_('Hello there, world!')}";
- CKAN.Strings.datasetNameAvailable = "${_('This dataset name is available!')}";
- CKAN.Strings.datasetNameNotAvailable = "${_('This dataset name is already used, please use a different name')}";
+ CKAN.Strings.checking = "${_('Checking...')}";
+ CKAN.Strings.urlIsAvailable = "${_('This URL is available!')}";
+ CKAN.Strings.urlIsNotAvailable = "${_('This URL is already used, please use a different one.')}";
CKAN.Strings.bracketsNone = "${_('(none)')}";
CKAN.Strings.failedToSave = "${_('Failed to save, possibly due to invalid data ')}";
CKAN.Strings.addDataset = "${_('Add Dataset')}";
+ CKAN.Strings.addGroup = "${_('Add Group')}";
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.
--- a/ckan/templates/package/authz.html Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/templates/package/authz.html Mon Oct 17 11:33:48 2011 +0100
@@ -2,7 +2,7 @@
xmlns:xi="http://www.w3.org/2001/XInclude"
py:strip="">
- <py:def function="page_title">${c.pkgtitle or c.pkgname} - Authorization - Datasets</py:def>
+ <py:def function="page_title">Authorization: ${c.pkgtitle or c.pkgname}</py:def><py:def function="page_heading">Authorization: ${c.pkgtitle or c.pkgname}</py:def><div py:match="content">
@@ -10,16 +10,17 @@
<form id="theform" method="POST">
${authz_form_table('theform', c.roles, c.users, c.user_role_dict)}
- <button type="submit" name="save">
- Save
+ <button type="submit" name="save" class="pretty-button primary">
+ Save Changes
</button>
+ <div class="clear"></div></form><h3>Add Roles for Any User</h3>
-
<form id="addform" method="POST">
${authz_add_table(c.roles)}
- <button type="submit" name="add"> Add </button>
+ <button type="submit" name="add" class="pretty-button primary">Add Role</button>
+ <div class="clear"></div></form><hr/>
@@ -28,16 +29,16 @@
<form id="authzgroup_form" method="POST">
${authz_form_group_table('authzgroup_form', c.roles, c.authz_groups, c.authz_groups_role_dict)}
- <button type="submit" name="authz_save">
- Save
- </button>
+ <button type="submit" name="authz_save" class="pretty-button primary">Save Changes</button>
+ <div class="clear"></div></form><h3>Add Roles for Any Authorization Group</h3><form id="authzgroup_addform" method="POST">
${authz_add_group_table(c.roles)}
- <button type="submit" name="authz_add"> Add </button>
+ <button type="submit" name="authz_add" class="pretty-button primary">Add Role</button>
+ <div class="clear"></div></form></div>
--- a/ckan/templates/package/edit.html Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/templates/package/edit.html Mon Oct 17 11:33:48 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/history.html Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/templates/package/history.html Mon Oct 17 11:33:48 2011 +0100
@@ -53,7 +53,7 @@
</tr></py:for></table>
- ${h.submit('diff', _('Compare »'))}
+ <input type="submit" name="diff" value="${_('Compare »')}" class="pretty-button primary" /></form></div><!-- content -->
--- a/ckan/templates/package/new.html Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/templates/package/new.html Mon Oct 17 11:33:48 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 Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/templates/package/new_package_form.html Mon Oct 17 11:33:48 2011 +0100
@@ -31,32 +31,46 @@
<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">${g.site_url+h.url_for(controller='package', id=None)+'/'}<span class="js-url-viewmode js-url-suffix"> </span><a href="#" style="display: none;" 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 basic">2+ characters, lowercase, using only 'a-z0-9' and '-_'</dd>
+ <dd 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,38 +78,19 @@
<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><fieldset id="resources"><div class="instructions basic"><h3>Resources: the files and APIs associated with this dataset</h3></div>
- <table class="resource-table edit">
+ <table class="resource-table-edit"><thead><tr><th class="field_req resource-url">Resource</th><th class="resource-delete-link"></th></tr></thead>
- <tbody class="resource-table">
+ <tbody class="js-resource-editor"></tbody></table>
@@ -111,6 +106,7 @@
</fieldset><fieldset id="groups">
+ <h3>Groups</h3><dl><py:for each="num, group in enumerate(data.get('groups', []))"><?python
@@ -134,10 +130,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 +215,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/templates/package/read_core.html Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/templates/package/read_core.html Mon Oct 17 11:33:48 2011 +0100
@@ -13,7 +13,7 @@
<!-- Resources --><div class="resources subsection"><h3>Resources</h3>
- <table>
+ <table class="resource-table-view"><thead><th>Resource</th><th>Format</th>
--- a/ckan/tests/functional/api/test_ajax.py Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/tests/functional/api/test_ajax.py Mon Oct 17 11:33:48 2011 +0100
@@ -14,15 +14,28 @@
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',
+ 'type': u'package',
+ '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={
+ 'type': u'package',
+ '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 Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/tests/functional/test_group.py Mon Oct 17 11:33:48 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,21 +282,20 @@
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
res = fv.submit('save', status=302, extra_environ={'REMOTE_USER': 'russianfan'})
res = res.follow()
assert group_name in res, res
- assert 'No Title' in res, res
model.Session.remove()
# Create duplicate group
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 Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/tests/functional/test_package.py Mon Oct 17 11:33:48 2011 +0100
@@ -148,11 +148,11 @@
self.check_tag(main_res, prefix+'title', params['title'])
self.check_tag(main_res, prefix+'version', params['version'])
self.check_tag(main_res, prefix+'url', params['url'])
- for res_index, res_field, expected_value in self._get_resource_values(params['resources']):
- ### only check fields that are on the form
- if res_field not in ['url', 'id', 'description', 'hash']:
- continue
- self.check_tag(main_res, '%sresources__%i__%s' % (prefix, res_index, res_field), expected_value)
+ #for res_index, res_field, expected_value in self._get_resource_values(params['resources']):
+ # ## only check fields that are on the form
+ # if res_field not in ['url', 'id', 'description', 'hash']:
+ # continue
+ # self.check_tag(main_res, '%sresources__%i__%s' % (prefix, res_index, res_field), expected_value)
self.check_tag_and_data(main_res, prefix+'notes', params['notes'])
self.check_tag_and_data(main_res, 'selected', params['license_id'])
if isinstance(params['tags'], (str, unicode)):
@@ -644,7 +644,7 @@
fv[prefix + 'name'] = new_name
fv[prefix + 'title'] = new_title
fv[prefix + 'url'] = newurl
- fv[prefix + 'resources__0__url'] = new_download_url
+ #fv[prefix + 'resources__0__url'] = new_download_url
fv[prefix + 'license_id'] = newlicense_id
fv[prefix + 'version'] = newversion
res = fv.submit('save')
@@ -657,7 +657,7 @@
pkg = model.Package.by_name(new_name)
assert pkg.title == new_title
assert pkg.url == newurl
- assert pkg.resources[0].url == new_download_url
+ #assert pkg.resources[0].url == new_download_url
assert pkg.version == newversion
assert newlicense_id == pkg.license.id
finally:
@@ -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/functional/test_user.py Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/tests/functional/test_user.py Mon Oct 17 11:33:48 2011 +0100
@@ -53,7 +53,7 @@
'http://anna.com',
'target="_blank"',
'rel="nofollow"')
- assert 'Edit' not in main_res, main_res
+ assert 'Edit Profile' not in main_res, main_res
assert 'Number of edits:</strong> 3' in res, res
assert 'Number of datasets administered:</strong> 1' in res, res
assert 'Revision History' in res, res
@@ -81,7 +81,7 @@
main_res = self.main_div(res)
assert 'annafan' in res, res
assert 'My Account' in main_res, main_res
- assert 'Edit' in main_res, main_res
+ assert 'Edit Profile' in main_res, main_res
def test_user_read_about_unfinished(self):
user = model.User.by_name(u'unfinisher')
--- a/ckan/tests/lib/test_dictization_schema.py Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/tests/lib/test_dictization_schema.py Mon Oct 17 11:33:48 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']}]
+ 'name': [u'Url must be purely lowercase alphanumeric (ascii) characters and these symbols: -_'],
+ #'resources': [{}, {'url': [u'Missing value']}]
},pformat(errors)
def test_2_group_schema(self):
--- a/ckan/tests/lib/test_munge.py Mon Oct 17 11:33:04 2011 +0100
+++ b/ckan/tests/lib/test_munge.py Mon Oct 17 11:33:48 2011 +0100
@@ -1,18 +1,8 @@
from nose.tools import assert_equal
-from ckan.lib.munge import munge_title_to_name, munge_name
+from ckan.lib.munge import 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)
http://bitbucket.org/okfn/ckan/changeset/f22e1a10e608/
changeset: f22e1a10e608
user: dread
date: 2011-10-17 13:06:32
summary: [merge] from release-1.5 (nop because of transplant).
affected #: 20 files (-1 bytes)
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