diff --git a/ckan/controllers/tag.py b/ckan/controllers/tag.py index bc45347db75..a736c17e8d0 100644 --- a/ckan/controllers/tag.py +++ b/ckan/controllers/tag.py @@ -1,5 +1,5 @@ from pylons.i18n import _ -from pylons import request, c +from pylons import request, c, config import ckan.logic as logic import ckan.model as model @@ -65,4 +65,7 @@ def read(self, id): except logic.NotFound: base.abort(404, _('Tag not found')) - return base.render('tag/read.html') + if h.asbool(config.get('ckan.legacy_templates', False)): + return base.render('tag/read.html') + else: + h.redirect_to(controller='package', action='search', tags=c.tag.get('name')) diff --git a/ckan/lib/activity_streams_session_extension.py b/ckan/lib/activity_streams_session_extension.py index 08a6e37756c..e1945657462 100644 --- a/ckan/lib/activity_streams_session_extension.py +++ b/ckan/lib/activity_streams_session_extension.py @@ -1,7 +1,11 @@ +from pylons import config from sqlalchemy.orm.session import SessionExtension +from paste.deploy.converters import asbool import logging + logger = logging.getLogger(__name__) + def activity_stream_item(obj, activity_type, revision, user_id): method = getattr(obj, "activity_stream_item", None) if callable(method): @@ -11,6 +15,7 @@ def activity_stream_item(obj, activity_type, revision, user_id): "activity_stream_item() method, it must not be a package.") return None + def activity_stream_detail(obj, activity_id, activity_type): method = getattr(obj, "activity_stream_detail", None) @@ -21,6 +26,7 @@ def activity_stream_detail(obj, activity_id, activity_type): "activity_stream_detail() method.") return None + class DatasetActivitySessionExtension(SessionExtension): """Session extension that emits activity stream activities for packages and related objects. @@ -36,6 +42,8 @@ class DatasetActivitySessionExtension(SessionExtension): """ def before_commit(self, session): + if not asbool(config.get('ckan.activity_streams_enabled', 'true')): + return session.flush() diff --git a/ckan/lib/app_globals.py b/ckan/lib/app_globals.py index 18ea4610e30..ecd2847b072 100644 --- a/ckan/lib/app_globals.py +++ b/ckan/lib/app_globals.py @@ -109,9 +109,15 @@ def get_config_value(key, default=''): value = model.get_system_info(key) else: value = None + config_value = config.get(key) + # sort encodeings if needed + if isinstance(config_value, str): + try: + config_value = config_value.decode('utf-8') + except UnicodeDecodeError: + config_value = config_value.decode('latin-1') # we want to store the config the first time we get here so we can # reset them if needed - config_value = config.get(key) if key not in _CONFIG_CACHE: _CONFIG_CACHE[key] = config_value if value is not None: diff --git a/ckan/lib/dictization/model_dictize.py b/ckan/lib/dictization/model_dictize.py index 70f9023f478..d61c68e73b1 100644 --- a/ckan/lib/dictization/model_dictize.py +++ b/ckan/lib/dictization/model_dictize.py @@ -399,15 +399,25 @@ def tag_dictize(tag, context): tag_dict = d.table_dictize(tag, context) query = search.PackageSearchQuery() - q = {'q': '+tags:"%s" +capacity:public' % tag.name, 'fl': 'data_dict', - 'wt': 'json', 'rows': 1000} + tag_query = u'+capacity:public ' + vocab_id = tag_dict.get('vocabulary_id') - package_dicts = [h.json.loads(result['data_dict']) for result in query.run(q)['results']] + if vocab_id: + model = context['model'] + vocab = model.Vocabulary.get(vocab_id) + tag_query += u'+vocab_{0}:"{1}"'.format(vocab.name, tag.name) + else: + tag_query += u'+tags:"{0}"'.format(tag.name) + + q = {'q': tag_query, 'fl': 'data_dict', 'wt': 'json', 'rows': 1000} + + package_dicts = [h.json.loads(result['data_dict']) + for result in query.run(q)['results']] # Add display_names to tags. At first a tag's display_name is just the # same as its name, but the display_name might get changed later (e.g. # translated into another language by the multilingual extension). - assert not tag_dict.has_key('display_name') + assert 'display_name' not in tag_dict tag_dict['display_name'] = tag_dict['name'] if context.get('for_view'): diff --git a/ckan/lib/helpers.py b/ckan/lib/helpers.py index 4c6be063221..840393fc2d8 100644 --- a/ckan/lib/helpers.py +++ b/ckan/lib/helpers.py @@ -1129,13 +1129,16 @@ def add_url_param(alternative_url=None, controller=None, action=None, return _create_url_with_params(params=params, controller=controller, action=action, extras=extras) - def remove_url_param(key, value=None, replace=None, controller=None, action=None, extras=None, alternative_url=None): - ''' Remove a key from the current parameters. A specific key/value - pair can be removed by passing a second value argument otherwise all - pairs matching the key will be removed. If replace is given then a - new param key=replace will be added. + ''' Remove one or multiple keys from the current parameters. + The first parameter can be either a string with the name of the key to + remove or a list of keys to remove. + A specific key/value pair can be removed by passing a second value + argument otherwise all pairs matching the key will be removed. If replace + is given then a new param key=replace will be added. + Note that the value and replace parameters only apply to the first key + provided (or the only one provided if key is a string). controller action & extras (dict) are used to create the base url via url_for(controller=controller, action=action, **extras) @@ -1144,14 +1147,20 @@ def remove_url_param(key, value=None, replace=None, controller=None, This can be overriden providing an alternative_url, which will be used instead. ''' + if isinstance(key, basestring): + keys = [key] + else: + keys = key + params_nopage = [(k, v) for k, v in request.params.items() if k != 'page'] params = list(params_nopage) if value: - params.remove((key, value)) + params.remove((keys[0], value)) else: - [params.remove((k, v)) for (k, v) in params[:] if k == key] + for key in keys: + [params.remove((k, v)) for (k, v) in params[:] if k == key] if replace is not None: - params.append((key, replace)) + params.append((keys[0], replace)) if alternative_url: return _url_with_params(alternative_url, params) @@ -1441,6 +1450,26 @@ def resource_preview(resource, pkg_id): resource_url=url, raw_resource_url=resource.get('url')) +def list_dict_filter(list_, search_field, output_field, value): + ''' Takes a list of dicts and returns the value of a given key if the + item has a matching value for a supplied key + + :param list_: the list to search through for matching items + :type list_: list of dicts + + :param search_field: the key to use to find matching items + :type search_field: string + + :param output_field: the key to use to output the value + :type output_field: string + + :param value: the value to search for + ''' + + for item in list_: + if item.get(search_field) == value: + return item.get(output_field, value) + return value def SI_number_span(number): ''' outputs a span with the number in SI unit eg 14700 -> 14.7k ''' @@ -1539,6 +1568,7 @@ def SI_number_span(number): 'localised_SI_number', 'localised_nice_date', 'localised_filesize', + 'list_dict_filter', # imported into ckan.lib.helpers 'literal', 'link_to', diff --git a/ckan/lib/search/index.py b/ckan/lib/search/index.py index d77d7cbfa12..9b50031df4b 100644 --- a/ckan/lib/search/index.py +++ b/ckan/lib/search/index.py @@ -157,12 +157,11 @@ def index_package(self, pkg_dict, defer_commit=False): # if there is an owner_org we want to add this to groups for index # purposes - if pkg_dict['owner_org'] and pkg_dict.get('organization'): + if pkg_dict.get('organization'): pkg_dict['organization'] = pkg_dict['organization']['name'] else: pkg_dict['organization'] = None - # tracking tracking_summary = pkg_dict.pop('tracking_summary', None) if tracking_summary: diff --git a/ckan/logic/action/create.py b/ckan/logic/action/create.py index f469333e2a8..323c5365214 100644 --- a/ckan/logic/action/create.py +++ b/ckan/logic/action/create.py @@ -1,7 +1,9 @@ import logging +from pylons import config from pylons.i18n import _ import ckan.new_authz as new_authz +import ckan.lib.helpers as h import ckan.lib.plugins as lib_plugins import ckan.logic as logic import ckan.rating as ratings @@ -422,13 +424,14 @@ def member_create(context, data_dict=None): filter(model.Member.table_name == obj_type).\ filter(model.Member.table_id == obj_id).\ filter(model.Member.group_id == group.id).\ - filter(model.Member.state == "active").first() + filter(model.Member.state == "active").first() if member: member.capacity = capacity else: member = model.Member(table_name = obj_type, table_id = obj_id, group_id = group.id, + state = 'active', capacity=capacity) model.Session.add(member) @@ -888,6 +891,9 @@ def activity_create(context, activity_dict, ignore_auth=False): :rtype: dictionary ''' + if not h.asbool(config.get('ckan.activity_streams_enabled', 'true')): + return + model = context['model'] # Any revision_id that the caller attempts to pass in the activity_dict is diff --git a/ckan/logic/action/get.py b/ckan/logic/action/get.py index c834b30cd1d..9340a7b0c9c 100644 --- a/ckan/logic/action/get.py +++ b/ckan/logic/action/get.py @@ -262,6 +262,9 @@ def member_list(context, data_dict=None): model = context['model'] group = model.Group.get(_get_or_bust(data_dict, 'id')) + if not group: + raise NotFound + obj_type = data_dict.get('object_type', None) capacity = data_dict.get('capacity', None) @@ -269,33 +272,23 @@ def member_list(context, data_dict=None): _check_access('group_show', context, data_dict) q = model.Session.query(model.Member).\ - filter(model.Member.group_id == group.id).\ - filter(model.Member.state == "active") + filter(model.Member.group_id == group.id).\ + filter(model.Member.state == "active") if obj_type: q = q.filter(model.Member.table_name == obj_type) if capacity: q = q.filter(model.Member.capacity == capacity) - lookup = {} - def type_lookup(name): - if name in lookup: - return lookup[name] - if hasattr(model, name.title()): - lookup[name] = getattr(model,name.title()) - return lookup[name] - return None - trans = new_authz.roles_trans() + def translated_capacity(capacity): try: return trans[capacity] except KeyError: return capacity - return [(m.table_id, - type_lookup(m.table_name), - translated_capacity(m.capacity),) + return [(m.table_id, m.table_name, translated_capacity(m.capacity)) for m in q.all()] def _group_or_org_list(context, data_dict, is_org=False): diff --git a/ckan/logic/validators.py b/ckan/logic/validators.py index dad232a2318..4b448168670 100644 --- a/ckan/logic/validators.py +++ b/ckan/logic/validators.py @@ -18,7 +18,7 @@ def owner_org_validator(key, data, errors, context): value = data.get(key) - if value is missing or value is None: + if value is missing or not value: if not ckan.new_authz.check_config_permission('create_unowned_dataset'): raise Invalid(_('A organization must be supplied')) data.pop(key, None) diff --git a/ckan/migration/versions/067_turn_extras_to_strings.py b/ckan/migration/versions/067_turn_extras_to_strings.py index e6c0f1a26f0..4659cbc6cb4 100644 --- a/ckan/migration/versions/067_turn_extras_to_strings.py +++ b/ckan/migration/versions/067_turn_extras_to_strings.py @@ -7,7 +7,7 @@ def upgrade(migrate_engine): revision_tables = 'package_extra_revision group_extra_revision' for table in tables.split(): - sql = """select id, value from {table} where substr(value,0,1) = '"' """.format(table=table) + sql = """select id, value from {table} where substr(value,1,1) = '"' """.format(table=table) results = connection.execute(sql) for result in results: id, value = result @@ -16,7 +16,7 @@ def upgrade(migrate_engine): json.loads(value), id) for table in revision_tables.split(): - sql = """select id, revision_id, value from {table} where substr(value,0,1) = '"' """.format(table=table) + sql = """select id, revision_id, value from {table} where substr(value,1,1) = '"' """.format(table=table) results = connection.execute(sql) for result in results: diff --git a/ckan/misc.py b/ckan/misc.py index 4820379d2e9..29f3ae6447b 100644 --- a/ckan/misc.py +++ b/ckan/misc.py @@ -35,8 +35,8 @@ class MarkdownFormat(TextFormat): normal_link = re.compile('<(http:[^>]+)>') html_whitelist = 'b center li ol p table td tr ul'.split(' ') - whitelist_elem = re.compile(r'<(\/?(%s)[^>]*)>' % "|".join(html_whitelist), re.IGNORECASE) - whitelist_escp = re.compile(r'\\xfc\\xfd(\/?(%s)[^>]*?)\\xfd\\xfc' % "|".join(html_whitelist), re.IGNORECASE) + whitelist_elem = re.compile(r'<(\/?((%s)(\s[^>]*)?))>' % "|".join(html_whitelist), re.IGNORECASE) + whitelist_escp = re.compile(r'\\xfc\\xfd(\/?((%s)(\s[^>]*?)?))\\xfd\\xfc' % "|".join(html_whitelist), re.IGNORECASE) normal_link = re.compile(r']*?href="([^"]*?)"[^>]*?>', re.IGNORECASE) abbrev_link = re.compile(r'<(http://[^>]*)>', re.IGNORECASE) any_link = re.compile(r']*?>', re.IGNORECASE) diff --git a/ckan/new_authz.py b/ckan/new_authz.py index 197d6ab35cf..f1bc0e8c5ed 100644 --- a/ckan/new_authz.py +++ b/ckan/new_authz.py @@ -49,6 +49,7 @@ def get_group_or_org_admin_ids(group_id): q = model.Session.query(model.Member) \ .filter(model.Member.group_id == group_id) \ .filter(model.Member.table_name == 'user') \ + .filter(model.Member.state == 'active') \ .filter(model.Member.capacity == 'admin') return [a.table_id for a in q.all()] @@ -135,6 +136,7 @@ def has_user_permission_for_group_or_org(group_id, user_name, permission): q = model.Session.query(model.Member) \ .filter(model.Member.group_id == group_id) \ .filter(model.Member.table_name == 'user') \ + .filter(model.Member.state == 'active') \ .filter(model.Member.table_id == user_id) # see if any role has the required permission # admin permission allows anything for the group @@ -158,6 +160,7 @@ def users_role_for_group_or_org(group_id, user_name): q = model.Session.query(model.Member) \ .filter(model.Member.group_id == group_id) \ .filter(model.Member.table_name == 'user') \ + .filter(model.Member.state == 'active') \ .filter(model.Member.table_id == user_id) # return the first role we find for row in q.all(): @@ -176,6 +179,7 @@ def has_user_permission_for_some_org(user_name, permission): # get any groups the user has with the needed role q = model.Session.query(model.Member) \ .filter(model.Member.table_name == 'user') \ + .filter(model.Member.state == 'active') \ .filter(model.Member.capacity.in_(roles)) \ .filter(model.Member.table_id == user_id) group_ids = [] diff --git a/ckan/public/base/css/main.css b/ckan/public/base/css/main.css index 0e9b5a7cd59..055f36eb1a6 100644 --- a/ckan/public/base/css/main.css +++ b/ckan/public/base/css/main.css @@ -4783,13 +4783,15 @@ a.tag:hover { box-shadow: inset 0 1px 0 #3d97b3; } .pill { - background-color: #8ca0a6; + display: inline-block; + background-color: #6f8890; color: #ffffff; - padding: 1px 5px 1px 8px; + padding: 2px 10px 1px 10px; margin-right: 5px; - -webkit-border-radius: 7px; - -moz-border-radius: 7px; - border-radius: 7px; + font-weight: normal; + -webkit-border-radius: 100px; + -moz-border-radius: 100px; + border-radius: 100px; } .pill a { color: #ffffff; @@ -4872,6 +4874,7 @@ a.tag:hover { margin: 20px 0; } .module-heading { + *zoom: 1; margin: 0; padding: 7px 25px; font-size: 14px; @@ -4880,12 +4883,25 @@ a.tag:hover { border-top: 1px solid #dddddd; border-bottom: 1px solid #dddddd; } +.module-heading:before, +.module-heading:after { + display: table; + content: ""; + line-height: 0; +} +.module-heading:after { + clear: both; +} .module-heading .action { float: right; color: #888888; - font-size: 11px; + font-size: 12px; + line-height: 20px; text-decoration: underline; } +.module-heading .action:hover { + color: #444444; +} .module-content { padding: 0 25px; margin: 20px 0; @@ -4928,11 +4944,16 @@ a.tag:hover { -webkit-border-radius: 0; -moz-border-radius: 0; border-radius: 0; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; border: 0; } .module .pagination li a { border-top: none; border-bottom: none; + padding-top: 7px; + padding-bottom: 7px; } .module .pagination li:first-child a, .module .pagination li:last-child a { @@ -5042,10 +5063,17 @@ a.tag:hover { background-color: #ffffff; border-bottom: 1px solid #dddddd; margin-top: 0; + margin-bottom: 0; -webkit-border-radius: 3px 3px 0 0; -moz-border-radius: 3px 3px 0 0; border-radius: 3px 3px 0 0; } +.module-resource .actions { + position: relative; + float: right; + top: -10px; + right: -15px; +} .module .module-tags { padding-bottom: 8px; } @@ -5251,10 +5279,6 @@ ol.media-grid:after { overflow: hidden; display: block; } -.nav-item [class^=icon] { - font-size: 1.2em; - margin: -1px 0 -1px -1px; -} .module-narrow .nav-item > a { padding-left: 15px; padding-right: 15px; @@ -5319,6 +5343,14 @@ ol.media-grid:after { -moz-border-radius: 100px; border-radius: 100px; } +.nav-simple > .nav-btn { + padding-left: 0; + padding-right: 0; + text-align: center; +} +.nav-simple > .nav-btn .btn { + display: inline-block; +} .js .js-hide { display: none; } @@ -5339,7 +5371,6 @@ label { label { cursor: pointer; font-size: 14px; - line-height: 2; } label:after { content: ":"; @@ -5348,13 +5379,6 @@ label.radio:after, label.checkbox:after { content: ""; } -input, -textarea, -select, -.uneditable-input { - font-size: 14px; - padding: 8px 10px; -} input[type=radio], input[type=checkbox] { position: relative; @@ -5367,7 +5391,6 @@ input[type=checkbox].checkbox-onown { top: 0; } select { - margin-top: 6px; padding: 4px; } textarea { @@ -5393,7 +5416,8 @@ textarea { } .control-large input, .control-large .control-label { - font-size: 18px; + font-size: 17.5px; + line-height: 30px; } .form-actions { background: none; @@ -5417,16 +5441,6 @@ textarea { } .form-horizontal .control-label { width: 120px; - line-height: 1.3; - padding-top: 9px; -} -.form-horizontal .control-large input, -.form-horizontal .control-large .control-label { - font-size: 18px; - line-height: 2; -} -.form-horizontal .control-large .control-label { - padding-top: 10px; } .form-horizontal .controls { margin-left: 130px; @@ -5695,24 +5709,23 @@ textarea { background: #c6898b; margin: -3px 0 0; color: #ffffff; - width: 216px; + width: 208px; } .control-medium .error-block { - width: 326px; + width: 318px; } .control-full .error-block { width: auto; } .control-group.error .input-prepend .error-block, .control-custom.error .error-block { - margin-left: -1px; width: auto; } .control-custom.error .error-block { - width: 409px; + width: 401px; } .control-select.error .error-block { - width: 204px; + width: 196px; } .stages { margin: 0; @@ -5890,7 +5903,7 @@ textarea { padding-left: 10px; } .select2-container { - margin-top: 5px; + margin-top: 1px; } .select2-container-multi { margin-top: 0; @@ -6015,7 +6028,6 @@ textarea { } .filter-list { color: #444444; - font-weight: bold; } .filter-list .extra { margin-top: 10px; @@ -7689,6 +7701,9 @@ textarea { .context-info.editing .module-content { margin-top: 0; } +.modal { + top: 50%; +} .hero { background: url("../../../base/images/background-tile.png"); padding: 20px 0; @@ -7738,15 +7753,16 @@ textarea { .hero .tags:after { clear: both; } +.hero .tags h3, .hero .tags .tag { display: block; float: left; margin: 5px 10px 0 0; } -.hero .tags h3.tag { +.hero .tags h3 { font-size: 14px; line-height: 20px; - margin-bottom: 0; + padding: 2px 8px; } .hero-primary, .hero-secondary { @@ -8161,6 +8177,62 @@ textarea { .lang-dropdown .select2-result-label[data-value*="/pt_BR/"]:before { background-position: 0 -132px; } +.table-selected td { + background-color: #f5f5f5; +} +.table-selected td .edit { + display: block; +} +.table-bulk-edit th input { + top: -5px; +} +.table-bulk-edit .table-actions .btn-group { + float: left; + margin: 0 10px 0 0; +} +.table-bulk-edit .context p { + margin-bottom: 0; +} +.table-header thead th { + background-color: #f6f6f6; +} +.table-edit-hover .edit { + display: none; + float: right; +} +.table-edit-hover tr:hover .edit { + display: block; +} +.js .table-toggle-more .toggle-more { + display: none; +} +.js .table-toggle-more .show-more { + display: inline; +} +.js .table-toggle-more .show-less { + display: none; +} +.js .table-toggle-more .toggle-seperator { + display: table-row; +} +.js .table-toggle-more .toggle-seperator td { + height: 11px; + padding: 0; + background-image: url("../../../base/images/table-seperator.png"); +} +.js .table .toggle-show td { + background: none; + text-align: center; +} +.js .table-toggle-less .show-less { + display: inline; +} +.js .table-toggle-less .show-more { + display: none; +} +.js .table-toggle-less .toggle-seperator { + display: none; +} .profile .empty, .profile .dataset-list { margin-bottom: 20px; diff --git a/ckan/public/base/less/forms.less b/ckan/public/base/less/forms.less index f0843c1d317..b38cbc406cf 100644 --- a/ckan/public/base/less/forms.less +++ b/ckan/public/base/less/forms.less @@ -372,11 +372,11 @@ textarea { background: @errorBorder; margin: -@inputBorderRadius 0 0; color: @inputBackground; - width: 216px; + width: 208px; } .control-medium .error-block { - width: 326px; + width: 318px; } .control-full .error-block { @@ -385,16 +385,15 @@ textarea { .control-group.error .input-prepend .error-block, .control-custom.error .error-block { - margin-left: -1px; width: auto; } .control-custom.error .error-block { - width: 409px; + width: 401px; } .control-select.error .error-block { - width: 204px; + width: 196px; } // Stages diff --git a/ckan/public/base/less/layout.less b/ckan/public/base/less/layout.less index 63c559bfd60..0033ccc7f82 100644 --- a/ckan/public/base/less/layout.less +++ b/ckan/public/base/less/layout.less @@ -137,3 +137,7 @@ } } } + +.modal { + top: 50%; +} diff --git a/ckan/templates/home/about.html b/ckan/templates/home/about.html index 1be3b858749..98a3c03e69b 100644 --- a/ckan/templates/home/about.html +++ b/ckan/templates/home/about.html @@ -11,7 +11,7 @@
{% block about %} {% if g.site_about %} - {{ h.markdown_extract(g.site_about) }} + {{ h.render_markdown(g.site_about) }} {% else %}

