Repository: OCHA-DAP/hdx-ckan

Issue:
On the 'Submit Data' page there are fields on Author and Maintainer. Those seem confusing.
_CJ edited the issue to add the agreed actions from below.  Luis' original comments follow._

Checklist:
- Remove `author` and `author email`.  
- Make `Maintainer` editible only to ckan user names (similar to the "add user to org" dialog) and defaulting to the user creating the dataset.  
- Make `Maintainer Email` default to the email of the user creating the dataset, but can be edited.
- Add pop up help for `Maintainer` saying _The maintainer is the user who is responsible for maintaining the sharing of these data on the HDX repository.  This user can be changed later._
- Add pop up help for `Maintainer Email` saying _This field will be publicly visible if the dataset's visibility is set to "public".  It is not a required field._

_Luis' original issue:_
On the 'Submit Data' page there are fields indicating to put information on the "author" and "maintainer" of that dataset. Considering that an user has to select an organization before adding a new dataset, authorship seems unnecessary. (Also, the help text suggests this field is a person not an organization which makes this more confusing.). 

Finally, the "maintainer" field makes sense, but I wonder if this is necessary. 

![screen shot 2014-09-18 at 12 53 59 pm](https://cloud.githubusercontent.com/assets/953118/4323867/d40b74a0-3f54-11e4-961c-83a3f4e9a714.png)

This is more of a _discussion_ than an issue.



Discussion:
Definitely worth a discussion as these are probably confusing to contributors.  Here's how I've been interpreting them:
1. The **Org** defines who has overall responsibility for sharing the dataset, but says nothing about **Source** or authorship
2. **Author** is basically a refinement of **Source**.  Who within the source organization is the author of this data.
3. **Maintainer** is basically a refinement of **Org**.  Who within the ckan org that owns the ckan dataset is responsible for this dataset. 

At the very least, we may need some help text to clarify these.

My preference would be to eliminate _author_ in the interest of making the dataset creation page shorter.  I think it's unlikely to be used, but to keep _maintainer_ and have it default to the user who is creating the dataset (but be editable). 

I think asking people to enter email addresses is probably uncomfortable for users.  If we resolve epic #1516, we could get rid of the email fields for maintainer.  

Tagging @ochadataproject .

Agree 

@ochadataproject 

To clarify the near term actions then: 
- Remove `author` and `author email`.  
- Make `Maintainer` editible only to ckan user names (similar to the "add user to org" dialog) and defaulting to the user creating the dataset.  
- Make `Maintainer Email` default to the email of the user creating the dataset, but can be edited.
- Add pop up help for `Maintainer` saying _The maintainer is the user who is responsible for maintaining the sharing of these data on the HDX repository.  This user can be changed later._
- Add pop up help for `Maintainer Email` saying _This field will be publicly visible if the dataset's visibility is set to "public".  It is not a required field._

yes agree thanks


Resolving patch:
diff --git a/ckanext-hdx_package/ckanext/hdx_package/controllers/dataset_controller.py b/ckanext-hdx_package/ckanext/hdx_package/controllers/dataset_controller.py
--- a/ckanext-hdx_package/ckanext/hdx_package/controllers/dataset_controller.py
+++ b/ckanext-hdx_package/ckanext/hdx_package/controllers/dataset_controller.py
@@ -549,6 +549,14 @@ def read(self, id, format='html'):
 
         # can the resources be previewed?
         for resource in c.pkg_dict['resources']:
+            domain = config.get('ckan.site_url', '')
+            if domain and domain in resource.get('url', ''):
+                perma_link = h.url_for(
+                    'perma_storage_file', id=id, resource_id=resource['id'])
+                resource['perma_link'] = perma_link
+            else:
+                resource['perma_link'] = resource.get('url', '')
+
             resource['can_be_previewed'] = self._resource_preview(
                 {'resource': resource, 'package': c.pkg_dict})
         print c.pkg.related
@@ -576,7 +584,7 @@ def read(self, id, format='html'):
             'hdx_get_activity_list')(context, act_data_dict)
         c.related_count = c.pkg.related_count
 
-        #count the number of resource downloads
+        # count the number of resource downloads
         c.downloads_count = 0
         for resource in c.pkg_dict['resources']:
             if resource['tracking_summary']:
@@ -623,14 +631,15 @@ def shorten(self):
         import requests
         params = request.params.items()
         url = params[0][1]
-        r = requests.post("https://www.googleapis.com/urlshortener/v1/url", data=json.dumps({'longUrl':url}), headers={'content-type':'application/json'})
+        r = requests.post("https://www.googleapis.com/urlshortener/v1/url",
+                          data=json.dumps({'longUrl': url}), headers={'content-type': 'application/json'})
         item = r.json()
 
         try:
             short = item['id']
         except:
             short = url
-        return self._finish(200, {'url':short}, content_type='json')
+        return self._finish(200, {'url': short}, content_type='json')
 
 
 # copy from package.py:1094
