Skip to content

Commit

Permalink
Merge branch 'feature-2445-add-related-new' into feature-2375-demo-theme
Browse files Browse the repository at this point in the history
  • Loading branch information
aron committed Jun 5, 2012
2 parents 9a8d9f4 + 1beea24 commit c853d24
Show file tree
Hide file tree
Showing 7 changed files with 290 additions and 6 deletions.
4 changes: 3 additions & 1 deletion ckan/config/routing.py
Expand Up @@ -158,8 +158,10 @@ def make_map():
##map.connect('/package/edit/{id}', controller='package_formalchemy', action='edit')

with SubMapper(map, controller='related') as m:
m.connect('related_new', '/dataset/{id}/related/new', action='new')
m.connect('related_edit', '/dataset/{id}/related/edit/{related_id}',
action='edit')
m.connect('related_list', '/dataset/{id}/related', action='list')
m.connect('related_read', '/dataset/{id}/related/{related_id}', action='read')

with SubMapper(map, controller='package') as m:
m.connect('/dataset', action='search')
Expand Down
95 changes: 93 additions & 2 deletions ckan/controllers/related.py
@@ -1,14 +1,23 @@


import ckan.model as model
import ckan.logic as logic
import ckan.lib.base as base
import ckan.lib.helpers as h
import ckan.lib.navl.dictization_functions as df

import pylons.i18n as i18n

c = base.c
_ = i18n._

class RelatedController(base.BaseController):


def new(self,id):
return self._edit_or_new(id, None, False)

def edit(self,id,related_id):
return self._edit_or_new(id, related_id, True)

def list(self, id):

context = {'model': model, 'session': model.Session,
Expand Down Expand Up @@ -44,3 +53,85 @@ def list(self, id):

return base.render( "package/related_list.html")


def _edit_or_new(self, id, related_id, is_edit):
"""
Edit and New were too similar and so I've put the code together
and try and do as much up front as possible.
"""
context = {'model': model, 'session': model.Session,
'user': c.user or c.author, 'for_view': True}
data_dict = {}

if is_edit:
tpl = 'related/edit.html'
auth_name = 'related_update'
auth_dict = {'id': related_id}
action_name = 'related_update'

try:
related = logic.get_action('related_show')(context, {'id': related_id})
except logic.NotFound:
base.abort(404, _('Related item not found'))
else:
tpl = 'related/new.html'
auth_name = 'related_create'
auth_dict = {}
action_name = 'related_create'

try:
logic.check_access(auth_name, context, auth_dict)
except logic.NotAuthorized:
base.abort(401, base._('Not authorized'))

try:
package = logic.get_action('package_show')(context, {'id': id})
except logic.NotFound:
base.abort(404, _('Package not found'))

data, errors, error_summary = {}, {}, {}

if base.request.method == "POST":
try:
data = logic.clean_dict(
df.unflatten(
logic.tuplize_dict(
logic.parse_params(base.request.params)
)))

if is_edit:
data['id'] = related_id
else:
data['dataset_id'] = id
data['owner_id'] = c.userobj.id

related = logic.get_action(action_name)(context, data)

if not is_edit:
h.flash_success(_("Related item was successfully created"))
else:
h.flash_success(_("Related item was successfully updated"))

h.redirect_to(controller='related',
action='list',
id=package['name'])
except df.DataError:
base.abort(400, _(u'Integrity Error'))
except logic.ValidationError, e:
errors = e.error_dict
error_summary = e.error_summary
else:
if is_edit:
data = related

c.types = (
("Application", "application"),
("Idea", "idea"),
("News Article", "news_article"),
("Paper", "paper"),
("Visualization", "visualization")
)

vars = {'data': data, 'errors': errors, 'error_summary': error_summary}
c.form = base.render("related/edit_form.html", extra_vars=vars)
return base.render(tpl)
2 changes: 1 addition & 1 deletion ckan/logic/schema.py
Expand Up @@ -243,7 +243,7 @@ def default_related_schema():
'description': [ignore_missing, unicode],
'type': [not_empty, unicode],
'image_url': [ignore_missing, unicode],
'url': [ignore_missing, unicode],
'url': [not_empty, unicode],
'owner_id': [not_empty, unicode],
'created': [ignore],
}
Expand Down
39 changes: 39 additions & 0 deletions ckan/templates/related/edit.html
@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:i18n="http://genshi.edgewall.org/i18n"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<!--! Include matchers nearest first -->
<xi:include href="../page.html" />

<!--! Now include the current page -->
<head>
<title>Edit a related item</title>
</head>
<body>
<div class="toolbar">
${h.snippet('package/new_breadcrumb.html')}
</div>

<div class="primary">
<section class="module">
<div class="content">
${c.form}
</div>
</section>
</div>

<div class="secondary">
<section class="module">
<h2 class="heading"><i class="ckan-icon ckan-icon-info"></i> What are related items?</h2>
<div class="content">
<p>Related Media is any app, article, visualisation or idea related to this dataset. For example, it could be a custom visualisation, pictograph or bar chart, an app using all or part of the data or even a news story that references this dataset.</p>
</div>
</section>
</div>

</body>
</html>


63 changes: 63 additions & 0 deletions ckan/templates/related/edit_form.html
@@ -0,0 +1,63 @@
<form
xmlns:i18n="http://genshi.edgewall.org/i18n"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude"
class="dataset-form form-horizontal" method="post">
<div class="alert alert-error error-explanation" py:if="error_summary">
<h2>Errors in form</h2>
<p>The form contains invalid entries:</p>
<ul>
<li py:for="key, error in error_summary.items()">${"%s: %s" % (key if not key=='Name' else 'URL', error)}</li>
</ul>
</div>

<div class="control-group control-large control-full" py:with="error = errors.get('title', '')">
<label class="control-label" for="field-title">Title</label>
<div class="controls">
<input id="field-title" name="title" value="${data.get('title', '')}" placeholder="${_('A descriptive title')}" />
<span class="error-block" py:if="error">${error}</span>
</div>
</div>

<div class="control-group" py:with="error = errors.get('type', '')">
<label class="control-label" for="field-license">Type</label>
<div class="controls">
<select id="field-type" name="type">
<py:for each="name, val in c.types">
<option value="${val}" py:attrs="{'selected': 'selected' if data.get('type', '') == val else None}">${name}</option>
</py:for>
</select>
<span class="error-block" py:if="error">${error}</span>
</div>
</div>

<div class="control-group control-full" py:with="error = errors.get('notes', '')">
<label class="control-label" for="field-notes">Description</label>
<div class="controls editor">
<textarea id="field-notes" name="description" cols="20" rows="5" placeholder="Some useful description about the item">${data.get('description', '')}</textarea>
<span class="info-block">You can use <a href="http://daringfireball.net/projects/markdown/syntax">Markdown</a> editing here</span>
<span class="error-block" py:if="error">${error}</span>
</div>
</div>

<div class="control-group control-large control-full" py:with="error = errors.get('url', '')">
<label class="control-label" for="field-title">URL</label>
<div class="controls">
<input id="field-url" type="url" name="url" value="${data.get('url', '')}" placeholder="${_('A URL for the related item')}" />
<span class="error-block" py:if="error">${error}</span>
</div>
</div>

<div class="control-group control-large control-full" py:with="error = errors.get('image_url', '')">
<label class="control-label" for="field-title">Image URL</label>
<div class="controls">
<input id="field-image-url" type="image_url" name="image_url" value="${data.get('image_url', '')}" placeholder="${_('URL of an image for this item')}" />
<span class="error-block" py:if="error">${error}</span>
</div>
</div>

<div class="form-actions">
<a class="btn" href="">Cancel</a>
<button class="btn btn-primary" type="submit" name="save">Save</button>
</div>
</form>
37 changes: 37 additions & 0 deletions ckan/templates/related/new.html
@@ -0,0 +1,37 @@
<!DOCTYPE html>
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:i18n="http://genshi.edgewall.org/i18n"
xmlns:py="http://genshi.edgewall.org/"
xmlns:xi="http://www.w3.org/2001/XInclude">
<!--! Include matchers nearest first -->
<xi:include href="../page.html" />

<!--! Now include the current page -->
<head>
<title>Create a related item</title>
</head>
<body>
<div class="toolbar">
${h.snippet('package/new_breadcrumb.html')}
</div>

<div class="primary">
<section class="module">
<div class="content">
${c.form}
</div>
</section>
</div>

<div class="secondary">
<section class="module">
<h2 class="heading"><i class="ckan-icon ckan-icon-info"></i> What are related items?</h2>
<div class="content">
<p>Related Media is any app, article, visualisation or idea related to this dataset. For example, it could be a custom visualisation, pictograph or bar chart, an app using all or part of the data or even a news story that references this dataset.</p>
</div>
</section>
</div>

</body>
</html>
56 changes: 54 additions & 2 deletions ckan/tests/functional/test_related.py
Expand Up @@ -3,7 +3,59 @@
import ckan.tests as tests
import ckan.model as model
import ckan.logic as logic
import ckan.tests.functional.api.base as base
import ckan.lib.helpers as h
import ckan.tests.functional.base as base
import ckan.tests.functional.api.base as apibase


class TestRelatedUI(base.FunctionalTestCase):
@classmethod
def setup_class(self):
model.Session.remove()
tests.CreateTestData.create()

@classmethod
def teardown_class(self):
model.repo.rebuild_db()

def test_related_new(self):
offset = h.url_for(controller='related',
action='new', id='warandpeace')
res = self.app.get(offset, status=200,
extra_environ={"REMOTE_USER": "testsysadmin"})
assert 'URL' in res, "URL missing in response text"
assert 'Title' in res, "Title missing in response text"

data = {
"title": "testing_create",
"url": u"http://ckan.org/feed/",
}
res = self.app.post(offset, params=data,
status=[200,302],
extra_environ={"REMOTE_USER": "testsysadmin"})

def test_related_new_missing(self):
offset = h.url_for(controller='related',
action='new', id='non-existent dataset')
res = self.app.get(offset, status=404,
extra_environ={"REMOTE_USER": "testsysadmin"})

def test_related_new_fail(self):
offset = h.url_for(controller='related',
action='new', id='warandpeace')
res = self.app.get(offset, status=200,
extra_environ={"REMOTE_USER": "testsysadmin"})
assert 'URL' in res, "URL missing in response text"
assert 'Title' in res, "Title missing in response text"

data = {
"title": "testing_create",
}
res = self.app.post(offset, params=data,
status=[200,302],
extra_environ={"REMOTE_USER": "testsysadmin"})
assert 'error' in res, res


class TestRelated:

Expand Down Expand Up @@ -148,7 +200,7 @@ def test_related_list(self):
result = logic.get_action('related_list')(context,data_dict)
assert len(result) == len(p.related)

class TestRelatedActionAPI(base.BaseModelApiTestCase):
class TestRelatedActionAPI(apibase.BaseModelApiTestCase):

@classmethod
def setup_class(cls):
Expand Down

0 comments on commit c853d24

Please sign in to comment.