{{ _('About') }}

{% snippet 'home/snippets/about_text.html' %} diff --git a/ckan/templates/package/new_package_metadata.html b/ckan/templates/package/new_package_metadata.html index b0aa54e9687..2b92105a1e8 100644 --- a/ckan/templates/package/new_package_metadata.html +++ b/ckan/templates/package/new_package_metadata.html @@ -2,4 +2,4 @@ {% block subtitle %}{{ _('Add metadata to the dataset') }}{% endblock %} -{% block form %}{{ h.snippet('package/snippets/package_metadata_form.html', data=data, errors=errors, include_metadata=false, pkg_name=pkg_name) }}{% endblock %} +{% block form %}{{ h.snippet('package/snippets/package_metadata_form.html', data=data, errors=errors, error_summary=error_summary, include_metadata=false, pkg_name=pkg_name) }}{% endblock %} diff --git a/ckan/templates/package/resource_read.html b/ckan/templates/package/resource_read.html index 85ea76de691..4d7101634f3 100644 --- a/ckan/templates/package/resource_read.html +++ b/ckan/templates/package/resource_read.html @@ -96,10 +96,6 @@

{{ _('Additional Information') }}

{{ _('License') }} {% snippet "snippets/license.html", pkg_dict=pkg, text_only=True %} - - {{ _('Author') }} - {{ res.author or _('unknown') }} - {% for key, value in h.format_resource_items(res.items()) %} {{ key }}{{ value }} {% endfor %} diff --git a/ckan/templates/package/search.html b/ckan/templates/package/search.html index 73326b159d5..1271aebab17 100644 --- a/ckan/templates/package/search.html +++ b/ckan/templates/package/search.html @@ -38,6 +38,7 @@
{% for field in c.fields_grouped %} + {% set search_facets_items = c.search_facets.get(field)['items'] %} {{ c.facet_titles.get(field) }}: {% for value in c.fields_grouped[field] %} @@ -45,6 +46,7 @@ {{ c.translated_fields[(field,value)] }} {%- else -%} {{ value }} + {{ h.list_dict_filter(search_facets_items , 'name', 'display_name', value) }} {%- endif %} diff --git a/ckan/templates/package/snippets/package_basic_fields.html b/ckan/templates/package/snippets/package_basic_fields.html index c6bb8afaad6..2b6933f62a3 100644 --- a/ckan/templates/package/snippets/package_basic_fields.html +++ b/ckan/templates/package/snippets/package_basic_fields.html @@ -1,19 +1,28 @@ {% import 'macros/form.html' as form %} +{% block package_basic_fields_title %} {{ form.input('title', id='field-title', label=_('Title'), placeholder=_('eg. A descriptive title'), value=data.title, error=errors.title, classes=['control-full', 'control-large'], attrs={'data-module': 'slug-preview-target'}) }} +{% endblock %} +{% block package_basic_fields_url %} {% set prefix = h.url_for(controller='package', action='read', id='') %} {% set domain = h.url_for(controller='package', action='read', id='', qualified=true) %} {% set domain = domain|replace("http://", "")|replace("https://", "") %} {% set attrs = {'data-module': 'slug-preview-slug', 'data-module-prefix': domain, 'data-module-placeholder': ''} %} {{ form.prepend('name', id='field-name', label=_('URL'), prepend=prefix, placeholder=_('eg. my-dataset'), value=data.name, error=errors.name, attrs=attrs) }} +{% endblock %} +{% block package_basic_fields_description %} {{ form.markdown('notes', id='field-notes', label=_('Description'), placeholder=_('eg. Some useful notes about the data'), value=data.notes, error=errors.notes) }} +{% endblock %} +{% block package_basic_fields_tags %} {% set tag_attrs = {'data-module': 'autocomplete', 'data-module-tags': '', 'data-module-source': '/api/2/util/tag/autocomplete?incomplete=?'} %} {{ form.input('tag_string', id='field-tags', label=_('Tags'), placeholder=_('eg. economy, mental health, government'), value=data.tag_string, error=errors.tags, classes=['control-full'], attrs=tag_attrs) }} +{% endblock %} +{% block package_basic_fields_license %}
{% set error = errors.license_id %} @@ -33,7 +42,9 @@
+{% endblock %} +{% block package_basic_fields_org %} {# if we have a default group then this wants remembering #} {% if data.group_id %} @@ -46,10 +57,10 @@
@@ -57,3 +68,4 @@
{% endif %} {% endif %} +{% endblock %} diff --git a/ckan/templates/package/snippets/package_metadata_fields.html b/ckan/templates/package/snippets/package_metadata_fields.html index b9c97f6aa43..6bdfa51f140 100644 --- a/ckan/templates/package/snippets/package_metadata_fields.html +++ b/ckan/templates/package/snippets/package_metadata_fields.html @@ -2,6 +2,7 @@ {% block package_metadata_fields %} +{% block package_metadata_fields_visibility %} {% if data.owner_org %}
@@ -14,18 +15,25 @@
{% endif %} +{% endblock %} +{% block package_metadata_author %} {{ form.input('author', label=_('Author'), id='field-author', placeholder=_('Joe Bloggs'), value=data.author, error=errors.author, classes=['control-medium']) }} {{ form.input('author_email', label=_('Author Email'), id='field-author-email', placeholder=_('joe@example.com'), value=data.author_email, error=errors.author_email, classes=['control-medium']) }} +{% endblock %} +{% block package_metadata_fields_maintainer %} {{ form.input('maintainer', label=_('Maintainer'), id='field-maintainer', placeholder=_('Joe Bloggs'), value=data.maintainer, error=errors.maintainer, classes=['control-medium']) }} {{ form.input('maintainer_email', label=_('Maintainer Email'), id='field-maintainer-email', placeholder=_('joe@example.com'), value=data.maintainer_email, error=errors.maintainer_email, classes=['control-medium']) }} +{% endblock %} +{% block package_metadata_fields_custom %} {% block custom_fields %} {% snippet 'snippets/custom_form_fields.html', extras=data.extras, errors=errors, limit=3 %} {% endblock %} +{% endblock %} {% block dataset_fields %} diff --git a/ckan/templates/package/snippets/resource_form.html b/ckan/templates/package/snippets/resource_form.html index e36e6306478..8ce9d8efae1 100644 --- a/ckan/templates/package/snippets/resource_form.html +++ b/ckan/templates/package/snippets/resource_form.html @@ -18,6 +18,8 @@
{% block basic_fields %} + + {% block basic_fields_data %}
{# This block uses a slightly odd pattern. Unlike the rest of the radio @@ -33,13 +35,21 @@
+ {% endblock %} + {% block basic_fields_url %} {{ form.input('url', id='field-url', label=_('Resource'), placeholder=_('eg. http://example.com/gold-prices-jan-2011.json'), value=data.url, error=errors.url, classes=['control-full', 'control-large']) }} + {% endblock %} + {% block basic_fields_name %} {{ form.input('name', id='field-name', label=_('Name'), placeholder=_('eg. January 2011 Gold Prices'), value=data.name, error=errors.name, classes=['control-full']) }} + {% endblock %} + {% block basic_fields_description %} {{ form.markdown('description', id='field-description', label=_('Description'), placeholder=_('Some useful notes about the data'), value=data.description, error=errors.description) }} + {% endblock %} + {% block basic_fields_format %} {% set format_attrs = {'data-module': 'autocomplete', 'data-module-source': '/api/2/util/resource/format_autocomplete?incomplete=?'} %} {% call form.input('format', id='field-format', label=_('Format'), placeholder=_('eg. CSV, XML or JSON'), value=data.format, error=errors.format, classes=['control-medium'], attrs=format_attrs) %} @@ -47,6 +57,7 @@ {{ _('This is generated automatically. You can edit if you wish') }} {% endcall %} + {% endblock %} {% endblock %} {% block metadata_fields %} diff --git a/ckan/templates/snippets/license.html b/ckan/templates/snippets/license.html index 7ed6cfa0e3e..d575ef0515b 100644 --- a/ckan/templates/snippets/license.html +++ b/ckan/templates/snippets/license.html @@ -14,8 +14,6 @@

{{ _('License') [Open Data] - {% else %} - {{ h.icon('lock') }} {% endif %}

diff --git a/ckan/templates/tag/index.html b/ckan/templates/tag/index.html new file mode 100644 index 00000000000..892345cec93 --- /dev/null +++ b/ckan/templates/tag/index.html @@ -0,0 +1,33 @@ +{% extends "page.html" %} + +{% block subtitle %}{{ _('Tags') }}{% endblock %} + +{% block breadcrumb_content %} +
  • {{ _('Tags') }}
  • +{% endblock %} + +{% block primary_content %} +
    +
    +

    {{ _('Tags') }}

    +
      + {% for tag in c.page.items %} +
    • {{ h.link_to(tag.display_name, h.url_for(controller='package', action='search', tags=tag.name), class_='tag') }}
    • + {% endfor %} +
    +
    + {{ c.page.pager(q=c.q) }} +
    +{% endblock %} + +{% block secondary_content %} +
    +
    +
    + + + +
    +
    +
    +{% endblock %} diff --git a/ckan/tests/functional/api/model/test_vocabulary.py b/ckan/tests/functional/api/model/test_vocabulary.py index 0b23e26f8c9..0ae424ce8ef 100644 --- a/ckan/tests/functional/api/model/test_vocabulary.py +++ b/ckan/tests/functional/api/model/test_vocabulary.py @@ -826,6 +826,28 @@ def test_add_vocab_tag_to_dataset(self): tag_in_pkg['vocabulary_id'] == tag['vocabulary_id']] assert len(tags_in_pkg) == 1 + # Test that the package appears vocabulary_list. + vocabs = self._post('/api/action/vocabulary_list')['result'] + genre_vocab = [vocab for vocab in vocabs if vocab['name'] == 'Genre'] + assert len(genre_vocab) == 1 + genre_vocab = genre_vocab[0] + noise_tag = [tag_ for tag_ in genre_vocab['tags'] + if tag_['name'] == 'noise'] + assert len(noise_tag) == 1 + noise_tag = noise_tag[0] + assert len([p for p in noise_tag['packages'] if + p['id'] == updated_package['id']]) == 1 + + # Test that the tagged package appears in vocabulary_show. + genre_vocab = self._post('/api/action/vocabulary_show', + params={'id': genre_vocab['id']})['result'] + noise_tag = [tag_ for tag_ in genre_vocab['tags'] + if tag_['name'] == 'noise'] + assert len(noise_tag) == 1 + noise_tag = noise_tag[0] + assert len([p for p in noise_tag['packages'] if + p['id'] == updated_package['id']]) == 1 + # Remove the new vocab tag from the package. package['tags'].remove(tag) updated_package = (self._post('/api/action/package_update', diff --git a/ckan/tests/functional/api/test_activity.py b/ckan/tests/functional/api/test_activity.py index 1c7a9c73709..461ae7685c8 100644 --- a/ckan/tests/functional/api/test_activity.py +++ b/ckan/tests/functional/api/test_activity.py @@ -13,8 +13,11 @@ logger = logging.getLogger(__name__) import pylons.test +from pylons import config +from paste.deploy.converters import asbool import paste.fixture from ckan.lib.helpers import json +from nose import SkipTest ##def package_update(context, data_dict): @@ -170,9 +173,10 @@ def find_new_activities(before, after): class TestActivity: - @classmethod def setup_class(self): + if not asbool(config.get('ckan.activity_streams_enabled', 'true')): + raise SkipTest('Activity streams not enabled') import ckan import ckan.model as model ckan.tests.CreateTestData.create() diff --git a/ckan/tests/functional/test_activity.py b/ckan/tests/functional/test_activity.py index 02d56252871..fa4b80423c0 100644 --- a/ckan/tests/functional/test_activity.py +++ b/ckan/tests/functional/test_activity.py @@ -1,7 +1,11 @@ +from pylons import config from pylons.test import pylonsapp +from paste.deploy.converters import asbool import paste.fixture -import ckan from routes import url_for +from nose import SkipTest + +import ckan from ckan.logic.action.create import package_create, user_create, group_create from ckan.logic.action.create import follow_dataset, follow_user from ckan.logic.action.update import package_update, resource_update @@ -18,6 +22,8 @@ class TestActivity(HtmlCheckMethods): """ @classmethod def setup(cls): + if not asbool(config.get('ckan.activity_streams_enabled', 'true')): + raise SkipTest('Activity streams not enabled') ckan.tests.CreateTestData.create() cls.sysadmin_user = ckan.model.User.get('testsysadmin') cls.app = paste.fixture.TestApp(pylonsapp) diff --git a/ckan/tests/logic/test_auth.py b/ckan/tests/logic/test_auth.py index b2e81ab44a8..3ea9f2db39f 100644 --- a/ckan/tests/logic/test_auth.py +++ b/ckan/tests/logic/test_auth.py @@ -3,7 +3,6 @@ import ckan.model as model import ckan.new_authz as new_authz import json -from ckan.tests import StatusCodes INITIAL_TEST_CONFIG_PERMISSIONS = { 'anon_create_dataset': False, @@ -16,6 +15,7 @@ 'create_unowned_dataset': False, } + class TestAuth(tests.WsgiAppCase): @classmethod def setup_class(cls): @@ -34,7 +34,7 @@ def teardown_class(cls): model.repo.rebuild_db() def _call_api(self, action, data, user, status=None): - params='%s=1' % json.dumps(data) + params = '%s=1' % json.dumps(data) return self.app.post('/api/action/%s' % action, params=params, extra_environ={'Authorization': self.apikeys[user]}, @@ -65,16 +65,16 @@ def test_01_create_users(self): self._call_api('user_create', user, 'no_org', 403) def test_02_create_orgs(self): - org = {'name': 'org_no_user',} + org = {'name': 'org_no_user'} self._call_api('organization_create', org, 'random_key', 403) self._call_api('organization_create', org, 'sysadmin') - org = {'name': 'org_with_user',} + org = {'name': 'org_with_user'} self._call_api('organization_create', org, 'random_key', 403) self._call_api('organization_create', org, 'sysadmin') #no user should be able to create org - org = {'name': 'org_should_not_be_created',} + org = {'name': 'org_should_not_be_created'} self._call_api('organization_create', org, 'org_admin', 403) def test_03_create_dataset_no_org(self): @@ -87,13 +87,16 @@ def test_03_create_dataset_no_org(self): def test_04_create_dataset_with_org(self): - dataset = {'name': 'admin_create_with_user', 'owner_org': 'org_with_user'} + dataset = {'name': 'admin_create_with_user', + 'owner_org': 'org_with_user'} self._call_api('package_create', dataset, 'sysadmin', 200) - dataset = {'name': 'sysadmin_create_no_user', 'owner_org': 'org_no_user'} + dataset = {'name': 'sysadmin_create_no_user', + 'owner_org': 'org_no_user'} self._call_api('package_create', dataset, 'sysadmin', 200) - dataset = {'name': 'user_create_with_org', 'owner_org': 'org_with_user'} + dataset = {'name': 'user_create_with_org', + 'owner_org': 'org_with_user'} self._call_api('package_create', dataset, 'no_org', 403) def test_05_add_users_to_org(self): @@ -127,11 +130,11 @@ def _add_datasets(self, user): self._call_api('package_create', dataset, user, 409) #admin not able to make dataset not owned by a org - dataset = {'name': user + '_dataset_bad' } + dataset = {'name': user + '_dataset_bad'} self._call_api('package_create', dataset, user, 409) #not able to add org to not existant org - dataset = {'name': user + '_dataset_bad', 'owner_org': 'org_not_exist' } + dataset = {'name': user + '_dataset_bad', 'owner_org': 'org_not_exist'} self._call_api('package_create', dataset, user, 409) def test_07_add_datasets(self): @@ -149,7 +152,7 @@ def _update_datasets(self, user): dataset = {'id': 'sysadmin_create_no_user', 'title': 'test'} self._call_api('package_update', dataset, user, 403) #non existant owner org - dataset = {'id': 'org_editor_dataset', 'owner_org': 'org_not_exist' } + dataset = {'id': 'org_editor_dataset', 'owner_org': 'org_not_exist'} self._call_api('package_update', dataset, user, 409) def test_08_update_datasets(self): @@ -191,17 +194,15 @@ def test_11_delete_org(self): class TestAuthGroups(TestAuth): def test_01_create_groups(self): - group = {'name': 'group_no_user',} + group = {'name': 'group_no_user'} self._call_api('group_create', group, 'random_key', 403) self._call_api('group_create', group, 'sysadmin') - group = {'name': 'group_with_user',} + group = {'name': 'group_with_user'} self._call_api('group_create', group, 'random_key', 403) self._call_api('group_create', group, 'sysadmin') - def test_02_add_users_to_group(self): - self.create_user('org_admin') self.create_user('org_editor') self.create_user('org_editor_wannabe') @@ -232,20 +233,28 @@ def test_03_add_dataset_to_group(self): package = {'name': 'package_added_by_editor', 'owner_org': 'org'} self._call_api('package_create', package, 'sysadmin') - group = {'id': 'group_with_user', 'packages': [{'id': 'package_added_by_admin'}]} + res = self._call_api('group_show', + {'id': 'group_with_user'}, + 'org_admin') + group = json.loads(res.body)['result'] self._call_api('group_update', group, 'no_group', 403) self._call_api('group_update', group, 'org_admin') group = {'id': 'group_with_user', - 'packages': [{'id': 'package_added_by_admin'}, {'id' :'package_added_by_editor'}]} + 'packages': [{'id': 'package_added_by_admin'}, + {'id': 'package_added_by_editor'}]} # org editor doesn't have edit rights self._call_api('group_update', group, 'org_editor', 403) def test_04_modify_group(self): - - group = {'id': 'group_with_user', 'title': 'moo', - 'packages': [{'id': 'package_added_by_admin'}]} - + res = self._call_api('group_show', + {'id': 'group_with_user'}, + 'org_admin') + group = json.loads(res.body)['result'] + group.update({ + 'title': 'moo', + 'packages': [{'id': 'package_added_by_admin'}] + }) self._call_api('group_update', group, 'org_admin') # need to think about this as is horrible may just let editor edit @@ -253,14 +262,9 @@ def test_04_modify_group(self): self._call_api('group_update', group, 'org_editor', 403) def test_05_delete_group(self): - org = {'id': 'group_with_user'} self._call_api('group_delete', org, 'org_editor', 403) self._call_api('group_delete', org, 'org_admin', 403) org = {'id': 'group_with_user'} self._call_api('group_delete', org, 'org_editor', 403) self._call_api('group_delete', org, 'org_admin', 403) - - - - diff --git a/ckan/tests/logic/test_member.py b/ckan/tests/logic/test_member.py index 3d6294e2913..38d1d795ac1 100644 --- a/ckan/tests/logic/test_member.py +++ b/ckan/tests/logic/test_member.py @@ -1,6 +1,8 @@ -from ckan import model -from ckan.logic import get_action -from ckan.lib.create_test_data import CreateTestData +from nose.tools import assert_raises +import ckan.model as model +import ckan.logic as logic +import ckan.lib.create_test_data as create_test_data + class TestMemberLogic(object): @@ -10,60 +12,62 @@ def setup_class(cls): cls.groupname = 'david' model.repo.new_revision() - CreateTestData.create() - cls.pkgs = [ - model.Package.by_name('warandpeace'), - model.Package.by_name('annakarenina'), - ] + create_test_data.CreateTestData.create() + cls.pkgs = [model.Package.by_name('warandpeace'), + model.Package.by_name('annakarenina')] @classmethod def teardown_class(cls): model.repo.rebuild_db() - def _build_context( self, obj, obj_type, capacity='public'): - ctx = { 'model': model, - 'session': model.Session, - 'user':self.username - } - dd = { - 'id': self.groupname, - 'object': obj, - 'object_type': obj_type, - 'capacity': capacity } + def _build_context(self, obj, obj_type, capacity='public'): + ctx = {'model': model, + 'session': model.Session, + 'user': self.username} + dd = {'id': self.groupname, + 'object': obj, + 'object_type': obj_type, + 'capacity': capacity} return ctx, dd - def _add_member( self, obj, obj_type, capacity): - ctx, dd = self._build_context(obj,obj_type,capacity) - return get_action('member_create')(ctx,dd) + def _add_member(self, obj, obj_type, capacity): + ctx, dd = self._build_context(obj, obj_type, capacity) + return logic.get_action('member_create')(ctx, dd) def test_member_add(self): - res = self._add_member( self.pkgs[0].id, 'package', 'public') + res = self._add_member(self.pkgs[0].id, 'package', 'public') assert 'capacity' in res and res['capacity'] == u'public' def test_member_list(self): - _ = self._add_member( self.pkgs[0].id, 'package', 'public') - _ = self._add_member( self.pkgs[1].id, 'package', 'public') - ctx, dd = self._build_context('','package') - res = get_action('member_list')(ctx,dd) + self._add_member(self.pkgs[0].id, 'package', 'public') + self._add_member(self.pkgs[1].id, 'package', 'public') + ctx, dd = self._build_context('', 'package') + res = logic.get_action('member_list')(ctx, dd) assert len(res) == 2, res + assert (self.pkgs[0].id, 'package', 'public') in res + assert (self.pkgs[1].id, 'package', 'public') in res - ctx, dd = self._build_context('','user', 'admin') - res = get_action('member_list')(ctx,dd) + ctx, dd = self._build_context('', 'user', 'admin') + res = logic.get_action('member_list')(ctx, dd) assert len(res) == 0, res - _ = self._add_member( self.username, 'user', 'admin') - ctx, dd = self._build_context('','user', 'admin') - res = get_action('member_list')(ctx,dd) - assert len(res) == 1, res + ctx, dd = self._build_context('', 'user', 'admin') + dd['id'] = u'foo' + assert_raises(logic.NotFound, logic.get_action('member_list'), ctx, dd) + self._add_member(self.username, 'user', 'admin') + ctx, dd = self._build_context('', 'user', 'admin') + res = logic.get_action('member_list')(ctx, dd) + assert len(res) == 1, res + assert (self.username, 'user', 'Admin') in res def test_member_delete(self): - _ = self._add_member( self.username, 'user', 'admin') - ctx, dd = self._build_context(self.username,'user', 'admin') - res = get_action('member_list')(ctx,dd) + self._add_member(self.username, 'user', 'admin') + ctx, dd = self._build_context(self.username, 'user', 'admin') + res = logic.get_action('member_list')(ctx, dd) assert len(res) == 1, res - get_action('member_delete')(ctx,dd) + logic.get_action('member_delete')(ctx, dd) - res = get_action('member_list')(ctx,dd) + res = logic.get_action('member_list')(ctx, dd) assert len(res) == 0, res diff --git a/ckanext/datastore/bin/set_permissions.sql b/ckanext/datastore/bin/set_permissions.sql index 1bbc06d203f..18b6265b63a 100644 --- a/ckanext/datastore/bin/set_permissions.sql +++ b/ckanext/datastore/bin/set_permissions.sql @@ -14,9 +14,9 @@ To run the script, execute: */ -- name of the main CKAN database -\set maindb '{ckandb}' +\set maindb "{ckandb}" -- the name of the datastore database -\set datastoredb '{datastoredb}' +\set datastoredb "{datastoredb}" -- username of the ckan postgres user \set ckanuser "{ckanuser}" -- username of the datastore user that can write diff --git a/ckanext/organizations/__init__.py b/ckanext/organizations/__init__.py deleted file mode 100644 index 9472ce03e3b..00000000000 --- a/ckanext/organizations/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -'''Organization form - -Provides a form for organization creation -''' -__version__ = '0.1' - diff --git a/ckanext/organizations/controllers.py b/ckanext/organizations/controllers.py deleted file mode 100644 index 56bba8f30e6..00000000000 --- a/ckanext/organizations/controllers.py +++ /dev/null @@ -1,174 +0,0 @@ -import logging - -from ckan.lib.base import BaseController, c, model, request, render, h, g -from ckan.lib.base import ValidationException, abort, gettext -from pylons.i18n import get_lang, _ -from ckan.lib.alphabet_paginate import AlphaPage -from ckan.lib.dictization.model_dictize import package_dictize - -import ckan.forms -import ckan.authz as authz -import ckan.lib.dictization.model_save as model_save -import ckan.lib.mailer as mailer -import ckan.lib.navl.dictization_functions as dict_func -import ckan.logic as logic -import ckan.logic.action as action -import ckan.logic.schema as schema -import ckan.model as model - -import pylons.config as config -from ckan.lib.navl.validators import (ignore_missing, - not_empty, - empty, - ignore, - keep_extras,) - -class OrganizationController(BaseController): - - def _send_application( self, group, reason ): - from genshi.template.text import NewTextTemplate - - if not reason: - h.flash_error(_("There was a problem with your submission, \ - please correct it and try again")) - errors = {"reason": ["No reason was supplied"]} - return self.apply(group.id, errors=errors, - error_summary=action.error_summary(errors)) - - admins = group.members_of_type( model.User, 'admin' ).all() - recipients = [(u.fullname,u.email) for u in admins] if admins else \ - [(config.get('ckan.admin.name', "CKAN Administrator"), - config.get('ckan.admin.email', None), )] - - if not recipients: - h.flash_error(_("There is a problem with the system configuration")) - errors = {"reason": ["No group administrator exists"]} - return self.apply(group.id, data=data, errors=errors, - error_summary=action.error_summary(errors)) - - extra_vars = { - 'group' : group, - 'requester': c.userobj, - 'reason' : reason - } - email_msg = render("organizations/email/join_publisher_request.txt", - extra_vars=extra_vars, - loader_class=NewTextTemplate) - - try: - for (name,recipient) in recipients: - mailer.mail_recipient(name, - recipient, - "Publisher request", - email_msg) - except: - h.flash_error(_("There is a problem with the system configuration")) - errors = {"reason": ["No mail server was found"]} - return self.apply(group.id, errors=errors, - error_summary=action.error_summary(errors)) - - h.flash_success(_("Your application has been submitted")) - h.redirect_to( 'publisher_read', id=group.name) - - def apply(self, id=None, data=None, errors=None, error_summary=None): - """ - A user has requested access to this publisher and so we will send an - email to any admins within the publisher. - """ - if 'parent' in request.params and not id: - id = request.params['parent'] - - if id: - c.group = model.Group.get(id) - if 'save' in request.params and not errors: - return self._send_application(c.group, request.params.get('reason', None)) - - self._add_publisher_list() - data = data or {} - errors = errors or {} - error_summary = error_summary or {} - - data.update(request.params) - - vars = {'data': data, 'errors': errors, 'error_summary': error_summary} - c.form = render('organizations/apply_form.html', extra_vars=vars) - return render('organizations/apply.html') - - def _add_users( self, group, parameters ): - if not group: - h.flash_error(_("There was a problem with your submission, " - "please correct it and try again")) - errors = {"reason": ["No reason was supplied"]} - return self.apply(group.id, errors=errors, - error_summary=action.error_summary(errors)) - - data_dict = logic.clean_dict(dict_func.unflatten( - logic.tuplize_dict(logic.parse_params(request.params)))) - data_dict['id'] = group.id - - # Temporary fix for strange caching during dev - l = data_dict['users'] - for d in l: - d['capacity'] = d.get('capacity','editor') - - context = { - "group" : group, - "schema": schema.default_group_schema(), - "model": model, - "session": model.Session - } - - # Temporary cleanup of a capacity being sent without a name - users = [d for d in data_dict['users'] if len(d) == 2] - data_dict['users'] = users - - model.repo.new_revision() - model_save.group_member_save(context, data_dict, 'users') - model.Session.commit() - - h.redirect_to( controller='group', action='edit', id=group.name) - - - def users(self, id, data=None, errors=None, error_summary=None): - c.group = model.Group.get(id) - - if not c.group: - abort(404, _('Group not found')) - - context = { - 'model': model, - 'session': model.Session, - 'user': c.user or c.author, - 'group': c.group } - - try: - logic.check_access('group_update',context) - except logic.NotAuthorized, e: - abort(401, _('User %r not authorized to edit %s') % (c.user, id)) - - if 'save' in request.params and not errors: - return self._add_users(c.group, request.params) - - data = data or {} - errors = errors or {} - error_summary = error_summary or {} - - data['users'] = [] - data['users'].extend( { "name": user.name, - "capacity": "admin" } - for user in c.group.members_of_type( model.User, "admin" ).all() ) - data['users'].extend( { "name": user.name, - "capacity": "editor" } - for user in c.group.members_of_type( model.User, 'editor' ).all() ) - - vars = {'data': data, 'errors': errors, 'error_summary': error_summary} - c.form = render('organizations/users_form.html', extra_vars=vars) - - return render('organizations/users.html') - - def _add_publisher_list(self): - c.possible_parents = model.Session.query(model.Group).\ - filter(model.Group.state == 'active').\ - filter(model.Group.type == 'organization').\ - order_by(model.Group.title).all() - diff --git a/ckanext/organizations/forms.py b/ckanext/organizations/forms.py deleted file mode 100644 index a9542973b46..00000000000 --- a/ckanext/organizations/forms.py +++ /dev/null @@ -1,274 +0,0 @@ -import os -import logging - -import ckan.authz as authz -from ckan.logic import NotAuthorized -from ckan.logic.schema import group_form_schema, form_to_db_package_schema -from ckan.lib import base -from ckan.lib.base import c, model, abort, request -from ckan.lib.base import redirect, _, config, h -from ckan.lib.navl.dictization_functions import DataError -import ckan.plugins as p - -from ckan.plugins import IGroupForm, IDatasetForm, IConfigurer, IRoutes -from ckan.plugins import implements, SingletonPlugin -from ckan.logic import check_access -from ckan.logic.validators import tag_string_convert -from ckan.lib.navl.validators import (ignore_missing, - not_empty, - empty, - ignore, - keep_extras) - -log = logging.getLogger(__name__) - - -def group_required(key, data, errors, context): - """ We want at least a group in the data we are provided """ - has_group = ('groups', 0, 'id') in data - if not has_group: - errors[('Organizations', '')] = \ - [_('Please choose an organization to add the dataset to')] - - -class OrganizationForm(SingletonPlugin): - """ - This plugin implements an IGroupForm for form associated with a - organization group. ``IConfigurer`` is used to add the local template - path and the IGroupForm supplies the custom form. - """ - implements(IGroupForm, inherit=True) - implements(IConfigurer, inherit=True) - implements(IRoutes) - implements(IConfigurer) - - def before_map(self, map): - controller = 'ckanext.organizations.controllers:OrganizationController' - map.connect('/organization/users/{id}', controller=controller, - action='users') - map.connect('/organization/apply/{id}', controller=controller, - action='apply') - map.connect('/organization/apply', controller=controller, - action='apply') - map.connect('/organization/edit/{id}', controller='group', - action='edit') - map.connect('/organization/new', controller='group', action='new') - map.connect('/organization/{id}', controller='group', action='read') - map.connect('/organization', controller='group', action='index') - map.redirect('/organizations', '/organization') - return map - - def after_map(self, map): - return map - - def update_config(self, config): - """ - This IConfigurer implementation causes CKAN to look in the - ```templates``` directory when looking for the group_form() - """ - templates = 'templates' - if p.toolkit.asbool(config.get('ckan.legacy_templates', False)): - templates = 'templates_legacy' - p.toolkit.add_template_directory(config, templates) - p.toolkit.add_public_directory(config, 'public') - - # Override /group/* as the default groups urls - config['ckan.default.group_type'] = 'organization' - - def new_template(self): - """ - Returns a string representing the location of the template to be - rendered for the new page - """ - return 'organizations/new.html' - - def index_template(self): - """ - Returns a string representing the location of the template to be - rendered for the index page - """ - return 'organizations/index.html' - - def read_template(self): - """ - Returns a string representing the location of the template to be - rendered for the read page - """ - return 'organizations/read.html' - - def history_template(self): - """ - Returns a string representing the location of the template to be - rendered for the read page - """ - return 'organizations/history.html' - - def edit_template(self): - """ - Returns a string representing the location of the template to be - rendered for the edit page - """ - return 'organization_edit.html' - - def group_form(self): - """ - Returns a string representing the location of the template to be - rendered. e.g. "forms/group_form.html". - """ - return 'organizations/form.html' - - def group_types(self): - """ - Returns an iterable of group type strings. - - If a request involving a group of one of those types is made, then - this plugin instance will be delegated to. - - There must only be one plugin registered to each group type. Any - attempts to register more than one plugin instance to a given group - type will raise an exception at startup. - """ - return ["organization"] - - def is_fallback(self): - """ - Returns true iff this provides the fallback behaviour, when no other - plugin instance matches a group's type. - - As this is not the fallback controller we should return False. If - we were wanting to act as the fallback, we'd return True - """ - return False - - def form_to_db_schema(self): - """ - Returns the schema for mapping group data from a form to a format - suitable for the database. - """ - return group_form_schema() - - def db_to_form_schema(self): - """ - Returns the schema for mapping group data from the database into a - format suitable for the form (optional) - """ - return group_form_schema() - - def check_data_dict(self, data_dict): - """ - Check if the return data is correct. - - raise a DataError if not. - """ - - def setup_template_variables(self, context, data_dict): - """ - Add variables to c just prior to the template being rendered. We should - use the available groups for the current user, but should be optional - in case this is a top level group - """ - c.user_groups = c.userobj.get_groups('organization') - local_ctx = {'model': model, 'session': model.Session, - 'user': c.user or c.author} - - try: - check_access('group_create', local_ctx) - c.is_superuser_or_groupadmin = True - except NotAuthorized: - c.is_superuser_or_groupadmin = False - - if 'group' in context: - group = context['group'] - # Only show possible groups where the current user is a member - c.possible_parents = c.userobj.get_groups('organization', 'admin') - - c.parent = None - grps = group.get_groups('organization') - if grps: - c.parent = grps[0] - c.users = group.members_of_type(model.User) - - -class OrganizationDatasetForm(SingletonPlugin): - - implements(IDatasetForm, inherit=True) - - def is_fallback(self): - return True - - def package_types(self): - return ['dataset'] - - def new_template(self): - """ - Returns a string representing the location of the template to be - rendered for the new page - """ - return 'package/new.html' - - def comments_template(self): - """ - Returns a string representing the location of the template to be - rendered for the comments page - """ - return 'package/comments.html' - - def search_template(self): - """ - Returns a string representing the location of the template to be - rendered for the search page (if present) - """ - return 'package/search.html' - - def read_template(self): - """ - Returns a string representing the location of the template to be - rendered for the read page - """ - return 'package/read.html' - - def history_template(self): - """ - Returns a string representing the location of the template to be - rendered for the history page - """ - return 'package/history.html' - - def package_form(self): - return 'organizations/package_form.html' - - def db_to_form_schema(self): - '''This is an interface to manipulate data from the database - into a format suitable for the form (optional)''' - - def form_to_db_schema(self): - schema = form_to_db_package_schema() - schema['groups']['capacity'] = [ignore_missing, unicode] - schema['__after'] = [group_required] - return schema - - def check_data_dict(self, data_dict, schema=None): - '''Check if the return data is correct, mostly for checking out - if spammers are submitting only part of the form''' - - def setup_template_variables(self, context, data_dict): - from pylons import config - - data_dict.update({'available_only': True}) - - c.groups_available = c.userobj and \ - c.userobj.get_groups('organization') or [] - c.licences = [('', '')] + base.model.Package.get_license_options() - c.is_sysadmin = authz.Authorizer().is_sysadmin(c.user) - - ## This is messy as auths take domain object not data_dict - context_pkg = context.get('package', None) - pkg = context_pkg or c.pkg - if pkg: - try: - if not context_pkg: - context['package'] = pkg - check_access('package_change_state', context) - c.auth_for_change_state = True - except NotAuthorized: - c.auth_for_change_state = False diff --git a/ckanext/organizations/templates/email/join_publisher_request.txt b/ckanext/organizations/templates/email/join_publisher_request.txt deleted file mode 100644 index c15b46e0c54..00000000000 --- a/ckanext/organizations/templates/email/join_publisher_request.txt +++ /dev/null @@ -1,12 +0,0 @@ -Dear administrator, - -A request has been made for membership of your organization $group.title by $requester.name {% if requester.fullname %}( $requester.fullname ){% end %} - -The reason given for the request was: - -"$reason" - -Please contact the user to verify and then if you would like to add this user you can do so by visiting ${h.url_for(controller='ckanext.organizers.controllers:OrganizationController', action='users', id=group.name, qualified=True) } - -If you do not wish to add this user you can safely disregard this email. - diff --git a/ckanext/organizations/templates/organization_edit.html b/ckanext/organizations/templates/organization_edit.html deleted file mode 100644 index 224d0733463..00000000000 --- a/ckanext/organizations/templates/organization_edit.html +++ /dev/null @@ -1,16 +0,0 @@ - - - Edit: ${c.group.display_name} - Edit: ${c.group.display_name} - ${h.literal('no-sidebar')} - - -
    - ${Markup(c.form)} -
    - - - - diff --git a/ckanext/organizations/templates/organizations/form.html b/ckanext/organizations/templates/organizations/form.html deleted file mode 100644 index 01d57727822..00000000000 --- a/ckanext/organizations/templates/organizations/form.html +++ /dev/null @@ -1,42 +0,0 @@ -{% extends "group/snippets/group_form.html" %} - -{# -As the form is rendered as a seperate page we take advantage of this by -overriding the form blocks depending on the current context -#} -{% block dataset_fields %} - {% if action == "edit" %}{{ super() }}{% endif %} -{% endblock %} - -{% block custom_fields %} - {% if action == "edit" %}{{ super() }}{% endif %} -{% endblock %} - -{% block save_text %} - {%- if action == "edit" -%} - {{ _('Update Organization') }} - {%- else -%} - {{ _('Create Organization') }} - {%- endif -%} -{% endblock %} - -{% block delete_button %} - {% if action == "edit" %}{{ super() }}{% endif %} -{% endblock %} - -{% block basic_fields %} - {% set attrs = {'data-module': 'slug-preview-target'} %} - {{ form.input('title', label=_('Title'), id='field-title', placeholder=_('My Organization'), value=data.title, error=errors.title, classes=['control-full'], attrs=attrs) }} - - {% set prefix = h.url_for(controller='group', action='read', id='') %} - {% set domain = h.url_for(controller='group', action='read', id='', qualified=true) %} - {% set domain = domain|replace("http://", "")|replace("https://", "") %} - {% set attrs = {'data-module': 'slug-preview-slug', 'data-module-prefix': domain, 'data-module-placeholder': ''} %} - - {{ form.prepend('name', label=_('URL'), prepend=prefix, id='field-url', placeholder=_('my-organization'), value=data.name, error=errors.name, attrs=attrs) }} - - {{ form.markdown('description', label=_('Description'), id='field-description', placeholder=_('A little information about my organization...'), value=data.description, error=errors.description) }} - - {{ form.input('image_url', label=_('Image URL'), id='field-image-url', type='url', placeholder=_('http://example.com/my-image.jpg'), value=data.image_url, error=errors.image_url, classes=['control-full']) }} - - {% endblock %} \ No newline at end of file diff --git a/ckanext/organizations/templates/organizations/index.html b/ckanext/organizations/templates/organizations/index.html deleted file mode 100644 index 41532b77b87..00000000000 --- a/ckanext/organizations/templates/organizations/index.html +++ /dev/null @@ -1,41 +0,0 @@ -{% extends "page.html" %} - -{% block subtitle %}{{ _('Organization') }}{% endblock %} - -{% block breadcrumb_content %} -
  • {% link_for _('Organizations'), controller='group', action='index' %}
  • -{% endblock %} - -{% block actions_content %} -
  • {% link_for _('Add Organization'), controller='group', action='new', class_='btn', icon='plus' %}
  • -{% endblock %} - -{% block primary_content %} -
    -
    -

    {{ _('Organizations') }}

    - {% if c.page.items %} - {% snippet "group/snippets/group_list.html", groups=c.page.items %} - {% else %} -

    - {{ _('There are currently no organizations for this site') }}. - {% if h.check_access('package_create') %} - {% link_for _('How about creating one?'), controller='group', action='new' %}. - {% endif %} -

    - {% endif %} -
    - {{ c.page.pager() }} -
    -{% endblock %} - -{% block secondary_content %} -
    -

    {{ _('What are Organizations?') }}

    -
    - {% trans %} -

    Organizations allow you to add both users and datasets to it in order to control who can access and manage your datasets

    - {% endtrans %} -
    -
    -{% endblock %} diff --git a/ckanext/organizations/templates/organizations/new.html b/ckanext/organizations/templates/organizations/new.html deleted file mode 100644 index 73a284f46a3..00000000000 --- a/ckanext/organizations/templates/organizations/new.html +++ /dev/null @@ -1,19 +0,0 @@ -{% extends "group/base_form_page.html" %} - -{% block subtitle %}{{ _('Create an organization') }}{% endblock %} - -{% block breadcrumb_link %}{{ h.nav_link(_('Create Organization'), controller='group', action='edit', id=c.group.name) }}{% endblock %} - -{% block page_heading %}{{ _('Create an organization') }}{% endblock %} - -{% block secondary_content %} -
    -

    {{ _('What are Organizations?') }}

    -
    - {% trans %} -

    An organization can be set-up to specify which users have permission to add or - remove datasets from it.

    - {% endtrans %} -
    -
    -{% endblock %} diff --git a/ckanext/organizations/templates/organizations/read.html b/ckanext/organizations/templates/organizations/read.html deleted file mode 100644 index c3a66ca1f84..00000000000 --- a/ckanext/organizations/templates/organizations/read.html +++ /dev/null @@ -1,47 +0,0 @@ -{% extends "page.html" %} - -{% block subtitle %}{{ c.group_dict.display_name }}{% endblock %} - -{% block breadcrumb_content %} -
  • {% link_for _('Organizations'), controller='group', action='index' %}
  • -
  • {% link_for c.group_dict.display_name|truncate(35), controller='group', action='read', id=c.group_dict.name %}
  • -{% endblock %} - -{% block actions_content %} - {% if h.check_access('group_update', {'id': c.group.id}) %} -
  • {% link_for _('Add Dataset to Organization'), controller='package', action='new', group=c.group_dict.id, class_='btn', icon='plus' %}
  • -
  • {% link_for _('Edit'), controller='group', action='edit', id=c.group_dict.name, class_='btn', icon='cog' %}
  • -
  • {% link_for _('Manage Users'), controller='ckanext.organizations.controllers:OrganizationController', action='users', id=c.group_dict.name, class_='btn', icon='cog' %}
  • - {% endif %} - {#
  • {% link_for _('History'), controller='group', action='history', id=c.group_dict.name, class_='btn', icon='undo' %}
  • #} -{% endblock %} - -{% block primary_content %} -
    -
    - {% include "package/snippets/search_form.html" %} -
    - {{ c.page.pager(q=c.q) }} -
    -{% endblock %} - -{% block secondary_content %} - {% snippet 'snippets/group.html', group=c.group_dict %} - -
    -

    {{ _('Administrators') }}

    - -
    - - {{ h.snippet('snippets/facet_list.html', title='Tags', name='tags', extras={'id':c.group_dict.id}) }} - {{ h.snippet('snippets/facet_list.html', title='Formats', name='res_format', extras={'id':c.group_dict.id}) }} -{% endblock %} - -{% block links %} - {{ super() }} - {% include "group/snippets/feeds.html" %} -{% endblock %} diff --git a/ckanext/organizations/templates/organizations/users.html b/ckanext/organizations/templates/organizations/users.html deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/ckanext/organizations/templates/organizations/users_form.html b/ckanext/organizations/templates/organizations/users_form.html deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/ckanext/organizations/templates_legacy/organizations/apply.html b/ckanext/organizations/templates_legacy/organizations/apply.html deleted file mode 100644 index d88eee54370..00000000000 --- a/ckanext/organizations/templates_legacy/organizations/apply.html +++ /dev/null @@ -1,15 +0,0 @@ - - - Apply - Apply for membership - - -
    - ${Markup(c.form)} -
    - - - - diff --git a/ckanext/organizations/templates_legacy/organizations/apply_form.html b/ckanext/organizations/templates_legacy/organizations/apply_form.html deleted file mode 100644 index 4799eb96536..00000000000 --- a/ckanext/organizations/templates_legacy/organizations/apply_form.html +++ /dev/null @@ -1,49 +0,0 @@ - -
    - -
    -

    Errors in form

    -

    The form contains invalid entries:

    -
      -
    • ${"%s: %s" % (key, error)}
    • -
    -
    - - - -
    -
    -
    - -
    -
    - -
    - - -
    -
    - -
    - Please explain to the owner your reasons for wishing to become an editor of this organization -
    -
    -
    - - -
    - - - - -
    -
    diff --git a/ckanext/organizations/templates_legacy/organizations/email/join_publisher_request.txt b/ckanext/organizations/templates_legacy/organizations/email/join_publisher_request.txt deleted file mode 100644 index c15b46e0c54..00000000000 --- a/ckanext/organizations/templates_legacy/organizations/email/join_publisher_request.txt +++ /dev/null @@ -1,12 +0,0 @@ -Dear administrator, - -A request has been made for membership of your organization $group.title by $requester.name {% if requester.fullname %}( $requester.fullname ){% end %} - -The reason given for the request was: - -"$reason" - -Please contact the user to verify and then if you would like to add this user you can do so by visiting ${h.url_for(controller='ckanext.organizers.controllers:OrganizationController', action='users', id=group.name, qualified=True) } - -If you do not wish to add this user you can safely disregard this email. - diff --git a/ckanext/organizations/templates_legacy/organizations/form.html b/ckanext/organizations/templates_legacy/organizations/form.html deleted file mode 100644 index 5a1dc6abe96..00000000000 --- a/ckanext/organizations/templates_legacy/organizations/form.html +++ /dev/null @@ -1,156 +0,0 @@ -
    - - - -
    -

    Errors in form

    -

    The form contains invalid entries:

    -
      -
    • ${"%s: %s" % (key if not key=='Name' else 'URL', error)}
    • -
    -
    - -
    -
    - -
    - -
    -
    -
    - -
    -
    - ${h.url(controller='group', action='index')+'/'} - -
    -

     

    -

    Warning: URL is very long. Consider changing it to something shorter.

    -

    2+ characters, lowercase, using only 'a-z0-9' and '-_'

    -

    ${errors.get('name', '')}

    -
    -
    -
    - -
    - ${markdown_editor('description', data.get('description'), 'notes', _('Start with a summary sentence ...'))} -
    -
    -
    - -
    - -

    The URL for the image that is associated with this organization.

    -
    -
    -
    - -
    - -
    -
    -
    - -
    - -
    - - ${ c.parent.title } - - - No parent organization - -
    -
    - -
    -
    - - -
    -

    Extras

    -
    - - -
    - -
    - - - -
    -
    -
    -
    - -
    - -
    - - -
    -
    -
    -
    -
    -
    - - -
    -

    Users (${len(c.users.all())})

    -Manage users - -
    - -
    - - - - -
    -
    -
    -

    There are no users currently in this publisher.

    -
    - - -
    - - - - -
    - diff --git a/ckanext/organizations/templates_legacy/organizations/history.html b/ckanext/organizations/templates_legacy/organizations/history.html deleted file mode 100644 index 227bf1b9a42..00000000000 --- a/ckanext/organizations/templates_legacy/organizations/history.html +++ /dev/null @@ -1,59 +0,0 @@ - - - History: ${c.group.display_name} - History: ${c.group.display_name} - -
    -

    - Revisions - -

    -
    - -

    - Error: ${c.error} -

    - - - - - - - - - - - - - - - - -
    RevisionTimestampAuthorLog Message
    - ${h.radio("selected1", revision_dict['id'], checked=(index == 0))} - ${h.radio("selected2", revision_dict['id'], checked=(index == len(c.group_revisions)-1))} - - ${revision_dict['id']} - ${revision_dict['timestamp']}${h.linked_user(revision_dict['author'])}${revision_dict['message']}
    - -
    -
    - - - - - - - diff --git a/ckanext/organizations/templates_legacy/organizations/index.html b/ckanext/organizations/templates_legacy/organizations/index.html deleted file mode 100644 index cbf38919286..00000000000 --- a/ckanext/organizations/templates_legacy/organizations/index.html +++ /dev/null @@ -1,24 +0,0 @@ - - - Organizations - Organizations - - -
  • -

    What Are Organizations?

    - Whilst tags are great at collecting datasets together, there are occasions when you want to restrict users from editing a collection. An organization can be set-up to specify which users have permission to add or remove datasets from it. -
  • -
    - - -
    - ${c.page.pager()} - ${group_list_from_dict(c.page.items)} - ${c.page.pager()} -
    - - - diff --git a/ckanext/organizations/templates_legacy/organizations/layout.html b/ckanext/organizations/templates_legacy/organizations/layout.html deleted file mode 100644 index 7e740e9bd3a..00000000000 --- a/ckanext/organizations/templates_legacy/organizations/layout.html +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - diff --git a/ckanext/organizations/templates_legacy/organizations/new.html b/ckanext/organizations/templates_legacy/organizations/new.html deleted file mode 100644 index 642d57f5b5e..00000000000 --- a/ckanext/organizations/templates_legacy/organizations/new.html +++ /dev/null @@ -1,14 +0,0 @@ - - - Add an organization - Add an organization - -
    - ${Markup(c.form)} -
    - - - - diff --git a/ckanext/organizations/templates_legacy/organizations/package_form.html b/ckanext/organizations/templates_legacy/organizations/package_form.html deleted file mode 100644 index cfd68253d3f..00000000000 --- a/ckanext/organizations/templates_legacy/organizations/package_form.html +++ /dev/null @@ -1,324 +0,0 @@ -
    - -
    -

    Errors in form

    -

    The form contains invalid entries:

    -
      -
    • ${"%s: %s" % (key if not key=='Name' else 'URL', error)} - -
        - -
      • - Resource ${idx}: -
          -
        • ${thiskey}: ${errorinfo};
        • -
        -
      • -
        -
      -
      -
    • - -
    -
    - -
    -
    - -
    - -

    ${errors.get('title', '')}

    -
    -
    - -
    - -
    -
    - ${h.url(controller='package', action='search')+'/'} - -
    -

     

    -

    Warning: URL is very long. Consider changing it to something shorter.

    -

    2+ characters, lowercase, using only 'a-z0-9' and '-_'

    -

    ${errors.get('name', '')}

    -
    -
    - -
    - -
    - -

    The URL for the web page describing the data (not the data itself).

    -

    e.g. http://www.example.com/growth-figures.html

    -

    ${errors.get('url', '')}

    -
    -
    - -
    - -
    - -

    (Don't worry if you don't know which license the data has been released under).

    -
    -
    - - -
    - -
    -
    -
      -
    • -
    • -
    - - - You can use Markdown formatting here. -
    -
    -
    - -
    - -
    - - - - - - - - - - ${organization.get('title', '')} - - Cannot add to any organizations. Please join an organization -
    -
    - -
    - -
    - -

    Comma-separated terms that may link this dataset to similar ones. For more information on conventions, see this wiki page.

    -

    e.g. pollution, rivers, water quality

    -

    ${errors.get('tag_string', '')}

    -
    -
    -
    - -
    - Add Resources -
    -

    Upload or link data files, APIs and other materials related to your dataset.

    -
    -
    - -
    - -
    -
    - -
    - - - - - - -
    -
    - -
    -
    - -
    - -

    The name of the main contact, for enquiries about this particular dataset, using the e-mail address in the following field.

    -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - -

    If there is another important contact person (in addition to the person in the Author field) then provide details here.

    -
    -
    - -
    - -
    - -
    -
    - -
    - -
    - -

    A number representing the version (if applicable)

    -

    e.g. 1.2.0

    -
    -
    -
    - -
    -

    Adding custom fields to the dataset such as "location:uk" can help users find it in the search engine. This data will also appear under Additional Information when viewing the dataset.

    - - -
    - -
    - - - -
    -
    -
    -
    - -
    - -
    - - -
    -
    -
    -
    -
    - -
    -
    -
    Delete
    -
    -

    Do you really want to change the state of this dataset?   

    - - This dataset is   - - -
    -
    -
    - -
    -
    - -
    -

    Briefly describe the changes you have made...

    - -
    -
    -
    - - -
    - -

    - Since you have not signed in this will just be your IP address. - Click here to sign in before saving (opens in new window). -

    -
    - -
    - - - - -

    - Important: By submitting content, you agree to release your contributions under the Open Database License. Please refrain from editing this page if you are not happy to do this. -

    -
    -
    - - -
    diff --git a/ckanext/organizations/templates_legacy/organizations/read.html b/ckanext/organizations/templates_legacy/organizations/read.html deleted file mode 100644 index a0224c00bca..00000000000 --- a/ckanext/organizations/templates_legacy/organizations/read.html +++ /dev/null @@ -1,81 +0,0 @@ - - - - - ${c.group_dict.display_name} - ${c.group_dict.display_name} - - ${c.group.image_url} - - - - - - - -
  • -
      - -
    • -

      Administrators

      -
        -
      • ${h.linked_user(admin)}
      • -
      -
    • -
      - -
    • -

      Members

      -
        -
      • ${h.linked_user(editor)}
      • -
      -
    • -
      -
    -
  • - ${facet_div('tags', 'Tags')} - ${facet_div('res_format', 'Resource Formats')} -
    - - -

    State: ${c.group['state']}

    -
    -
    - ${c.description_formatted} -
    -
    - -
    -
    -

    Datasets

    - - - ${field_list()} - -

    You searched for "${c.q}". ${c.page.item_count} datasets found.

    - ${c.page.pager()} - ${package_list_from_dict(c.page.items)} - ${c.page.pager()} -
    -
    - - - diff --git a/ckanext/organizations/templates_legacy/organizations/users.html b/ckanext/organizations/templates_legacy/organizations/users.html deleted file mode 100644 index 9b5e47d9dbf..00000000000 --- a/ckanext/organizations/templates_legacy/organizations/users.html +++ /dev/null @@ -1,16 +0,0 @@ - - - Users: ${c.group.display_name} - Users: ${c.group.display_name} - - -
    - ${Markup(c.form)} -
    - - - - - diff --git a/ckanext/organizations/templates_legacy/organizations/users_form.html b/ckanext/organizations/templates_legacy/organizations/users_form.html deleted file mode 100644 index cc9ff15e0bf..00000000000 --- a/ckanext/organizations/templates_legacy/organizations/users_form.html +++ /dev/null @@ -1,72 +0,0 @@ -
    - -
    -

    Errors in form

    -

    The form contains invalid entries:

    -
      -
    • ${"%s: %s" % (key, error)}
    • -
    -
    - - - -
    -

    Users

    -
    - -
    - - - -
    - Admin - Editor - - -
    -
    -
    -
    -

    There are no users currently in this organization.

    - -

    Add users

    -
    -
    - -
    -
    -
    - - -
    - - - - -
    - - - -
    - diff --git a/doc/about.rst b/doc/about.rst deleted file mode 100644 index 72df911d93f..00000000000 --- a/doc/about.rst +++ /dev/null @@ -1,33 +0,0 @@ -About CKAN -=========== - -Development ------------ - -CKAN is an open source project and contributions are welcome! - -We discuss large changes and new features on the `ckan-discuss `_ mailing list, and technical issues on the `ckan-dev `_ mailing list. Please join us there. - -You can find developer resources and links to our ticketing system on the `CKAN wiki `_. - -Acknowledgements ----------------- - -Thanks to the following projects, without which CKAN would not have -been possible: - - * `Python `_ - * `Pylons `_ - * CKAN logo: "angry hamster" by http://www.maedelmaedel.com/ and - http://www.villainous.biz/ - * `FamFamFam silk icons `_ - -Copying and Licence -------------------- - -This material is copyright (c) 2006-2011 Open Knowledge Foundation. - -It is open and licensed under the GNU Affero General Public License (AGPL) v3.0 -whose full text may be found at: - - diff --git a/doc/administrative-dashboard.rst b/doc/administrative-dashboard.rst deleted file mode 100644 index 6e90735e055..00000000000 --- a/doc/administrative-dashboard.rst +++ /dev/null @@ -1,66 +0,0 @@ -============================= -CKAN Administrative Dashboard -============================= - -CKAN provides an administrative dashboard available to Sysadmin Administrators. -The dashboard allows you to: - -* Create and remove sysadmins -* Edit general system level authorization -* Manage the 'trash' bin (i.e. datasets or revisions that have been marked as deleted) - -The dashboard is located, relative to your site root, at ``/ckan-admin/``. - -.. note:: To create your first sysadmin you cannot use Dashboard as you will - not yet have access! Instead create a sysadmin using the command line - ``paster`` by running the following command:: - - paster sysadmin -h - -Setting System-Level Roles -========================== - -Authorization interface is located at: ``/ckan-admin/authz`` - -This page allows you to see and change the users and authorization groups who -have 'roles' on the 'System Object'. In a standard installation, there are four -'roles' which a user can have on the System (or on any object): - -* admin (administrator) - - * Having an admin role on the System objects means you are a System Administrator - and may carry out **any** operation on any object. - - .. warning:: Once a person is an system administrator, they can carry out - **any** operatoin on the system including **destructive** - ones. Grant System Administrator access with care! - -* reader (Read action allowed) - - * Without read access a site user or visitor will not be able to see - anything except the login page, even the page which allows them to - create an account, so they're locked out forever unless they already - have a valid account. - -* editor (Update action allowed) -* anon-editor - -.. note:: these roles can be applied to users on your system as well as to - 'pseudo-users' like 'visitor', which stands for anyone who accesses - the site whether logged in or not (see :doc:`authorization` for more - on permissions and roles). - -Make Someone a Sysadmin -======================= - -Given the user the role 'admin'. - -The Trash -========= - -When you delete datasets or revisions they go into the 'trash'. The contents of -the trash can be viewed by System Administrators at: ``/ckan-admin/trash``. - -Contents of the trash can be removed permanently (and **irreversibly**) by -going to the trash page and selecting the purge option. - diff --git a/doc/configuration.rst b/doc/configuration.rst index 7dbc7a4c7f8..6e9bae57393 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -233,6 +233,23 @@ CKAN operates a delegated authentication model based on `OpenID