diff --git a/ckanext-hdx_package/ckanext/hdx_package/controllers/storage_controller.py b/ckanext-hdx_package/ckanext/hdx_package/controllers/storage_controller.py
--- a/ckanext-hdx_package/ckanext/hdx_package/controllers/storage_controller.py
+++ b/ckanext-hdx_package/ckanext/hdx_package/controllers/storage_controller.py
@@ -61,24 +61,11 @@ def ofs(self):
             FileDownloadController._ofs_impl = get_ofs()
         return FileDownloadController._ofs_impl
 
-    def file(self, label):
-        from sqlalchemy.engine import create_engine
-        # from label find resource id
-        url = config.get('ckan.site_url', '') + \
-            '/storage/f/' + urllib.quote(label)
-        engine = create_engine(config.get('sqlalchemy.url', ''), echo=True)
-        connection = engine.connect()
-        query = connection.execute(
-            """SELECT * from resource where url= %s""", (url,))
-        res = query.fetchone()
-        if not res:
-            #             raise logic.NotFound
-            r = generate_response(404, u'File not found')
-            return r
-
+    def _download_file(self, res, label):
         # We need this as a resource object to check access so create a dummy
         # obj and trick CKAN
         resource = model.Resource()
+
         for k in res.keys():
             setattr(resource, k, res[k])
 
@@ -118,11 +105,56 @@ def file(self, label):
                 'Pragma': 'no-cache',
                 'Cache-Control': 'no-cache',
                 'Content-Type': metadata.get('_format', 'text/plain')}
+            if resource.name:
+                headers[
+                    'Content-Disposition'] = 'inline; filename={}'.format(resource.name)
             fapp = FileApp(filepath, headers=None, **headers)
             return fapp(request.environ, self.start_response)
         else:
             h.redirect_to(file_url.encode('ascii', 'ignore'))
 
+    def file(self, label):
+        from sqlalchemy.engine import create_engine
+        # from label find resource id
+        url = config.get('ckan.site_url', '') + \
+            '/storage/f/' + urllib.quote(label)
+        engine = create_engine(config.get('sqlalchemy.url', ''), echo=True)
+        connection = engine.connect()
+        query = connection.execute(
+            """SELECT * from resource where url= %s""", (url,))
+        res = query.fetchone()
+        if not res:
+            #             raise logic.NotFound
+            r = generate_response(404, u'File not found')
+            return r
+
+        return self._download_file(label)
+
+    def perma_file(self, id, resource_id):
+        from sqlalchemy.engine import create_engine
+        # from label find resource id
+        engine = create_engine(config.get('sqlalchemy.url', ''), echo=True)
+        connection = engine.connect()
+        query = connection.execute(
+            """SELECT * from resource where id= %s""", (resource_id,))
+        res = query.fetchone()
+        if not res:
+            #             raise logic.NotFound
+            r = generate_response(404, u'File not found')
+            return r
+        else:
+            label = self._get_label_from_resource(res)
+
+        return self._download_file(res, label)
+
+    def _get_label_from_resource(self, resource):
+        prefix = '/storage/f/'
+        url = resource.url
+        index = url.find(prefix)
+        label = url[index + len(prefix):]
+        uq_label = urllib.unquote(label)
+        return uq_label
+
 
 def create_pairtree_marker(folder):
     """ Creates the pairtree marker for tests if it doesn't exist """
diff --git a/ckanext-hdx_package/ckanext/hdx_package/plugin.py b/ckanext-hdx_package/ckanext/hdx_package/plugin.py
--- a/ckanext-hdx_package/ckanext/hdx_package/plugin.py
+++ b/ckanext-hdx_package/ckanext/hdx_package/plugin.py
@@ -60,6 +60,8 @@ def update_config(self, config):
     def before_map(self, map):
         map.connect('storage_file', '/storage/f/{label:.*}', controller='ckanext.hdx_package.controllers.storage_controller:FileDownloadController',
                     action='file')
+        map.connect('perma_storage_file', '/dataset/{id}/resource_download/{resource_id}', controller='ckanext.hdx_package.controllers.storage_controller:FileDownloadController',
+                    action='perma_file')
         map.connect('dataset_preselect', '/dataset/preselect',
                     controller='ckanext.hdx_package.controllers.dataset_controller:DatasetController', action='preselect')
         map.connect('resource_edit', '/dataset/{id}/resource_edit/{resource_id}',
@@ -109,7 +111,7 @@ def _modify_package_schema(self, schema):
             'methodology': [tk.get_validator('ignore_missing'),
                             tk.get_converter('convert_to_extras')],
             'methodology_other': [tk.get_validator('ignore_missing'),
-                              tk.get_converter('convert_to_extras')],
+                                  tk.get_converter('convert_to_extras')],
             'license_other': [tk.get_validator('ignore_missing'),
                               tk.get_converter('convert_to_extras')],
         })
@@ -144,7 +146,7 @@ def show_package_schema(self):
             'methodology': [tk.get_converter('convert_from_extras'),
                             tk.get_validator('ignore_missing')],
             'methodology_other': [tk.get_converter('convert_from_extras'),
-                              tk.get_validator('ignore_missing')],
+                                  tk.get_validator('ignore_missing')],
             'license_other': [tk.get_converter('convert_from_extras'),
                               tk.get_validator('ignore_missing')],
         })


Test patch: