[ckan-changes] commit/ckan: 2 new changesets

Bitbucket commits-noreply at bitbucket.org
Tue Sep 13 10:39:04 UTC 2011


2 new changesets in ckan:

http://bitbucket.org/okfn/ckan/changeset/b4e6b3c5a385/
changeset:   b4e6b3c5a385
branch:      feature-1324-add-fields-to-resource
user:        kindly
date:        2011-09-13 10:43:23
summary:     [merge] feature-1294-ux-improvements-dataset
affected #:  10 files (5.7 KB)

--- a/ckan/controllers/package.py	Tue Sep 13 08:56:38 2011 +0100
+++ b/ckan/controllers/package.py	Tue Sep 13 09:43:23 2011 +0100
@@ -379,6 +379,7 @@
             abort(404, _('Package not found'))
 
         c.pkg = context.get("package")
+        c.pkg_json = json.dumps(data)
 
         try:
             check_access('package_update',context)
@@ -505,12 +506,12 @@
         '''
         assert action in ('new', 'edit')
         if action == 'new':
-            msg = _('Congratulations, your dataset has been created. ' \
-                    'You\'ll probably want to <a href="%s">upload or link ' \
-                    'some data</a> now.')
+            msg = _('<span class="new-dataset">Congratulations, your dataset has been created. ' \
+                    '<a href="%s">Upload or link ' \
+                    'some data now »</a></span>')
             msg = msg % h.url_for(controller='package', action='edit',
                     id=pkgname, anchor='section-resources')
-            h.flash_notice(msg,allow_html=True)
+            h.flash_success(msg,allow_html=True)
         url = request.params.get('return_to') or \
               config.get('package_%s_return_url' % action)
         if url:


--- a/ckan/public/css/style.css	Tue Sep 13 08:56:38 2011 +0100
+++ b/ckan/public/css/style.css	Tue Sep 13 09:43:23 2011 +0100
@@ -200,7 +200,8 @@
 
 /* Notices */
 
-.notice a, .notice a:visited {
+.notice a, .notice a:visited,
+.success a, .success a:visited {
   text-decoration: underline;
 }
 
@@ -785,6 +786,11 @@
   padding-top: 6px;
   text-align: center;
 }
+th.resource-is-changed, td.resource-is-changed {
+  text-align: center;
+  padding: 8px 0 0 0;
+  width: 20px;
+}
 /* ====================== */
 /* = Add Resources Page = */
 /* ====================== */
@@ -793,7 +799,55 @@
   width: 60%;
 }
 
+/* ==================== */
+/* = Add Dataset Page = */
+/* ==================== */
 
+body.package.new dt.homepage-label,
+body.package.new dd.homepage-field,
+body.package.new dd.homepage-instructions 
+{
+  display: none;
+}
+body.package.new dt.tags-label,
+body.package.new dd.tags-field,
+body.package.new dd.tags-instructions 
+{
+  display: none;
+}
+body.package.new dt.description-label,
+body.package.new dd.description-field,
+body.package.new dd.description-instructions 
+{
+  display: none;
+}
+body.package.new dt.license-label,
+body.package.new dd.license-field,
+body.package.new dd.license-instructions 
+{
+  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;
+}
+
+.success .new-dataset {
+  font-size: 150%;
+}
+.success .new-dataset a {
+  font-weight: bold;
+}
 
 /* =============================== */
 /* = Mini-Tabs [Markdown Editor] = */


--- a/ckan/public/scripts/application.js	Tue Sep 13 08:56:38 2011 +0100
+++ b/ckan/public/scripts/application.js	Tue Sep 13 09:43:23 2011 +0100
@@ -28,19 +28,11 @@
     if (isDatasetEdit) {
       // Set up hashtag nagivigation
       CKAN.Utils.setupDatasetEditNavigation();
-      // Set up edit table expanding
-      $('a.resource-expand-link').live('click', function(e) {
-        e.preventDefault();
-        $tr = $(e.target).closest('.resource-summary');
-        $tr.find('a.resource-expand-link').toggle();
-        $tr.find('.resource-summary').toggle();
-        $tr.find('.resource-expanded').toggle();
-      });
 
-      // Create Backbone view for adding resources
-      var $el=$('.resource-add');
-      var view=new CKAN.View.ResourceAdd({
-        model: null,
+      var _dataset = new CKAN.Model.Dataset(preload_dataset);
+      var $el=$('form#dataset-edit');
+      var view=new CKAN.View.DatasetEdit({
+        model: _dataset,
         el: $el
       });
       view.render();
@@ -406,9 +398,173 @@
 });
 
 
+CKAN.View.DatasetEdit = Backbone.View.extend({
+  initialize: function() {
+    _.bindAll(this, 'render');
+
+    var boundToUnload = false;
+    this.el.change(function() {
+      if (!boundToUnload) {
+        boundToUnload = true;
+        window.onbeforeunload = function () { 
+          return "You have unsaved changes. Hit Save Changes at the bottom of the page to submit them."; 
+        };
+      }
+    });
+    this.el.submit(function() {
+      // Don't stop us leaving
+      window.onbeforeunload = null;
+    });
+
+
+
+    // Create Backbone view for adding resources
+    var $el=this.el.find('.resource-add');
+    this.addView=new CKAN.View.ResourceAdd({
+      collection: this.model.get('resources'),
+      el: $el
+    });
+
+    // Create Resource Edit list
+    var $el=this.el.find('.resource-table.edit');
+    this.resourceList=new CKAN.View.ResourceEditList({
+      collection: this.model.get('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);
+  },
+
+  nextIndex: function() {
+    var maxId=-1;
+    this.el.find('input').each(function(idx,input) {
+      var myId = parseInt($(input).attr('name').split('__')[1])
+      maxId = Math.max(myId, maxId);
+    });
+    return maxId+1;
+  },
+
+  addRow: function(resource) {
+    // TODO tidy up so the view creates its own elements
+    var $tr = $('<tr />');
+    this.el.find('tbody').append($tr);
+    var _view = new CKAN.View.ResourceEdit({
+      model: resource,
+      el: $tr,
+      position: this.nextIndex()
+    });
+    _view.render();
+  },
+
+  events: {
+  }
+});
+
+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();
+  },
+
+  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();
+    }
+    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();
+    }
+  },
+
+  events: {
+    'click a.resource-expand-link': 'toggleExpanded',
+    'click a.resource-collapse-link': 'toggleExpanded'
+  },
+
+  saveData: function() {
+    this.model.set(this.getData(), {
+      error: function(model, error) {
+        var msg = 'Failed to save, possibly due to invalid data ';
+        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;
+    // 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({
   initialize: function() {
-    _.bindAll(this, 'render');
+    _.bindAll(this, 'render', 'addNewResource', 'reset');
   },
 
   render: function() {
@@ -419,36 +575,35 @@
     'click input[name=reset]': 'reset'
   },
 
-  reset: function(e) {
-    e.preventDefault();
+  reset: function() {
     this.el.find('.tabs a').removeClass('selected');
-    this.deleteSubpane();
-  },
-
-  deleteSubpane: function() {
-    this.el.find('.resource-add-subpane').remove();
+    if (this.subView != null) {
+      this.subView.remove();
+      this.subView = null;
+    }
+    return false;
   },
 
   clickAdd: function(e) {
     e.preventDefault();
+
+    this.reset();
+
     var action = $(e.target).attr('action');
     this.el.find('.tabs a').removeClass('selected');
     this.el.find('.tabs a[action='+action+']').addClass('selected');
 
-    this.deleteSubpane();
-
     var $subPane = $('<div />').addClass('resource-add-subpane');
     this.el.append($subPane);
 
-    var resource = new CKAN.Model.Resource({
-      'dataset': this.model
-    });
-    resource.bind('change', this.addNewResource);
+    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: resource,
+        model: tempResource,
         // TODO: horrible reverse depedency ...
         client: CKAN.UI.workspace.client
       });
@@ -456,28 +611,21 @@
     else if (action=='link-file' || action=='link-api') {
       this.subView = new CKAN.View.ResourceAddLink({
         el: $subPane,
-        model: resource,
+        model: tempResource,
         mode: (action=='link-file')? 'file' : 'api',
         // TODO: horrible reverse depedency ...
         client: CKAN.UI.workspace.client
       });
     }
     this.subView.render();
-    return false;
   },
 
-  addNewResource: function(resource) {
-    var maxId = 0;
-    var ids = $.map($('.resource-table').find('input'), function(item, idx) {
-      var thisId = parseInt($(item).attr('name').split('__')[1]);
-      maxId=Math.max(maxId, thisId);
-    });
-    var tmplData = {
-      resource: resource.toTemplateJSON(),
-      num: maxId+1
-    };
-    var $newRow = $.tmpl(CKAN.Templates.resourceEntry, tmplData);
-    $('.resource-table tbody').append($newRow);
+  addNewResource: function(tempResource) {
+    // Deep-copy the tempResource we had bound to
+    var resource=new CKAN.Model.Resource(tempResource.toJSON());
+
+    this.collection.add(resource);
+    this.reset();
   }
 });
 


--- a/ckan/public/scripts/templates.js	Tue Sep 13 08:56:38 2011 +0100
+++ b/ckan/public/scripts/templates.js	Tue Sep 13 09:43:23 2011 +0100
@@ -73,43 +73,61 @@
 
 
 CKAN.Templates.resourceEntry = ' \
-  <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"> \
-      ${resource.url} \
-    </td> \
-    <td class="resource-summary resource-format"> \
-      ${resource.format} \
-    </td> \
-    <td class="resource-summary resource-description"> \
-      ${resource.description} \
-    </td> \
-    <td class="resource-expanded" colspan="3" style="display: none;"> \
-      <dl> \
-        <dt><label class="field_opt">Url</label></dt> \
-        <dd> \
-          <input name="resources__${num}__url" type="text" value="${resource.url}" class="long" /> \
-        </dd> \
-        <dt><label class="field_opt">Format</label></dt> \
-        <dd> \
-          <input name="resources__${num}__format" type="text" value="${resource.format}" class="long" /> \
-        </dd> \
-        <dt><label class="field_opt">Description</label></dt> \
-        <dd> \
-          <input name="resources__${num}__description" type="text" value="${resource.description}" class="long" /> \
-        </dd> \
-        <dt><label class="field_opt">Hash</label></dt> \
-        <dd> \
-          <input name="resources__${num}__hash" type="text" value="${resource.hash}" class="long" /> \
-        </dd> \
-        <dt><label class="field_opt">Id</label></dt> \
-        <dd> \
-          <input name="resources__${num}__id" disabled="disabled" type="text" value="${resource.id}" class="long disabled" /> \
-        </dd> \
-      </dl> \
-    </td> \
-  </tr> \
+  <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-format"> \
+    ${resource.format} \
+  </td> \
+  <td class="resource-summary resource-description"> \
+    ${resource.description} \
+  </td> \
+  <td class="resource-expanded" colspan="3"> \
+    <dl> \
+      <dt><label class="field_opt">Url</label></dt> \
+      <dd> \
+        <input name="resources__${num}__url" type="text" value="${resource.url}" class="long" /> \
+      </dd> \
+      <dt>Type</dt> \
+      <dd> \
+        ${resource.type} \
+      </dd> \
+      <dt>Mimetype</dt> \
+      <dd> \
+        ${resource.mimetype} \
+      </dd> \
+      <dt>Mimetype-inner</dt> \
+      <dd> \
+        ${resource.mimetype_inner} \
+      </dd> \
+      <dt>Size</dt> \
+      <dd> \
+        ${resource.size} \
+      </dd> \
+      <dt>Last Modified</dt> \
+      <dd> \
+        ${resource.lastModified} \
+      </dd> \
+      <dt><label class="field_opt">Format</label></dt> \
+      <dd> \
+        <input name="resources__${num}__format" type="text" value="${resource.format}" class="long" /> \
+      </dd> \
+      <dt><label class="field_opt">Description</label></dt> \
+      <dd> \
+        <input name="resources__${num}__description" type="text" value="${resource.description}" class="long" /> \
+      </dd> \
+      <dt><label class="field_opt">Hash</label></dt> \
+      <dd> \
+        <input name="resources__${num}__hash" type="text" value="${resource.hash}" class="long" /> \
+      </dd> \
+    </dl> \
+    <input name="resources__${num}__id" type="hidden" value="${resource.id}" class="long disabled" /> \
+  </td> \
+  <td class="resource-is-changed"> \
+    <img src="/images/icons/add.png" title="This resource has unsaved changes." class="resource-is-changed" /> \
+  </td> \
 ';


--- a/ckan/templates/package/edit.html	Tue Sep 13 08:56:38 2011 +0100
+++ b/ckan/templates/package/edit.html	Tue Sep 13 09:43:23 2011 +0100
@@ -9,9 +9,9 @@
   <py:def function="body_class">hide-sidebar</py:def><py:def function="optional_head">
-    <!-- Flexitable --> 
-    <script type="text/javascript" src="${g.site_url}/scripts/flexitable.js"></script> 
-    <link rel="stylesheet" href="${g.site_url}/css/flexitable.css" /> 
+    <script>
+      var preload_dataset = ${c.pkg_json};
+    </script></py:def><py:match path="primarysidebar">


--- a/ckan/templates/package/form_extra_fields.html	Tue Sep 13 08:56:38 2011 +0100
+++ b/ckan/templates/package/form_extra_fields.html	Tue Sep 13 09:43:23 2011 +0100
@@ -32,7 +32,7 @@
         </py:for></dl><!--
-          <table class="flexitable">
+          <table><thead><tr><th>Key</th><th>Value</th></tr></thead>


--- a/ckan/templates/package/form_resources.html	Tue Sep 13 08:56:38 2011 +0100
+++ b/ckan/templates/package/form_resources.html	Tue Sep 13 09:43:23 2011 +0100
@@ -1,6 +1,6 @@
 <table xmlns:py="http://genshi.edgewall.org/"
       xmlns:xi="http://www.w3.org/2001/XInclude"
-      class="flexitable">
+      ><thead><tr>


--- a/ckan/templates/package/new.html	Tue Sep 13 08:56:38 2011 +0100
+++ b/ckan/templates/package/new.html	Tue Sep 13 09:43:23 2011 +0100
@@ -13,10 +13,6 @@
     <script type="text/javascript" src="${g.site_url}/scripts/reveal_instructions.js"></script><link rel="stylesheet" href="${g.site_url}/css/reveal_instructions.css" /> 
 
-    <!-- Flexitable --> 
-    <script type="text/javascript" src="${g.site_url}/scripts/flexitable.js"></script> 
-    <link rel="stylesheet" href="${g.site_url}/css/flexitable.css" /> 
-
     <!-- Auto-generate 'name' field --><script type="text/javascript">
       jQuery(document).ready(function($) {


--- a/ckan/templates/package/new_package_form.html	Tue Sep 13 08:56:38 2011 +0100
+++ b/ckan/templates/package/new_package_form.html	Tue Sep 13 09:43:23 2011 +0100
@@ -15,32 +15,32 @@
 
 <fieldset id="basic-information"><dl>
-    <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="instructions basic">A short descriptive title for the dataset.</dd>
-    <dd class="instructions further">It should not be a description though - save that for the Notes field. Do not give a trailing full stop.</dd>
-    <dd class="field_error" py:if="errors.get('title', '')">${errors.get('title', '')}</dd>
+    <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" value="${data.get('title', '')}"/></dd>
+    <dd class="title-instructions instructions basic">A short descriptive title for the dataset.</dd>
+    <dd class="title-instructions instructions further">It should not be a description though - save that for the Notes field. Do not give a trailing full stop.</dd>
+    <dd class="title-instructions field_error" py:if="errors.get('title', '')">${errors.get('title', '')}</dd>
 
-    <dt><label class="field_req" for="name">Name *</label></dt>
-    <dd><input id="name" maxlength="100" name="name" type="text" value="${data.get('name', '')}" /></dd>
-    <dd class="instructions basic">A unique identifier for the dataset.</dd>
-    <dd class="instructions further">It should be broadly humanly readable, in the spirit of Semantic Web URIs. Only use an acronym if it is widely recognised. Renaming is possible but discouraged.</dd>
-    <dd class="hints">2+ characters, lowercase, using only 'a-z0-9' and '-_'</dd>
-    <dd class="field_error" py:if="errors.get('name', '')">${errors.get('name', '')}</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 for the dataset.</dd>
+    <dd class="name-instructions instructions further">It should be broadly humanly readable, in the spirit of Semantic Web URIs. Only use an acronym if it is widely recognised. 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><label class="field_opt" for="url">Home Page</label></dt>
-    <dd><input id="url" name="url" type="text" value="${data.get('url', '')}"/></dd>
-    <dd class="instructions basic">The URL for the web page describing the data (not the data itself).</dd>
-    <dd class="hints">e.g. http://www.example.com/growth-figures.html</dd>
-    <dd class="field_error" py:if="errors.get('url', '')">${errors.get('url', '')}</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-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><label class="field_opt" for="notes">Description</label></dt>
-    <dd><div class="markdown-editor">
+    <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>
-      <textarea class="markdown-input" name="notes" id="notes" placeholder="Start with a summary sentence ...">${data.get('notes','')}</textarea>
+      <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><span class="hints">You can use <a href="http://daringfireball.net/projects/markdown/syntax">Markdown formatting</a> here.</span><!--
@@ -49,24 +49,24 @@
       --></div></dd>
 
-    <dt><label class="field_opt" for="license_id">Licence</label></dt>
-    <dd>
-      <select id="license_id" name="license_id">
+    <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="instructions basic">The licence under which the dataset is released.</dd>
+    <dd class="license-instructions instructions basic">The licence under which the dataset is released.</dd>
 
-    <dt><label class="field_opt" for="tags">Tags</label></dt>
-    <dd>
-      <input class="long autocomplete-tag" id="tag_string" name="tag_string" size="60" type="text" 
+    <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="instructions basic">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="hints">e.g. pollution rivers water-quality</dd>
-    <dd class="field_error" py:if="errors.get('tag_string', '')">${errors.get('tag_string', '')}</dd>
+    <dd class="tags-instructions instructions basic">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>
 
@@ -79,6 +79,7 @@
         <th class="field_req resource-url">URL*</th><th class="field_opt resource-format">Format</th><th class="field_opt resource-description">Description</th>
+        <th class="field_opt resource-is-changed"></th></tr></thead><tbody>
@@ -117,10 +118,12 @@
             </dd><dt><label class="field_opt">Id</label></dt><dd>
-              <input name="resources__${num}__id" disabled="disabled" type="text" value="${res.get('id', '')}" class="long disabled" />
+              <input name="resources__${num}__id" type="hidden" value="${res.get('id', '')}" class="long disabled" /></dd></dl></td>
+        <td class="resource-is-changed">
+        </td></tr></py:for></tbody>
@@ -136,13 +139,6 @@
     </ul><div class="resource-add-form"></div></div>
-
-  <!--
-  <div class="instructions basic">The files containing the data or address of the APIs for accessing it.</div>
-  <div class="instructions further"><br />These can be repeated as required. For example if the data is being supplied in multiple formats, or split into different areas or time periods, each file is a different 'resource' which should be described differently. They will all appear on the dataset page on CKAN together.<br /><br /><b>URL:</b> This is the Internet link directly to the data - by selecting this link in a web browser, the user will immediately download the full dataset. Note that datasets are not hosted on this site, but by the publisher of the data. Alternatively the URL can point to an API server such as a SPARQL endpoint or JSON-P service.<br /><b>Format:</b> This should give the file format in which the data is supplied. <br /><b>Description</b> Any information you want to add to describe the resource.<br /></div>
-  <div class="hints">Format choices: CSV | RDF | XML | XBRL | SDMX | HTML+RDFa | Other as appropriate</div>
-  <div class="field_error" py:if="errors.get('resources', '')">Dataset resource(s) incomplete.</div>
-  --></fieldset><fieldset id="groups">
@@ -247,8 +243,7 @@
   <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.
   </p>
-  <input id="save" class="pretty primary" name="save" type="submit" value="Save Changes" />
-  <input id="cancel" class="pretty" name="cancel" type="reset" value="Cancel" />
+  <input id="save" tabindex="99" class="pretty primary" name="save" type="submit" value="Save Changes" /></div>
 
 


--- a/ckan/tests/functional/test_authz.py	Tue Sep 13 08:56:38 2011 +0100
+++ b/ckan/tests/functional/test_authz.py	Tue Sep 13 09:43:23 2011 +0100
@@ -98,10 +98,15 @@
                 offset = '/%s/list' % entity
         elif action == 'create':
             offset = '/%s/new' % entity
-            str_required_in_response = 'New'
+            if entity == 'dataset':
+                str_required_in_response = 'Add'
+            else:
+                str_required_in_response = 'New'
         elif action == 'delete':
             offset = url_for(controller=controller_name, action=model.Action.EDIT, id=unicode(entity_name))
-            str_required_in_response = 'state'
+            # this is ludicrously sensitive (we have to improve html testing!)
+            # str_required_in_response = 'state'
+            str_required_in_response = '<select id="state"'
         else:
             raise NotImplementedError
         res = self.app.get(offset, extra_environ={'REMOTE_USER': user.name.encode('utf8')}, expect_errors=True)
@@ -110,6 +115,8 @@
         tests['error string'] = bool('error' not in res)
         tests['status'] = bool(res.status in (200, 201))
         tests['0 packages found'] = bool(u'0 packages found' not in res)
+        print tests
+        print res
         is_ok = False not in tests.values()
         # clear flash messages - these might make the next page request
         # look like it has an error
@@ -376,7 +383,7 @@
         assert not model.Package.by_name(u'annakarenina')
         offset = url_for(controller='package', action='new')
         res = self.app.get(offset, extra_environ={'REMOTE_USER': user.name.encode('utf8')})
-        assert 'New - Datasets' in res
+        assert 'Add - Datasets' in res
         fv = res.forms['dataset-edit']
         prefix = ''
         fv[prefix + 'name'] = u'annakarenina'


http://bitbucket.org/okfn/ckan/changeset/cf2955884fa2/
changeset:   cf2955884fa2
branch:      feature-1324-add-fields-to-resource
user:        kindly
date:        2011-09-13 12:38:45
summary:     [model] fixes for extra resource fields
affected #:  5 files (2.2 KB)

--- a/ckan/lib/search/sql.py	Tue Sep 13 09:43:23 2011 +0100
+++ b/ckan/lib/search/sql.py	Tue Sep 13 11:38:45 2011 +0100
@@ -94,6 +94,10 @@
                 model_attr = getattr(model.Resource, field)
                 if field == 'hash':                
                     q = q.filter(model_attr.ilike(unicode(term) + '%'))
+                ##not text fields
+                elif field in ('size', 'last_modified', 
+                               'cache_last_updated', 'webstore_last_updated'):
+                    q = q.filter(model_attr == term)
                 elif field in model.Resource.get_extra_columns():
                     model_attr = getattr(model.Resource, 'extras')
 


--- a/ckan/tests/functional/test_package.py	Tue Sep 13 09:43:23 2011 +0100
+++ b/ckan/tests/functional/test_package.py	Tue Sep 13 11:38:45 2011 +0100
@@ -59,7 +59,7 @@
     
     def _assert_form_errors(self, res):
         self.check_tag(res, '<form', 'class="has-errors"')
-        assert 'class="field_error"' in res, res
+        assert 'field_error' in res, res
 
     def diff_responses(self, res1, res2):
         return self.diff_html(res1.body, res2.body)
@@ -148,6 +148,9 @@
         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)
         self.check_tag_and_data(main_res, prefix+'notes', params['notes'])
         self.check_tag_and_data(main_res, 'selected', params['license_id'])
@@ -789,7 +792,7 @@
             resources = ((u'http://something.com/somewhere-else.xml', u'xml', u'Best', u'hash1', 'alt'),
                          (u'http://something.com/somewhere-else2.xml', u'xml2', u'Best2', u'hash2', 'alt'),
                          )
-            assert len(resources[0]) == len(model.Resource.get_columns())
+            assert len(resources[0]) == 5
             notes = u'Very important'
             license_id = u'gpl-3.0'
             state = model.State.ACTIVE


--- a/ckan/tests/lib/test_dictization.py	Tue Sep 13 09:43:23 2011 +0100
+++ b/ckan/tests/lib/test_dictization.py	Tue Sep 13 11:38:45 2011 +0100
@@ -49,25 +49,45 @@
             'notes': u'Some test notes\n\n### A 3rd level heading\n\n**Some bolded text.**\n\n*Some italicized text.*\n\nForeign characters:\nu with umlaut \xfc\n66-style quote \u201c\nforeign word: th\xfcmb\n \nNeeds escaping:\nleft arrow <\n\n<http://ckan.net/>\n\n',
             'relationships_as_object': [],
             'relationships_as_subject': [],
-            'resources': [{'alt_url': u'alt123',
-                           'description': u'Full text. Needs escaping: " Umlaut: \xfc',
-                           'size': u'123',
-                           'format': u'plain text',
-                           'hash': u'abc123',
-                           'position': 0,
-                           'state': u'active',
-                           'url': u'http://www.annakarenina.com/download/x=1&y=2'},
-                          {'alt_url': u'alt345',
-                           'description': u'Index of the novel',
-                           'size': u'345',
-                           'format': u'json',
-                           'hash': u'def456',
-                           'position': 1,
-                           'state': u'active',
-                           'url': u'http://www.annakarenina.com/index.json'}],
+            'resources': [{u'alt_url': u'alt123',
+                            u'cache_last_updated': None,
+                            u'cache_url': None,
+                            u'description': u'Full text. Needs escaping: " Umlaut: \xfc',
+                            u'format': u'plain text',
+                            u'hash': u'abc123',
+                            u'last_modified': None,
+                            u'mimetype': None,
+                            u'mimetype_inner': None,
+                            u'name': None,
+                            u'position': 0,
+                            u'resource_type': None,
+                            u'size': None,
+                            u'size_extra': u'123',
+                            u'state': u'active',
+                            u'url': u'http://www.annakarenina.com/download/x=1&y=2',
+                            u'webstore_last_updated': None,
+                            u'webstore_url': None},
+                           {u'alt_url': u'alt345',
+                            u'cache_last_updated': None,
+                            u'cache_url': None,
+                            u'description': u'Index of the novel',
+                            u'format': u'json',
+                            u'hash': u'def456',
+                            u'last_modified': None,
+                            u'mimetype': None,
+                            u'mimetype_inner': None,
+                            u'name': None,
+                            u'position': 1,
+                            u'resource_type': None,
+                            u'size': None,
+                            u'size_extra': u'345',
+                            u'state': u'active',
+                            u'url': u'http://www.annakarenina.com/index.json',
+                            u'webstore_last_updated': None,
+                            u'webstore_url': None}],
             'state': u'active',
-                        'tags': [{'name': u'russian', 'state': u'active'},
-                                 {'name': u'tolstoy', 'state': u'active'}],
+            'tags': [{'name': u'russian', 'state': u'active'},
+                     {'name': u'tolstoy', 'state': u'active'}],
             'title': u'A Novel By Tolstoy',
             'url': u'http://www.annakarenina.com',
             'version': u'0.7a'}
@@ -139,14 +159,25 @@
 
 
         assert result == {
-             'alt_url': u'alt123',
+            u'alt_url': u'alt123',
+             'cache_last_updated': None,
+             'cache_url': None,
              'description': u'Full text. Needs escaping: " Umlaut: \xfc',
              'format': u'plain text',
              'hash': u'abc123',
+             'last_modified': None,
+             'mimetype': None,
+             'mimetype_inner': None,
+             'name': None,
              'position': 0,
-             'size': u'123',
+             'resource_type': None,
+             'size': None,
+             u'size_extra': u'123',
              'state': u'active',
-             'url': u'http://www.annakarenina.com/download/x=1&y=2'}, pprint(result)
+             'url': u'http://www.annakarenina.com/download/x=1&y=2',
+             'webstore_last_updated': None,
+             'webstore_url': None
+            }, pprint(result)
 
         ## package extra
 
@@ -629,12 +660,23 @@
         forth_dictized = self.remove_changable_columns(package_dictize(anna1, context))
 
         third_dictized['notes'] = 'wee'
-        third_dictized['resources'].insert(2, {u'description': u'',
-                                            u'format': u'plain text',
-                                            u'hash': u'',
-                                            u'position': 2,
-                                            u'state': u'active',
-                                            u'url': u'newurl'})
+        third_dictized['resources'].insert(2, {
+            u'cache_last_updated': None,
+            u'cache_url': None,
+            u'description': u'',
+            u'format': u'plain text',
+            u'hash': u'',
+            u'last_modified': None,
+            u'mimetype': None,
+            u'mimetype_inner': None,
+            u'name': None,
+            u'position': 2,
+            u'resource_type': None,
+            u'size': None,
+            u'state': u'active',
+            u'url': u'newurl',
+            u'webstore_last_updated': None,
+            u'webstore_url': None})
 
         third_dictized['tags'].insert(1, {'name': u'newnew_tag', 'state': 'active'})
         third_dictized['extras'].insert(0, {'key': 'david', 
@@ -656,15 +698,23 @@
         model.Session.commit()
 
         new_resource = {
-            'alt_url': u'empty resource group id',
+            'mimetype': None,
+            u'alt_url': u'empty resource group id',
+            'hash': u'abc123',
             'description': u'Full text. Needs escaping: " Umlaut: \xfc',
-            'size': u'123',
             'format': u'plain text',
-            'hash': u'abc123',
+            'url': u'test',
+            'cache_url': None,
+            'webstore_url': None,
+            'cache_last_updated': None,
+            'state': u'active',
+            'mimetype_inner': None,
+            'webstore_last_updated': None,
+            'last_modified': None,
             'position': 0,
-            'state': u'active',
-            'url': u'test'
-        }
+            'size': 123,
+            'resource_type': None,
+            'name': None}
 
         model.repo.new_revision()
         resource_dict_save(new_resource, context)


--- a/ckan/tests/lib/test_dictization_schema.py	Tue Sep 13 09:43:23 2011 +0100
+++ b/ckan/tests/lib/test_dictization_schema.py	Tue Sep 13 11:38:45 2011 +0100
@@ -76,13 +76,13 @@
                                                 'description': u'Full text. Needs escaping: " Umlaut: \xfc',
                                                 'format': u'plain text',
                                                 'hash': u'abc123',
-                                                'size': u'123',
+                                                'size_extra': u'123',
                                                 'url': u'http://www.annakarenina.com/download/x=1&y=2'},
                                                {'alt_url': u'alt345',
                                                 'description': u'Index of the novel',
                                                 'format': u'json',
                                                 'hash': u'def456',
-                                                'size': u'345',
+                                                'size_extra': u'345',
                                                 'url': u'http://www.annakarenina.com/index.json'}],
                                  'tags': [{'name': u'russian'}, {'name': u'tolstoy'}],
                                  'title': u'A Novel By Tolstoy',


--- a/ckan/tests/lib/test_resource_search.py	Tue Sep 13 09:43:23 2011 +0100
+++ b/ckan/tests/lib/test_resource_search.py	Tue Sep 13 11:38:45 2011 +0100
@@ -25,14 +25,14 @@
                   'format':'Excel spreadsheet',
                   'hash':'abc-123',
                   'alt_url': 'alt1',
-                  'extras':{'size': '100'},
+                  'extras':{'size_extra': '100'},
                   },
                  {'url':self.cd,
                   'description':'This is site cd.',
                   'format':'Office spreadsheet',
                   'hash':'qwe-456',
                   'alt_url':'alt2',
-                  'extras':{'size':'200'},
+                  'extras':{'size_extra':'200'},
                   },
                  ]             
              },
@@ -122,7 +122,7 @@
         assert isinstance(res_dict, dict)
         res_keys = set(res_dict.keys())
         expected_res_keys = set(model.Resource.get_columns())
-        expected_res_keys.update(['id', 'resource_group_id', 'package_id', 'position', 'size'])
+        expected_res_keys.update(['id', 'resource_group_id', 'package_id', 'position', 'size_extra'])
         assert_equal(res_keys, expected_res_keys)
         pkg1 = model.Package.by_name(u'pkg1')
         ab = pkg1.resources[0]
@@ -183,7 +183,7 @@
 
         # Document that resource extras not in ckan.extra_resource_fields
         # can't be searched
-        fields = {'size':'100'}
+        fields = {'size_extra':'100'}
         assert_raises(SearchError, self.backend.query_for(model.Resource).run, fields=fields)

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