def read(self, id, format='html'):
        if not format == 'html':
            ctype, extension, loader = \
                self._content_type_from_extension(format)
            if not ctype:
                # An unknown format, we'll carry on in case it is a
                # revision specifier and re-constitute the original id
                id = "%s.%s" % (id, format)
                ctype, format, loader = "text/html; charset=utf-8", "html", \
                    MarkupTemplate
        else:
            ctype, format, loader = self._content_type_from_accept()

        response.headers['Content-Type'] = ctype

        package_type = self._get_package_type(id.split('@')[0])
        context = {'model': model, 'session': model.Session,
                   'user': c.user or c.author, 'for_view': True,
                   'auth_user_obj': c.userobj}
        data_dict = {'id': id}

        # interpret @<revision_id> or @<date> suffix
        split = id.split('@')
        if len(split) == 2:
            data_dict['id'], revision_ref = split
            if model.is_id(revision_ref):
                context['revision_id'] = revision_ref
            else:
                try:
                    date = h.date_str_to_datetime(revision_ref)
                    context['revision_date'] = date
                except TypeError, e:
                    abort(400, _('Invalid revision format: %r') % e.args)
                except ValueError, e:
                    abort(400, _('Invalid revision format: %r') % e.args)
Beispiel #2
0
def resource_update(context, data_dict):
    model = context['model']
    user = context.get('user')
    resource = logic_auth.get_resource_object(context, data_dict)

    # check authentication against package
    query = model.Session.query(model.Package)\
        .join(model.ResourceGroup)\
        .join(model.Resource)\
        .filter(model.ResourceGroup.id == resource.resource_group_id)
    pkg = query.first()
    if not pkg:
        raise logic.NotFound(
            _('No package found for this resource, cannot check auth.')
        )

    pkg_dict = {'id': pkg.id}
    authorized = new_authz.is_authorized('package_update', context, pkg_dict).get('success')

    if not authorized:
        return {'success': False,
                'msg': _('User %s not authorized to edit resource %s') %
                        (str(user), resource.id)}
    else:
        return {'success': True}
Beispiel #3
0
def package_update(context, data_dict):
    user = context.get('user')
    package = logic_auth.get_package_object(context, data_dict)

    if package.owner_org:
        # if there is an owner org then we must have update_dataset
        # premission for that organization
        check1 = new_authz.has_user_permission_for_group_or_org(
            package.owner_org, user, 'update_dataset'
        )
    else:
        # If dataset is not owned then we can edit if config permissions allow
        if not new_authz.auth_is_anon_user(context):
            check1 = new_authz.check_config_permission(
                'create_dataset_if_not_in_organization')
        else:
            check1 = new_authz.check_config_permission('anon_create_dataset')
    if not check1:
        return {'success': False,
                'msg': _('User %s not authorized to edit package %s') %
                        (str(user), package.id)}
    else:
        check2 = _check_group_auth(context, data_dict)
        if not check2:
            return {'success': False,
                    'msg': _('User %s not authorized to edit these groups') %
                            (str(user))}

    return {'success': True}
    def _save_edit(self, name_or_id, context, package_type=None):
        from ckan.lib.search import SearchIndexError
        log.debug('Package save request name: %s POST: %r',
                  name_or_id, request.POST)
        try:
            data_dict = clean_dict(dict_fns.unflatten(
                tuplize_dict(parse_params(request.POST))))
            if '_ckan_phase' in data_dict:
                # we allow partial updates to not destroy existing resources
                context['allow_partial_update'] = True
                data_dict['tags'] = self._tag_string_to_list(
                    data_dict['tag_string'])
                del data_dict['_ckan_phase']
                del data_dict['save']
            context['message'] = data_dict.get('log_message', '')
            if not context['moderated']:
                context['pending'] = False
            data_dict['id'] = name_or_id

            #log.fatal("-----------> data dict: % s" % data_dict)

            pkg = get_action('package_update')(context, data_dict)
            if request.params.get('save', '') == 'Approve':
                get_action('make_latest_pending_package_active')(
                    context, data_dict)
            c.pkg = context['package']
            c.pkg_dict = pkg

            self._form_save_redirect(pkg['name'], 'edit', package_type=package_type)
        except NotAuthorized:
            abort(401, _('Unauthorized to read package %s') % id)
        except NotFound, e:
            abort(404, _('Dataset not found'))
Beispiel #5
0
def user_update(context, data_dict):
    user = context['user']

    # FIXME: We shouldn't have to do a try ... except here, validation should
    # have ensured that the data_dict contains a valid user id before we get to
    # authorization.
    try:
        user_obj = logic_auth.get_user_object(context, data_dict)
    except logic.NotFound:
        return {'success': False, 'msg': _('User not found')}

    # If the user has a valid reset_key in the db, and that same reset key
    # has been posted in the data_dict, we allow the user to update
    # her account without using her password or API key.
    if user_obj.reset_key and 'reset_key' in data_dict:
        if user_obj.reset_key == data_dict['reset_key']:
            return {'success': True}

    if not user:
        return {'success': False,
                'msg': _('Have to be logged in to edit user')}

    if user == user_obj.name:
        # Allow users to update their own user accounts.
        return {'success': True}
    else:
        # Don't allow users to update other users' accounts.
        return {'success': False,
                'msg': _('User %s not authorized to edit user %s') %
                        (user, user_obj.id)}
Beispiel #6
0
    def authz(self, id):
        group = model.Group.get(id)
        if group is None:
            abort(404, _('Group not found'))
        group_type = group.type
        if group_type not in self.group_types:
            abort(404, _('Incorrect group type'))
        c.groupname = group.name
        c.grouptitle = group.display_name

        try:
            context = \
                {'model': model, 'user': c.user, 'group': group}
            self._check_access('group_edit_permissions', context)
            c.authz_editable = True
            c.group = context['group']
        except NotAuthorized:
            c.authz_editable = False
        if not c.authz_editable:
            abort(403,
                  _('User %r not authorized to edit %s authorizations') %
                   (c.user, id))

        roles = self._handle_update_of_authz(group)
        self._prepare_authz_info_for_render(roles)
        return render('group/authz.html',
                      extra_vars={'group_type': group_type})
Beispiel #7
0
    def member_delete(self, id):
        group_type = self._ensure_controller_matches_group_type(id)

        if 'cancel' in request.params:
            self._redirect_to_this_controller(action='members', id=id)

        context = {'model': model, 'session': model.Session,
                   'user': c.user}

        try:
            self._check_access('group_member_delete', context, {'id': id})
        except NotAuthorized:
            abort(403, _('Unauthorized to delete group %s members') % '')

        try:
            user_id = request.params.get('user')
            if request.method == 'POST':
                self._action('group_member_delete')(
                    context, {'id': id, 'user_id': user_id})
                h.flash_notice(_('Group member has been deleted.'))
                self._redirect_to_this_controller(action='members', id=id)
            c.user_dict = self._action('user_show')(context, {'id': user_id})
            c.user_id = user_id
            c.group_id = id
        except NotAuthorized:
            abort(403, _('Unauthorized to delete group %s members') % '')
        except NotFound:
            abort(404, _('Group not found'))
        return self._render_template('group/confirm_delete_member.html',
                                     group_type)
Beispiel #8
0
def list_of_strings(key, data, errors, context):
    value = data.get(key)
    if not isinstance(value, list):
        raise Invalid(_('Not a list'))
    for x in value:
        if not isinstance(x, string_types):
            raise Invalid('%s: %s' % (_('Not a string'), x))
Beispiel #9
0
 def _ensure_controller_matches_group_type(self, id):
     group = model.Group.get(id)
     if group is None:
         abort(404, _('Group not found'))
     if group.type not in self.group_types:
         abort(404, _('Incorrect group type'))
     return group.type
Beispiel #10
0
def package_name_validator(key, data, errors, context):
    model = context['model']
    session = context['session']
    package = context.get('package')

    query = session.query(model.Package.state).filter_by(name=data[key])
    if package:
        package_id = package.id
    else:
        package_id = data.get(key[:-1] + ('id',))
    if package_id and package_id is not missing:
        query = query.filter(model.Package.id != package_id)
    result = query.first()
    if result and result.state != State.DELETED:
        errors[key].append(_('That URL is already in use.'))

    value = data[key]
    if len(value) < PACKAGE_NAME_MIN_LENGTH:
        raise Invalid(
            _('Name "%s" length is less than minimum %s') % (value, PACKAGE_NAME_MIN_LENGTH)
        )
    if len(value) > PACKAGE_NAME_MAX_LENGTH:
        raise Invalid(
            _('Name "%s" length is more than maximum %s') % (value, PACKAGE_NAME_MAX_LENGTH)
        )
Beispiel #11
0
def user_name_exists(user_name, context):
    model = context['model']
    session = context['session']
    result = session.query(model.User).filter_by(name=user_name).first()
    if not result:
        raise Invalid('%s: %s' % (_('Not found'), _('User')))
    return result.name
Beispiel #12
0
def owner_org_validator(key, data, errors, context):

    value = data.get(key)

    if value is missing or value is None:
        if not authz.check_config_permission('create_unowned_dataset'):
            raise Invalid(_('An organization must be provided'))
        data.pop(key, None)
        raise df.StopOnError

    model = context['model']
    user = context['user']
    user = model.User.get(user)
    if value == '':
        if not authz.check_config_permission('create_unowned_dataset'):
            raise Invalid(_('An organization must be provided'))
        return

    group = model.Group.get(value)
    if not group:
        raise Invalid(_('Organization does not exist'))
    group_id = group.id
    if not context.get(u'ignore_auth', False) and not(user.sysadmin or
           authz.has_user_permission_for_group_or_org(
               group_id, user.name, 'create_dataset')):
        raise Invalid(_('You cannot add a dataset to this organization'))
    data[key] = group_id
Beispiel #13
0
def name_validator(value, context):
    '''Return the given value if it's a valid name, otherwise raise Invalid.

    If it's a valid name, the given value will be returned unmodified.

    This function applies general validation rules for names of packages,
    groups, users, etc.

    Most schemas also have their own custom name validator function to apply
    custom validation rules after this function, for example a
    ``package_name_validator()`` to check that no package with the given name
    already exists.

    :raises ckan.lib.navl.dictization_functions.Invalid: if ``value`` is not
        a valid name

    '''
    if not isinstance(value, string_types):
        raise Invalid(_('Names must be strings'))

    # check basic textual rules
    if value in ['new', 'edit', 'search']:
        raise Invalid(_('That name cannot be used'))

    if len(value) < 2:
        raise Invalid(_('Must be at least %s characters long') % 2)
    if len(value) > PACKAGE_NAME_MAX_LENGTH:
        raise Invalid(_('Name must be a maximum of %i characters long') % \
                      PACKAGE_NAME_MAX_LENGTH)
    if not name_match.match(value):
        raise Invalid(_('Must be purely lowercase alphanumeric '
                        '(ascii) characters and these symbols: -_'))
    return value
Beispiel #14
0
    def delete(self, id):
        if 'cancel' in request.params:
            self._redirect_to(controller='group', action='edit', id=id)

        context = {'model': model, 'session': model.Session,
                   'user': c.user or c.author}

        try:
            self._check_access('group_delete', context, {'id': id})
        except NotAuthorized:
            abort(401, _('Unauthorized to delete group %s') % '')

        try:
            if request.method == 'POST':
                self._action('group_delete')(context, {'id': id})
                if self.group_type == 'organization':
                    h.flash_notice(_('Organization has been deleted.'))
                else:
                    h.flash_notice(_('Group has been deleted.'))
                self._redirect_to(controller='group', action='index')
            c.group_dict = self._action('group_show')(context, {'id': id})
        except NotAuthorized:
            abort(401, _('Unauthorized to delete group %s') % '')
        except NotFound:
            abort(404, _('Group not found'))
        return self._render_template('group/confirm_delete.html')
def get_terms_of_use_icon(terms_of_use):
    term_to_image_mapping = {
        'NonCommercialAllowed-CommercialAllowed-ReferenceNotRequired': {  # noqa
            'title': _('Open data'),
            'icon': 'terms_open',
        },
        'NonCommercialAllowed-CommercialAllowed-ReferenceRequired': {  # noqa
            'title': _('Reference required'),
            'icon': 'terms_by',
        },
        'NonCommercialAllowed-CommercialWithPermission-ReferenceNotRequired': {  # noqa
            'title': _('Commercial use with permission allowed'),
            'icon': 'terms_ask',
        },
        'NonCommercialAllowed-CommercialWithPermission-ReferenceRequired': {  # noqa
            'title': _('Reference required / Commercial use with permission allowed'),  # noqa
            'icon': 'terms_by-ask',
        },
        'ClosedData': {
            'title': _('Closed data'),
            'icon': 'terms_closed',
        },
    }
    term_id = simplify_terms_of_use(terms_of_use)
    return term_to_image_mapping.get(term_id, None)
Beispiel #16
0
    def read(self, id, limit=20):
        group_type = self._get_group_type(id.split('@')[0])
        if group_type != self.group_type:
            abort(404, _('Incorrect group type'))

        context = {'model': model, 'session': model.Session,
                   'user': c.user or c.author,
                   'schema': self._db_to_form_schema(group_type=group_type),
                   'for_view': True}
        data_dict = {'id': id}

        # unicode format (decoded from utf8)
        q = c.q = request.params.get('q', '')

        try:
            # Do not query for the group datasets when dictizing, as they will
            # be ignored and get requested on the controller anyway
            context['include_datasets'] = False
            c.group_dict = self._action('group_show')(context, data_dict)
            c.group = context['group']
        except NotFound:
            abort(404, _('Group not found'))
        except NotAuthorized:
            abort(401, _('Unauthorized to read group %s') % id)

        self._read(id, limit)
        return render(self._read_template(c.group_dict['type']))
Beispiel #17
0
    def edit(self, id, data=None, errors=None, error_summary=None):
        group_type = self._get_group_type(id.split('@')[0])
        context = {'model': model, 'session': model.Session,
                   'user': c.user or c.author,
                   'save': 'save' in request.params,
                   'for_edit': True,
                   'parent': request.params.get('parent', None)
                   }
        data_dict = {'id': id}

        if context['save'] and not data:
            return self._save_edit(id, context)

        try:
            old_data = self._action('group_show')(context, data_dict)
            c.grouptitle = old_data.get('title')
            c.groupname = old_data.get('name')
            data = data or old_data
        except NotFound:
            abort(404, _('Group not found'))
        except NotAuthorized:
            abort(401, _('Unauthorized to read group %s') % '')

        group = context.get("group")
        c.group = group
        c.group_dict = self._action('group_show')(context, data_dict)

        try:
            self._check_access('group_update', context)
        except NotAuthorized, e:
            abort(401, _('User %r not authorized to edit %s') % (c.user, id))
Beispiel #18
0
    def logged_in(self):
        # redirect if needed
        came_from = request.params.get('came_from', '')
        if self._sane_came_from(came_from):
            return h.redirect_to(str(came_from))

        if c.user:
            context = None
            data_dict = {'id': c.user}

            user_dict = get_action('user_show')(context, data_dict)

            h.flash_success(
                _("%s is now logged in") % user_dict['display_name'])
            return self.me()
        else:
            err = _('Login failed. Bad username or password.')
            if g.openid_enabled:
                err += _(' (Or if using OpenID, it hasn\'t been associated '
                         'with a user account.)')
            if h.asbool(config.get('ckan.legacy_templates', 'false')):
                h.flash_error(err)
                h.redirect_to(
                    controller='user', action='login', came_from=came_from)
            else:
                return self.login(error=err)
Beispiel #19
0
 def dataset_facets(self, facets_dict, package_type):
     facets_dict = OrderedDict()
     facets_dict['tags'] = _('Tags')
     facets_dict['organization'] = _('Organizations')
     facets_dict['groups'] = _('Groups')
     facets_dict['format'] = _('Formats')
     return facets_dict
Beispiel #20
0
def tag_delete(context, data_dict):
    '''Delete a tag.

    You must be a sysadmin to delete tags.

    :param id: the id or name of the tag
    :type id: string
    :param vocabulary_id: the id or name of the vocabulary that the tag belongs
        to (optional, default: None)
    :type vocabulary_id: string

    '''
    model = context['model']

    if not data_dict.has_key('id') or not data_dict['id']:
        raise ValidationError({'id': _('id not in data')})
    tag_id_or_name = _get_or_bust(data_dict, 'id')

    vocab_id_or_name = data_dict.get('vocabulary_id')

    tag_obj = model.tag.Tag.get(tag_id_or_name, vocab_id_or_name)

    if tag_obj is None:
        raise NotFound(_('Could not find tag "%s"') % tag_id_or_name)

    _check_access('tag_delete', context, data_dict)

    tag_obj.delete()
    model.repo.commit()
Beispiel #21
0
    def edit(self, id=None, data=None, errors=None, error_summary=None):
        context = {'save': 'save' in request.params,
                   'schema': self._edit_form_to_db_schema(),
                   'model': model, 'session': model.Session,
                   'user': c.user, 'auth_user_obj': c.userobj
                   }
        if id is None:
            if c.userobj:
                id = c.userobj.id
            else:
                abort(400, _('No user specified'))
        data_dict = {'id': id}

        try:
            check_access('user_update', context, data_dict)
        except NotAuthorized:
            abort(403, _('Unauthorized to edit a user.'))

        if (context['save']) and not data:
            return self._save_edit(id, context)

        try:
            old_data = get_action('user_show')(context, data_dict)

            schema = self._db_to_edit_form_schema()
            if schema:
                old_data, errors = \
                    dictization_functions.validate(old_data, schema, context)

            c.display_name = old_data.get('display_name')
            c.user_name = old_data.get('name')

            data = data or old_data

        except NotAuthorized:
            abort(403, _('Unauthorized to edit user %s') % '')
        except NotFound:
            abort(404, _('User not found'))

        user_obj = context.get('user_obj')

        if not (authz.is_sysadmin(c.user)
                or c.user == user_obj.name):
            abort(403, _('User %s not authorized to edit %s') %
                  (str(c.user), id))

        errors = errors or {}
        vars = {'data': data, 'errors': errors, 'error_summary': error_summary}

        self._setup_template_variables({'model': model,
                                        'session': model.Session,
                                        'user': c.user},
                                       data_dict)

        c.is_myself = True
        c.show_email_notifications = asbool(
            config.get('ckan.activity_streams_email_notifications'))
        c.form = render(self.edit_user_form, extra_vars=vars)

        return render('user/edit.html')
Beispiel #22
0
    def request_reset(self):
        context = {'model': model, 'session': model.Session, 'user': c.user,
                   'auth_user_obj': c.userobj}
        data_dict = {'id': request.params.get('user')}
        try:
            check_access('request_reset', context)
        except NotAuthorized:
            abort(401, _('Unauthorized to request reset password.'))

        if request.method == 'POST':
            id = request.params.get('user')

            context = {'model': model,
                       'user': c.user}

            data_dict = {'id': id}
            user_obj = None
            try:
                user_dict = get_action('user_show')(context, data_dict)
                user_obj = context['user_obj']
            except NotFound:
                # Show success regardless of outcome to prevent scanning
                h.flash_success(_('Please check your inbox for '
                                  'a reset code.'))

            if user_obj:
                try:
                    mailer.send_reset_link(user_obj)
                    h.flash_success(_('Please check your inbox for '
                                      'a reset code.'))
                    h.redirect_to('/')
                except mailer.MailerException, e:
                    h.flash_error(_('Could not send reset link: %s') %
                                  unicode(e))
Beispiel #23
0
    def search(self, ver=None, register=None):

        log.debug("search %s params: %r" % (register, request.params))
        if register == "revision":
            since_time = None
            if "since_id" in request.params:
                id = request.params["since_id"]
                if not id:
                    return self._finish_bad_request(_(u"No revision specified"))
                rev = model.Session.query(model.Revision).get(id)
                if rev is None:
                    return self._finish_not_found(_(u"There is no revision with id: %s") % id)
                since_time = rev.timestamp
            elif "since_time" in request.params:
                since_time_str = request.params["since_time"]
                try:
                    since_time = h.date_str_to_datetime(since_time_str)
                except ValueError, inst:
                    return self._finish_bad_request("ValueError: %s" % inst)
            else:
                return self._finish_bad_request(
                    _("Missing search term ('since_id=UUID' or " + " 'since_time=TIMESTAMP')")
                )
            revs = model.Session.query(model.Revision).filter(model.Revision.timestamp > since_time)
            return self._finish_ok([rev.id for rev in revs])
Beispiel #24
0
def related_update(context, data_dict):
    model = context['model']
    user = context['user']
    if not user:
        return {'success': False,
                'msg': _('Only the owner can update a related item')}

    related = logic_auth.get_related_object(context, data_dict)
    userobj = model.User.get(user)

    if related.datasets:
        package = related.datasets[0]
        pkg_dict = {'id': package.id}
        authorized = _auth_update.package_update(context, pkg_dict).get('success')
        if authorized:
            return {'success': True}

    if not userobj or userobj.id != related.owner_id:
        return {'success': False,
                'msg': _('Only the owner can update a related item')}

    # Only sysadmins can change the featured field.
    if ('featured' in data_dict and data_dict['featured'] != related.featured):
        return {'success': False,
                'msg': _('You must be a sysadmin to change a related item\'s '
                         'featured field.')}

    return {'success': True}
Beispiel #25
0
    def perform_reset(self, id):
        # FIXME 403 error for invalid key is a non helpful page
        # FIXME We should reset the reset key when it is used to prevent
        # reuse of the url
        context = {'model': model, 'session': model.Session,
                   'user': c.user,
                   'keep_sensitive_data': True}

        data_dict = {'id': id}

        try:
            check_access('user_reset', context)
        except NotAuthorized:
            abort(401, _('Unauthorized to reset password.'))

        try:
            user_dict = get_action('user_show')(context, data_dict)

            # Be a little paranoid, and get rid of sensitive data that's
            # not needed.
            user_dict.pop('apikey', None)
            user_dict.pop('reset_key', None)
            user_obj = context['user_obj']
        except NotFound, e:
            abort(404, _('User not found'))
Beispiel #26
0
    def action(self, logic_function, ver=None):
        try:
            function = get_action(logic_function)
        except KeyError:
            log.info('Can\'t find logic function: %s', logic_function)
            return self._finish_bad_request(
                _('Action name not known: %s') % logic_function)

        context = {'model': model, 'session': model.Session, 'user': c.user,
                   'api_version': ver, 'return_type': 'LazyJSONObject',
                   'auth_user_obj': c.userobj}
        model.Session()._context = context

        return_dict = {'help': h.url_for(controller='api',
                                         action='action',
                                         logic_function='help_show',
                                         ver=ver,
                                         name=logic_function,
                                         qualified=True,
                                         )
                       }
        try:
            side_effect_free = getattr(function, 'side_effect_free', False)
            request_data = self._get_request_data(try_url_params=
                                                  side_effect_free)
        except ValueError, inst:
            log.info('Bad Action API request data: %s', inst)
            return self._finish_bad_request(
                _('JSON Error: %s') % inst)
Beispiel #27
0
    def search(self, ver=None, register=None):

        log.debug('search %s params: %r', register, request.params)
        if register == 'revision':
            since_time = None
            if 'since_id' in request.params:
                id = request.params['since_id']
                if not id:
                    return self._finish_bad_request(
                        _(u'No revision specified'))
                rev = model.Session.query(model.Revision).get(id)
                if rev is None:
                    return self._finish_not_found(
                        _(u'There is no revision with id: %s') % id)
                since_time = rev.timestamp
            elif 'since_time' in request.params:
                since_time_str = request.params['since_time']
                try:
                    since_time = h.date_str_to_datetime(since_time_str)
                except ValueError, inst:
                    return self._finish_bad_request('ValueError: %s' % inst)
            else:
                return self._finish_bad_request(
                    _("Missing search term ('since_id=UUID' or " +
                      " 'since_time=TIMESTAMP')"))
            revs = model.Session.query(model.Revision) \
                .filter(model.Revision.timestamp > since_time) \
                .order_by(model.Revision.timestamp) \
                .limit(50)  # reasonable enough for a page
            return self._finish_ok([rev.id for rev in revs])
Beispiel #28
0
    def resource_data(self, id, resource_id):

        if toolkit.request.method == "POST":
            try:
                toolkit.c.pkg_dict = p.toolkit.get_action("datapusher_submit")(None, {"resource_id": resource_id})
            except logic.ValidationError:
                pass

            base.redirect(
                core_helpers.url_for(
                    controller="ckanext.datapusher.plugin:ResourceDataController",
                    action="resource_data",
                    id=id,
                    resource_id=resource_id,
                )
            )

        try:
            toolkit.c.pkg_dict = p.toolkit.get_action("package_show")(None, {"id": id})
            toolkit.c.resource = p.toolkit.get_action("resource_show")(None, {"id": resource_id})
        except logic.NotFound:
            base.abort(404, _("Resource not found"))
        except logic.NotAuthorized:
            base.abort(401, _("Unauthorized to edit this resource"))

        try:
            datapusher_status = p.toolkit.get_action("datapusher_status")(None, {"resource_id": resource_id})
        except logic.NotFound:
            datapusher_status = {}
        except logic.NotAuthorized:
            base.abort(401, _("Not authorized to see this page"))

        return base.render("package/resource_data.html", extra_vars={"status": datapusher_status})
    def resource_read(self, id, resource_id):
        context = {'model': model, 'session': model.Session,
                   'user': c.user or c.author, 'auth_user_obj': c.userobj}

        try:
            c.resource = get_action('resource_show')(context,
                                                     {'id': resource_id})
            c.package = get_action('package_show')(context, {'id': id})
            # required for nav menu
            c.pkg = context['package']
            c.pkg_dict = c.package
        except NotFound:
            abort(404, _('Resource not found'))
        except NotAuthorized:
            abort(401, _('Unauthorized to read resource %s') % id)
        # get package license info
        license_id = c.package.get('license_id')
        try:
            c.package['isopen'] = model.Package.\
                get_license_register()[license_id].isopen()
        except KeyError:
            c.package['isopen'] = False

        # TODO: find a nicer way of doing this
        c.datastore_api = '%s/api/action' % config.get('ckan.site_url', '').rstrip('/')

        c.related_count = c.pkg.related_count

        c.resource['can_be_previewed'] = self._resource_preview(
            {'resource': c.resource, 'package': c.package})
        return render('package/resource_read.html')        
Beispiel #30
0
def user_name_validator(key, data, errors, context):
    '''Validate a new user name.

    Append an error message to ``errors[key]`` if a user named ``data[key]``
    already exists. Otherwise, do nothing.

    :raises ckan.lib.navl.dictization_functions.Invalid: if ``data[key]`` is
        not a string
    :rtype: None

    '''
    model = context['model']
    new_user_name = data[key]

    if not isinstance(new_user_name, basestring):
        raise Invalid(_('User names must be strings'))

    user = model.User.get(new_user_name)
    if user is not None:
        # A user with new_user_name already exists in the database.

        user_obj_from_context = context.get('user_obj')
        if user_obj_from_context and user_obj_from_context.id == user.id:
            # If there's a user_obj in context with the same id as the user
            # found in the db, then we must be doing a user_update and not
            # updating the user name, so don't return an error.
            return
        else:
            # Otherwise return an error: there's already another user with that
            # name, so you can create a new user with that name or update an
            # existing user's name to that name.
            errors[key].append(_('That login name is not available.'))
Beispiel #31
0
def _finish_not_authz(extra_msg=None):
    response_data = _(u'Access denied')
    if extra_msg:
        response_data = u'%s - %s' % (response_data, extra_msg)
    return _finish(403, response_data, u'json')
Beispiel #32
0
def before_request():
    try:
        context = dict(model=model, user=g.user, auth_user_obj=g.userobj)
        logic.check_access(u'sysadmin', context)
    except logic.NotAuthorized:
        base.abort(403, _(u'Need to be system administrator to administer'))
Beispiel #33
0
def download(context,
             resource,
             url_timeout=30,
             max_content_length='default',
             method='GET'):
    '''Given a resource, tries to download it.

    Params:
      resource - dict of the resource

    Exceptions from tidy_url may be propagated:
       LinkInvalidError if the URL is invalid

    If there is an error performing the download, raises:
       DownloadException - connection problems etc.
       DownloadError - HTTP status code is an error or 0 length

    If download is not suitable (e.g. too large), raises:
       ChooseNotToDownload

    If the basic GET fails then it will try it with common API
    parameters (SPARQL, WMS etc) to get a better response.

    Returns a dict of results of a successful download:
      mimetype, size, hash, headers, saved_file, url_redirected_to
    '''
    from ckanext.archiver import default_settings as settings
    from pylons import config

    if max_content_length == 'default':
        max_content_length = settings.MAX_CONTENT_LENGTH

    url = resource['url']
    url = tidy_url(url)

    if (resource.get('url_type') == 'upload' and not url.startswith('http')):
        url = context['site_url'].rstrip('/') + url

    hosted_externally = not url.startswith(config['ckan.site_url'])
    if resource.get('url_type') == 'upload' and hosted_externally:
        # ckanext-cloudstorage for example does that

        # enable ckanext-archiver.archive_cloud for qa to work on cloud resources
        # till https://github.com/ckan/ckanext-qa/issues/48 is resolved
        # Warning: this will result in double storage of all files below archival filesize limit

        if not config.get('ckanext-archiver.archive_cloud', False):
            raise ChooseNotToDownload(
                'Skipping resource hosted externally to download resource: %s'
                % url, url)

    headers = _set_user_agent_string({})

    # start the download - just get the headers
    # May raise DownloadException
    method_func = {'GET': requests.get, 'POST': requests.post}[method]
    res = requests_wrapper(
        log,
        method_func,
        url,
        timeout=url_timeout,
        stream=True,
        headers=headers,
        verify=verify_https(),
    )
    url_redirected_to = res.url if url != res.url else None

    if context.get('previous') and ('etag' in res.headers):
        if context.get('previous').etag == res.headers['etag']:
            log.info("ETAG matches, not downloading content")
            raise NotChanged("etag suggests content has not changed")

    if not res.ok:  # i.e. 404 or something
        raise DownloadError(
            'Server reported status error: %s %s' %
            (res.status_code, res.reason), url_redirected_to)
    log.info('GET started successfully. Content headers: %r', res.headers)

    # record headers
    mimetype = _clean_content_type(res.headers.get('content-type', '').lower())

    # make sure resource content-length does not exceed our maximum
    content_length = res.headers.get('content-length')

    if content_length:
        try:
            content_length = int(content_length)
        except ValueError:
            # if there are multiple Content-Length headers, requests
            # will return all the values, comma separated
            if ',' in content_length:
                try:
                    content_length = int(content_length.split(',')[0])
                except ValueError:
                    pass
    if isinstance(content_length, int) and \
       int(content_length) >= max_content_length:
        # record fact that resource is too large to archive
        log.warning(
            'Resource too large to download: %s > max (%s). '
            'Resource: %s %r', content_length, max_content_length,
            resource['id'], url)
        raise ChooseNotToDownload(
            _('Content-length %s exceeds maximum '
              'allowed value %s') % (content_length, max_content_length),
            url_redirected_to)
    # content_length in the headers is useful but can be unreliable, so when we
    # download, we will monitor it doesn't go over the max.

    # continue the download - stream the response body
    def get_content():
        return res.content

    log.info('Downloading the body')
    content = requests_wrapper(log, get_content)

    # APIs can return status 200, but contain an error message in the body
    if response_is_an_api_error(content):
        raise DownloadError(
            _('Server content contained an API error message: %s') %
            content[:250], url_redirected_to)

    content_length = len(content)
    if content_length > max_content_length:
        raise ChooseNotToDownload(
            _("Content-length %s exceeds maximum allowed value %s") %
            (content_length, max_content_length), url_redirected_to)

    log.info('Saving resource')
    try:
        length, hash, saved_file_path = _save_resource(resource, res,
                                                       max_content_length)
    except ChooseNotToDownload, e:
        raise ChooseNotToDownload(str(e), url_redirected_to)
Beispiel #34
0
    log.info('Saving resource')
    try:
        length, hash, saved_file_path = _save_resource(resource, res,
                                                       max_content_length)
    except ChooseNotToDownload, e:
        raise ChooseNotToDownload(str(e), url_redirected_to)
    log.info('Resource saved. Length: %s File: %s', length, saved_file_path)

    # zero length (or just one byte) indicates a problem
    if length < 2:
        # record fact that resource is zero length
        log.warning(
            'Resource found was length %i - not archiving. Resource: %s %r',
            length, resource['id'], url)
        raise DownloadError(
            _("Content-length after streaming was %i") % length,
            url_redirected_to)

    log.info(
        'Resource downloaded: id=%s url=%r cache_filename=%s length=%s hash=%s',
        resource['id'], url, saved_file_path, length, hash)

    return {
        'mimetype': mimetype,
        'size': length,
        'hash': hash,
        'headers': dict(res.headers),
        'saved_file': saved_file_path,
        'url_redirected_to': url_redirected_to,
        'request_type': method
    }
Beispiel #35
0
def action(logic_function, ver=API_DEFAULT_VERSION):
    u'''Main endpoint for the action API (v3)

    Creates a dict with the incoming request data and calls the appropiate
    logic function. Returns a JSON response with the following keys:

        * ``help``: A URL to the docstring for the specified action
        * ``success``: A boolean indicating if the request was successful or
                an exception was raised
        * ``result``: The output of the action, generally an Object or an Array
    '''

    # Check if action exists
    try:
        function = get_action(logic_function)
    except KeyError:
        msg = u'Action name not known: {0}'.format(logic_function)
        log.info(msg)
        return _finish_bad_request(msg)

    context = {
        u'model': model,
        u'session': model.Session,
        u'user': g.user,
        u'api_version': ver,
        u'auth_user_obj': g.userobj
    }
    model.Session()._context = context

    return_dict = {
        u'help':
        url_for(
            u'api.action',
            logic_function=u'help_show',
            ver=ver,
            name=logic_function,
            _external=True,
        )
    }

    # Get the request data
    try:
        side_effect_free = getattr(function, u'side_effect_free', False)

        request_data = _get_request_data(try_url_params=side_effect_free)
    except ValueError as inst:
        log.info(u'Bad Action API request data: %s', inst)
        return _finish_bad_request(_(u'JSON Error: %s') % inst)
    if not isinstance(request_data, dict):
        # this occurs if request_data is blank
        log.info(u'Bad Action API request data - not dict: %r', request_data)
        return _finish_bad_request(
            _(u'Bad request data: %s') %
            u'Request data JSON decoded to %r but '
            u'it needs to be a dictionary.' % request_data)
    if u'callback' in request_data:
        del request_data[u'callback']
        g.user = None
        g.userobj = None
        context[u'user'] = None
        context[u'auth_user_obj'] = None

    # Call the action function, catch any exception
    try:
        result = function(context, request_data)
        return_dict[u'success'] = True
        return_dict[u'result'] = result
    except DataError as e:
        log.info(u'Format incorrect (Action API): %s - %s', e.error,
                 request_data)
        return_dict[u'error'] = {
            u'__type': u'Integrity Error',
            u'message': e.error,
            u'data': request_data
        }
        return_dict[u'success'] = False
        return _finish(400, return_dict, content_type=u'json')
    except NotAuthorized as e:
        return_dict[u'error'] = {
            u'__type': u'Authorization Error',
            u'message': _(u'Access denied')
        }
        return_dict[u'success'] = False

        if text_type(e):
            return_dict[u'error'][u'message'] += u': %s' % e

        return _finish(403, return_dict, content_type=u'json')
    except NotFound as e:
        return_dict[u'error'] = {
            u'__type': u'Not Found Error',
            u'message': _(u'Not found')
        }
        if text_type(e):
            return_dict[u'error'][u'message'] += u': %s' % e
        return_dict[u'success'] = False
        return _finish(404, return_dict, content_type=u'json')
    except ValidationError as e:
        error_dict = e.error_dict
        error_dict[u'__type'] = u'Validation Error'
        return_dict[u'error'] = error_dict
        return_dict[u'success'] = False
        # CS nasty_string ignore
        log.info(u'Validation error (Action API): %r', str(e.error_dict))
        return _finish(409, return_dict, content_type=u'json')
    except SearchQueryError as e:
        return_dict[u'error'] = {
            u'__type': u'Search Query Error',
            u'message': u'Search Query is invalid: %r' % e.args
        }
        return_dict[u'success'] = False
        return _finish(400, return_dict, content_type=u'json')
    except SearchError as e:
        return_dict[u'error'] = {
            u'__type': u'Search Error',
            u'message': u'Search error: %r' % e.args
        }
        return_dict[u'success'] = False
        return _finish(409, return_dict, content_type=u'json')
    except SearchIndexError as e:
        return_dict[u'error'] = {
            u'__type': u'Search Index Error',
            u'message': u'Unable to add package to search index: %s' % str(e)
        }
        return_dict[u'success'] = False
        return _finish(500, return_dict, content_type=u'json')

    return _finish_ok(return_dict)
Beispiel #36
0
def _finish_bad_request(extra_msg=None):
    response_data = _(u'Bad request')
    if extra_msg:
        response_data = u'%s - %s' % (response_data, extra_msg)
    return _finish(400, response_data, u'json')
Beispiel #37
0
def _finish_not_found(extra_msg=None):
    response_data = _(u'Not found')
    if extra_msg:
        response_data = u'%s - %s' % (response_data, extra_msg)
    return _finish(404, response_data, u'json')
Beispiel #38
0
def natural_number_validator(value, context):
    value = int_validator(value, context)
    if value < 0:
        raise Invalid(_('Must be a natural number'))
    return value
Beispiel #39
0
def role_exists(role, context):
    if role not in authz.ROLE_PERMISSIONS:
        raise Invalid(_('role does not exist.'))
    return role
Beispiel #40
0
def is_positive_integer(value, context):
    value = int_validator(value, context)
    if value < 1:
        raise Invalid(_('Must be a postive integer'))
    return value
Beispiel #41
0
def user_about_validator(value,context):
    if 'http://' in value or 'https://' in value:
        raise Invalid(_('Edit not allowed as it looks like spam. Please avoid links in your description.'))

    return value
Beispiel #42
0
def extra_key_not_in_root_schema(key, data, errors, context):

    for schema_key in context.get('schema_keys', []):
        if schema_key == data[key]:
            raise Invalid(_('There is a schema field with the same name'))
Beispiel #43
0
def package_version_validator(value, context):

    if len(value) > PACKAGE_VERSION_MAX_LENGTH:
        raise Invalid(_('Version must be a maximum of %i characters long') % \
                      PACKAGE_VERSION_MAX_LENGTH)
    return value
Beispiel #44
0
def vocabulary_id_not_changed(value, context):
    vocabulary = context.get('vocabulary')
    if vocabulary and value != vocabulary.id:
        raise Invalid(_('Cannot change value of key from %s to %s. '
                        'This key is read-only') % (vocabulary.id, value))
    return value
Beispiel #45
0
    def querytool_public_read(self, name):
        '''
        :return: base template
        '''
        querytool = _get_action('querytool_public_read', {'name': name})

        if not querytool:
            abort(404, _('Application not found.'))

        # only sysadmins or organization members can access private querytool
        if querytool['private'] is True:
            context = _get_context()
            try:
                check_access('querytool_show', context, {'name': name})
            except NotAuthorized:
                abort(403, _('Not authorized to see this page'))

        # Check if the data for this querytool still exists
        if querytool['dataset_name']:
            try:
                _get_action('package_show', {'id': querytool['dataset_name']})
            except NotFound:
                abort(
                    404,
                    _('The data used for creating this '
                      'Application has been removed '
                      'by the administrator.'))

        if not querytool['visualizations']:
            abort(404, _('Application not fully set.'))

        params = toolkit.request.params

        querytools = []
        items = []
        items.append({u'order': 0, u'name': querytool['name']})

        if querytool['related_querytools']:
            items.extend(json.loads(querytool['related_querytools']))

        items.sort(key=itemgetter('order'))
        for item in items:

            q_item = _get_action('querytool_public_read',
                                 {'name': item['name']})
            if q_item:
                q_item['visualizations'] = json.loads(q_item['visualizations'])
                q_item['visualizations'].sort(key=itemgetter('order'))

                q_name = q_item['name']
                new_filters = json.loads(q_item['filters'])

                for k, v in params.items():
                    # Update query filters
                    if k.startswith('{}_data_filter_name_'.format(q_name)):
                        id = k.split('_')[-1]
                        for filter in new_filters:
                            # Apply changes only on public filters
                            # to protect changing private
                            # filters by changing the url query params
                            if filter['visibility'] == 'public':
                                if v == filter.get('name'):
                                    filter['value'] = \
                                        params.get('{}_data_filter_value_{}'
                                                   .format(q_name, id))
                                # Replace & with %26 to fix the error for graphs
                                # not being generated for values with & in them
                                filter['value'] = filter['value'].replace(
                                    '&', '%26')
                    # Update charts y_axis value
                    if k.startswith('{}_y_axis_column'.format(q_name)):
                        q_item['y_axis_column'] = v
                    # Update visualizations filters
                    if k.startswith('{}_viz_filter_name'.format(q_name)):
                        id = k.split('_')[-1]
                        for visualization in q_item['visualizations']:
                            if visualization['order'] == int(id):
                                visualization['filter_name'] = \
                                    params.get('{}_viz_filter_name_{}'.
                                               format(q_name, id))
                                visualization['filter_value'] = \
                                    params.get('{}_viz_filter_value_{}'.
                                               format(q_name, id))

                for image in q_item['visualizations']:
                    if image['type'] == 'image':
                        is_upload = image[
                            'url'] and not image['url'].startswith('http')

                        if is_upload:
                            image['url'] = '{0}/uploads/vs/{1}'.format(
                                toolkit.request.host_url, image['url'])

                related_sql_string = helpers.create_query_str(
                    q_item.get('chart_resource'), new_filters)
                q_item['public_filters'] = new_filters
                q_item['public_filters'].sort(key=itemgetter('order'))
                q_item['sql_string'] = related_sql_string

                # Add slug to filters
                for filter in new_filters:
                    filter['slug'] = helpers.slugify(filter.get('alias', ''))

                # Need this hack for chart filter
                q_item['public_main_filters'] = json.dumps(new_filters)

                querytools.append(q_item)

        return render('querytool/public/read.html',
                      extra_vars={'querytools': querytools})
Beispiel #46
0
def tag_not_uppercase(value, context):

    tagname_uppercase = re.compile('[A-Z]')
    if tagname_uppercase.search(value):
        raise Invalid(_('Tag "%s" must not be uppercase' % (value)))
    return value
Beispiel #47
0
    def querytool_edit(self,
                       querytool=None,
                       data=None,
                       errors=None,
                       error_summary=None):
        '''
            Create/edit query tool

        :return: query create/edit template page

        '''
        if querytool:
            querytool = querytool[1:]

        data_dict = {'name': querytool}

        context = _get_context()
        try:
            check_access('querytool_update', context, data_dict)
        except NotAuthorized:
            abort(403, _('Not authorized to see this page'))

        _querytool = _get_action('querytool_get', data_dict)

        if _querytool is None and len(querytool) > 0:
            abort(404, _('Application not found.'))

        if _querytool is None:
            _querytool = {}

        # Check if the data for this querytool still exists
        if 'dataset_name' in _querytool.keys():
            try:
                _get_action('package_show', {'id': _querytool['dataset_name']})
            except NotFound:
                abort(
                    404,
                    _('The data used for creating this '
                      'Application has been removed '
                      'by the administrator.'))

        if toolkit.request.method == 'POST' and not data:

            data = dict(toolkit.request.POST)
            filters = []
            y_axis_columns = []
            related_querytools = []
            for k, v in data.items():

                if k.startswith('data_filter_name_'):
                    filter = {}
                    id = k.split('_')[-1]
                    filter['order'] = int(id)
                    filter['name'] = data['data_filter_name_{}'.format(id)]
                    filter['value'] = data['data_filter_value_{}'.format(id)]
                    # Replace & with %26 to fix the error for graphs
                    # not being generated for values with & in them
                    filter['value'] = filter['value'].replace('&', '%26')
                    filter['alias'] = data['data_filter_alias_{}'.format(id)]
                    filter['visibility'] = \
                        data['data_filter_visibility_{}'.format(id)]
                    filter['filter_color'] = \
                        data['data_filter_color_{}'.format(id)]

                    filters.append(filter)
                elif k.startswith('y_axis_name_'):
                    id = k.split('_')[-1]
                    alias = data.get('y_axis_alias_%s' % id, '')
                    y_axis_columns.append({'name': v, 'alias': alias})

                elif k.startswith('related_querytool_'):
                    related_querytool = {}
                    id = k.split('_')[-1]
                    related_querytool['order'] = int(id)
                    related_querytool['name'] = \
                        data['related_querytool_{}'.format(id)]
                    related_querytools.append(related_querytool)

            if any(filters):
                _querytool['filters'] = json.dumps(filters)
                sql_string = helpers.create_query_str(data['chart_resource'],
                                                      filters)
            else:
                _querytool['filters'] = ''
                sql_string = ''

            if 'private' not in data.keys():
                _querytool['private'] = True

            if any(related_querytools):
                _querytool['related_querytools'] = json.\
                    dumps(related_querytools)
            else:
                _querytool['related_querytools'] = ''

            _querytool.update(data)
            _querytool['querytool'] = querytool
            _querytool['sql_string'] = sql_string
            _querytool['y_axis_columns'] = (json.dumps(y_axis_columns)
                                            if y_axis_columns else '')
            _querytool['owner_org'] = data['owner_org']
            try:
                junk = _get_action('querytool_update', _querytool)
                h.flash_success(_('Data Successfully updated.'))
            except ValidationError, e:
                errors = e.error_dict
                error_summary = e.error_summary
                return self.querytool_edit('/' + querytool, _querytool, errors,
                                           error_summary)
            if 'save_data' in data.keys():
                # redirect to querytools group
                url = h.url_for('querytool_list_by_group',
                                group=_querytool['group'])
            else:
                # redirect to manage visualisations
                url = h.url_for('querytool_edit_visualizations',
                                querytool='/' + _querytool['name'])
            h.redirect_to(url)
Beispiel #48
0
def resource_id_exists(value, context):
    model = context['model']
    session = context['session']
    if not session.query(model.Resource).get(value):
        raise Invalid('%s: %s' % (_('Not found'), _('Resource')))
    return value
Beispiel #49
0
def resource_update(context, data_dict):
    '''Update a resource.

    To update a resource you must be authorized to update the dataset that the
    resource belongs to.

    For further parameters see
    :py:func:`~ckan.logic.action.create.resource_create`.

    :param id: the id of the resource to update
    :type id: string

    :returns: the updated resource
    :rtype: string

    '''
    model = context['model']
    user = context['user']
    id = _get_or_bust(data_dict, "id")
    if not data_dict.get('url'):
        data_dict['url'] = ''

    resource = model.Resource.get(id)
    context["resource"] = resource

    if not resource:
        log.debug('Could not find resource %s', id)
        raise NotFound(_('Resource was not found.'))

    _check_access('resource_update', context, data_dict)
    del context["resource"]

    package_id = resource.package.id
    pkg_dict = _get_action('package_show')(dict(context, return_type='dict'), {
        'id': package_id
    })

    for n, p in enumerate(pkg_dict['resources']):
        if p['id'] == id:
            break
    else:
        log.error('Could not find resource %s after all', id)
        raise NotFound(_('Resource was not found.'))

    # Persist the datastore_active extra if already present and not provided
    if ('datastore_active' in resource.extras
            and 'datastore_active' not in data_dict):
        data_dict['datastore_active'] = resource.extras['datastore_active']

    for plugin in plugins.PluginImplementations(plugins.IResourceController):
        plugin.before_update(context, pkg_dict['resources'][n], data_dict)

    upload = uploader.get_resource_uploader(data_dict)

    if 'mimetype' not in data_dict:
        if hasattr(upload, 'mimetype'):
            data_dict['mimetype'] = upload.mimetype

    if 'size' not in data_dict and 'url_type' in data_dict:
        if hasattr(upload, 'filesize'):
            data_dict['size'] = upload.filesize

    pkg_dict['resources'][n] = data_dict

    try:
        context['defer_commit'] = True
        context['use_cache'] = False
        updated_pkg_dict = _get_action('package_update')(context, pkg_dict)
        context.pop('defer_commit')
    except ValidationError, e:
        errors = e.error_dict['resources'][n]
        raise ValidationError(errors)
Beispiel #50
0
    def edit_visualizations(self,
                            querytool=None,
                            data=None,
                            errors=None,
                            error_summary=None):
        '''
            Create or edit visualizations for the querytool

        :return: query edit template page
        '''
        if querytool:
            querytool = querytool[1:]

        data_dict = {'name': querytool}

        context = _get_context()

        try:
            check_access('querytool_update', context, data_dict)
        except NotAuthorized:
            abort(403, _('Not authorized to see this page'))

        _querytool = _get_action('querytool_get', data_dict)

        if _querytool is None and len(querytool) > 0:
            abort(404, _('Application not found.'))

        # Check if the data for this querytool still exists
        if _querytool['dataset_name']:
            try:
                _get_action('package_show', {'id': _querytool['dataset_name']})
            except NotFound:
                abort(
                    404,
                    _('The data used for creating this '
                      'Application has been removed by '
                      'the administrator.'))

        _visualization_items = \
            _get_action('querytool_get_visualizations', data_dict)

        if _visualization_items is None:
            _visualization_items = {'name': querytool}

        if toolkit.request.method == 'POST' and not data:
            data = dict(toolkit.request.POST)
            visualizations = []
            text_boxes = []
            images = []
            maps = []
            tables = []
            for k, v in data.items():
                '''
                TODO: save visualizations with key value e.g {'charts' :[]
                # 'images': []} for easier itteration
                '''
                if k.startswith('chart_field_graph_'):
                    visualization = {}
                    id = k.split('_')[-1]
                    visualization['type'] = 'chart'
                    visualization['order'] = int(id)
                    visualization['graph'] = \
                        data.get('chart_field_graph_{}'.format(id))
                    visualization['x_axis'] = \
                        data.get('chart_field_axis_x_{}'.format(id))
                    visualization['y_axis'] = \
                        data.get('chart_field_axis_y_{}'.format(id))
                    visualization['color'] = \
                        data.get('chart_field_color_{}'.format(id))
                    visualization['title'] = \
                        data.get('chart_field_title_{}'.format(id))
                    visualization['x_text_rotate'] = \
                        data.get('chart_field_x_text_rotate_{}'.format(id))
                    visualization['tooltip_name'] = \
                        data.get('chart_field_tooltip_name_{}'.format(id))
                    visualization['data_format'] = \
                        data.get('chart_field_data_format_{}'.format(id))
                    visualization['y_tick_format'] = \
                        data.get('chart_field_y_ticks_format_{}'.format(id))
                    visualization['padding_bottom'] = \
                        data.get('chart_field_padding_bottom_{}'.format(id))
                    visualization['padding_top'] = \
                        data.get('chart_field_padding_top_{}'.format(id))
                    visualization['tick_count'] = \
                        data.get('chart_field_tick_count_{}'.format(id))
                    visualization['y_label'] = \
                        data.get('chart_field_y_label_{}'.format(id))
                    visualization['size'] = \
                        data.get('chart_field_size_{}'.format(id))
                    visualization['chart_padding_left'] = \
                        data.get('chart_field_chart_padding_left_{}'.format(id))
                    visualization['chart_padding_bottom'] = \
                        data.get('chart_field_chart_padding_bottom_{}'.format(id))
                    visualization['static_reference_columns'] = \
                        toolkit.request.POST.getall(
                            'chart_field_static_reference_columns_%s' % id)
                    visualization['static_reference_label'] = \
                        data.get('chart_field_static_reference_label_%s' % id)
                    visualization['dynamic_reference_type'] = \
                        data.get('chart_field_dynamic_reference_type_%s' % id)
                    visualization['dynamic_reference_factor'] = \
                        data.get('chart_field_dynamic_reference_factor_%s' % id)
                    visualization['dynamic_reference_label'] = \
                        data.get('chart_field_dynamic_reference_label_%s' % id)
                    visualization['sort'] = \
                        data.get('chart_field_sort_{}'.format(id))
                    if 'chart_field_x_text_multiline_{}'.format(id) in data:
                        visualization['x_text_multiline'] = 'true'
                    else:
                        visualization['x_text_multiline'] = 'false'
                    visualization['x_tick_culling_max'] = \
                        data.get('chart_field_x_tick_culling_max_{}'.format(id))
                    if 'chart_field_legend_{}'.format(id) in data:
                        visualization['show_legend'] = 'true'
                    else:
                        visualization['show_legend'] = 'false'
                    if 'chart_field_labels_{}'.format(id) in data:
                        visualization['show_labels'] = 'true'
                    else:
                        visualization['show_labels'] = 'false'
                    if 'chart_field_y_label_hide_{}'.format(id) in data:
                        visualization['y_label_hide'] = 'true'
                    else:
                        visualization['y_label_hide'] = 'false'
                    if 'chart_field_show_labels_as_percentages_{}'.format(
                            id) in data:
                        visualization['show_labels_as_percentages'] = 'true'
                    else:
                        visualization['show_labels_as_percentages'] = 'false'
                    if 'chart_field_y_from_zero_{}'.format(id) in data:
                        visualization['y_from_zero'] = 'true'
                    else:
                        visualization['y_from_zero'] = 'false'
                    if data['chart_field_filter_name_{}'.format(id)]:
                        visualization['filter_name'] = \
                            data['chart_field_filter_name_{}'.format(id)]
                        visualization['filter_value'] = \
                            data['chart_field_filter_value_{}'.format(id)]
                        visualization['filter_alias'] = \
                            data['chart_field_filter_alias_{}'.format(id)]
                        visualization['filter_visibility'] = \
                            data['chart_field_filter_visibility_{}'.format(id)]
                    else:
                        visualization['filter_name'] = ''
                        visualization['filter_value'] = ''
                        visualization['filter_alias'] = ''
                        visualization['filter_visibility'] = ''

                    if 'chart_field_category_name_{}'.format(id) in data:
                        visualization['category_name'] = \
                                data['chart_field_category_name_{}'.format(id)]
                    else:
                        visualization['category_name'] = ''

                    visualizations.append(visualization)

                if k.startswith('text_box_description_'):
                    text_box = {}
                    id = k.split('_')[-1]
                    text_box['type'] = 'text_box'
                    text_box['order'] = int(id)
                    text_box['description'] = \
                        data['text_box_description_{}'.format(id)]
                    text_box['size'] = \
                        data['text_box_size_{}'.format(id)]

                    text_boxes.append(text_box)

                if k.startswith('image_field_size_'):
                    image = {}
                    id = k.split('_')[-1]
                    image['type'] = 'image'
                    image['order'] = int(id)

                    image_url = data['media_image_url_{}'.format(id)]

                    if h.uploads_enabled():
                        image_upload = data['media_image_upload_{}'.format(id)]
                        if isinstance(image_upload, cgi.FieldStorage):
                            upload = uploader.get_uploader('vs', image_url)
                            upload.update_data_dict(
                                data, 'media_image_url_{}'.format(id),
                                'media_image_upload_{}'.format(id), 'False')
                            upload.upload(uploader)
                            image_url = upload.filename

                        image['url'] = image_url

                    image['size'] = \
                        data['image_field_size_{}'.format(id)]

                    images.append(image)

                if k.startswith('map_resource_'):
                    map_item = {}
                    id = k.split('_')[-1]
                    map_item['type'] = 'map'
                    map_item['order'] = int(id)
                    map_item['map_resource'] = \
                        data['map_resource_{}'.format(id)]
                    map_item['map_title_field'] = \
                        data['map_title_field_{}'.format(id)]
                    map_item['map_key_field'] = \
                        data['map_key_field_{}'.format(id)]
                    map_item['data_key_field'] = \
                        data['map_data_key_field_{}'.format(id)]
                    map_item['map_color_scheme'] = \
                        data['map_color_scheme_{}'.format(id)]
                    map_item['size'] = \
                        data['map_size_{}'.format(id)]

                    if data['map_field_filter_name_{}'.format(id)]:
                        map_item['filter_name'] = \
                            data['map_field_filter_name_{}'.format(id)]
                        map_item['filter_value'] = \
                            data['map_field_filter_value_{}'.format(id)]
                        map_item['filter_alias'] = \
                            data['map_field_filter_alias_{}'.format(id)]
                        map_item['filter_visibility'] = \
                            data['map_field_filter_visibility_{}'.format(id)]
                    else:
                        map_item['filter_name'] = ''
                        map_item['filter_value'] = ''
                        map_item['filter_alias'] = ''
                        map_item['filter_visibility'] = ''

                    maps.append(map_item)

                if k.startswith('table_size_'):
                    table_item = {}
                    id = k.split('_')[-1]
                    table_item['type'] = 'table'
                    table_item['order'] = int(id)
                    table_item['y_axis'] = \
                        data['choose_y_axis_column']
                    table_item['size'] = \
                        data['table_size_{}'.format(id)]
                    table_item['main_value'] = \
                        data['table_main_value_{}'.format(id)]
                    table_item['title'] = \
                        data['table_field_title_{}'.format(id)]
                    table_item['data_format'] = \
                        data['table_data_format_{}'.format(id)]
                    if data['table_field_filter_name_{}'.format(id)]:
                        table_item['filter_name'] = \
                            data['table_field_filter_name_{}'.format(id)]
                        table_item['filter_value'] = \
                            data['table_field_filter_value_{}'.format(id)]
                        table_item['filter_alias'] = \
                            data['table_field_filter_alias_{}'.format(id)]
                        table_item['filter_visibility'] = \
                            data['table_field_filter_visibility_{}'.format(id)]
                    else:
                        table_item['filter_name'] = ''
                        table_item['filter_value'] = ''
                        table_item['filter_alias'] = ''
                        table_item['filter_visibility'] = ''

                    if data['table_category_name_{}'.format(id)]:
                        table_item['category_name'] = \
                                data['table_category_name_{}'.format(id)]
                    else:
                        table_item['category_name'] = ''

                    tables.append(table_item)

            vis = visualizations + text_boxes + images + maps + tables
            _visualization_items['visualizations'] = json.dumps(vis)

            if 'choose_y_axis_column' in data:
                _visualization_items['y_axis_column'] = \
                    data['choose_y_axis_column']
            else:
                _visualization_items['y_axis_column'] = ''

            try:
                junk = _get_action('querytool_visualizations_update',
                                   _visualization_items)
                h.flash_success(_('Visualizations Successfully updated.'))
            except ValidationError, e:
                errors = e.error_dict
                error_summary = e.error_summary
                return self.querytool_edit('/' + querytool, data, errors,
                                           error_summary)

            if 'save-edit-data' in data.keys():
                # redirect to edit data
                url = h.url_for('querytool_edit',
                                querytool='/' + _querytool['name'])
            else:
                # redirect to querytools group
                url = h.url_for('querytool_list_by_group',
                                group=_querytool['group'])
            h.redirect_to(url)
Beispiel #51
0
def package_owner_org_update(context, data_dict):
    '''Update the owning organization of a dataset

    :param id: the name or id of the dataset to update
    :type id: string

    :param organization_id: the name or id of the owning organization
    :type id: string
    '''
    model = context['model']
    user = context['user']
    name_or_id = data_dict.get('id')
    organization_id = data_dict.get('organization_id')

    _check_access('package_owner_org_update', context, data_dict)

    pkg = model.Package.get(name_or_id)
    if pkg is None:
        raise NotFound(_('Package was not found.'))
    if organization_id:
        org = model.Group.get(organization_id)
        if org is None or not org.is_organization:
            raise NotFound(_('Organization was not found.'))

        # FIXME check we are in that org
        pkg.owner_org = org.id
    else:
        org = None
        pkg.owner_org = None

    if context.get('add_revision', True):
        rev = model.repo.new_revision()
        rev.author = user
        if 'message' in context:
            rev.message = context['message']
        else:
            rev.message = _(u'REST API: Update object %s') % pkg.get("name")

    members = model.Session.query(model.Member) \
        .filter(model.Member.table_id == pkg.id) \
        .filter(model.Member.capacity == 'organization')

    need_update = True
    for member_obj in members:
        if org and member_obj.group_id == org.id:
            need_update = False
        else:
            member_obj.state = 'deleted'
            member_obj.save()

    # add the organization to member table
    if org and need_update:
        member_obj = model.Member(table_id=pkg.id,
                                  table_name='package',
                                  group=org,
                                  capacity='organization',
                                  group_id=org.id,
                                  state='active')
        model.Session.add(member_obj)

    if not context.get('defer_commit'):
        model.Session.commit()
Beispiel #52
0
def _group_or_org_update(context, data_dict, is_org=False):
    model = context['model']
    user = context['user']
    session = context['session']
    id = _get_or_bust(data_dict, 'id')

    group = model.Group.get(id)
    context["group"] = group
    if group is None:
        raise NotFound('Group was not found.')

    data_dict['type'] = group.type

    # get the schema
    group_plugin = lib_plugins.lookup_group_plugin(group.type)
    try:
        schema = group_plugin.form_to_db_schema_options({
            'type':
            'update',
            'api':
            'api_version' in context,
            'context':
            context
        })
    except AttributeError:
        schema = group_plugin.form_to_db_schema()

    upload = uploader.get_uploader('group', group.image_url)
    upload.update_data_dict(data_dict, 'image_url', 'image_upload',
                            'clear_upload')

    if is_org:
        _check_access('organization_update', context, data_dict)
    else:
        _check_access('group_update', context, data_dict)

    if 'api_version' not in context:
        # old plugins do not support passing the schema so we need
        # to ensure they still work
        try:
            group_plugin.check_data_dict(data_dict, schema)
        except TypeError:
            group_plugin.check_data_dict(data_dict)

    data, errors = lib_plugins.plugin_validate(
        group_plugin, context, data_dict, schema,
        'organization_update' if is_org else 'group_update')
    log.debug('group_update validate_errs=%r user=%s group=%s data_dict=%r',
              errors, context.get('user'),
              context.get('group').name if context.get('group') else '',
              data_dict)

    if errors:
        session.rollback()
        raise ValidationError(errors)

    rev = model.repo.new_revision()
    rev.author = user

    if 'message' in context:
        rev.message = context['message']
    else:
        rev.message = _(u'REST API: Update object %s') % data.get("name")

    group = model_save.group_dict_save(data,
                                       context,
                                       prevent_packages_update=is_org)

    if is_org:
        plugin_type = plugins.IOrganizationController
    else:
        plugin_type = plugins.IGroupController

    for item in plugins.PluginImplementations(plugin_type):
        item.edit(group)

    if is_org:
        activity_type = 'changed organization'
    else:
        activity_type = 'changed group'

    activity_dict = {
        'user_id': model.User.by_name(user.decode('utf8')).id,
        'object_id': group.id,
        'activity_type': activity_type,
    }
    # Handle 'deleted' groups.
    # When the user marks a group as deleted this comes through here as
    # a 'changed' group activity. We detect this and change it to a 'deleted'
    # activity.
    if group.state == u'deleted':
        if session.query(ckan.model.Activity).filter_by(
                object_id=group.id, activity_type='deleted').all():
            # A 'deleted group' activity for this group has already been
            # emitted.
            # FIXME: What if the group was deleted and then activated again?
            activity_dict = None
        else:
            # We will emit a 'deleted group' activity.
            activity_dict['activity_type'] = 'deleted group'
    if activity_dict is not None:
        activity_dict['data'] = {
            'group': dictization.table_dictize(group, context)
        }
        activity_create_context = {
            'model': model,
            'user': user,
            'defer_commit': True,
            'ignore_auth': True,
            'session': session
        }
        _get_action('activity_create')(activity_create_context, activity_dict)
        # TODO: Also create an activity detail recording what exactly changed
        # in the group.

    upload.upload(uploader.get_max_image_size())

    if not context.get('defer_commit'):
        model.repo.commit()

    return model_dictize.group_dictize(group, context)
    def bulk_member_new(self, id):

        try:
            req_dict = clean_dict(
                dict_fns.unflatten(tuplize_dict(parse_params(request.params))))
            role = req_dict.get('role')
            emails = req_dict.get('emails', '').strip()
            new_members = []
            invited_members = []
            if emails and role:
                for email in emails.split(','):
                    context = self._get_context()
                    email = email.strip()
                    try:
                        if email:
                            # Check if email is used
                            user_dict = self._get_user_obj(email)
                            if user_dict:
                                added = self._add_existing_user_as_member(
                                    context, id, role, user_dict)
                                if added:
                                    new_members.append(user_dict.display_name)
                            else:
                                user_data_dict = {
                                    'email': email,
                                    'group_id': id,
                                    'role': role,
                                    'id':
                                    id  # This is something staging/prod need
                                }
                                user_dict = self._action('user_invite')(
                                    context, user_data_dict)
                                invited_members.append(email)
                                # h.flash_success(email + ' has been invited as ' + role)
                                log.info('{} was invited as a new user'.format(
                                    email))
                    except tk.Invalid as e:
                        h.flash_error(
                            _('Invalid email address or unknown username provided: '
                              ) + email)

                if new_members:
                    new_members_msg = _(' were added to the organization.'
                                        ) if len(new_members) != 1 else _(
                                            ' was added to the organization.')
                    h.flash_success(', '.join(new_members) + new_members_msg)

                if invited_members:
                    invited_members_msg = _(
                        ' were invited to join the organization. An account was created for them.'
                    ) if len(invited_members) != 1 else _(
                        ' was invited to join the organization. An account was created for her/him.'
                    )
                    h.flash_success(', '.join(invited_members) +
                                    invited_members_msg)

                org_obj = model.Group.get(id)
                self.notify_admin_users(org_obj, new_members, invited_members,
                                        role)
                self._send_analytics_info(org_obj, new_members,
                                          invited_members)
            else:
                h.flash_error(_('''No user or role was specified'''))
            self._redirect_to_this_controller(action='members', id=id)

        except NotAuthorized:
            abort(401, _('Unauthorized to add member to group %s') % '')
        except NotFound:
            abort(404, _('Group not found'))
        except ValidationError, e:
            h.flash_error(e.error_summary)
Beispiel #54
0
def package_update(context, data_dict):
    '''Update a dataset (package).

    You must be authorized to edit the dataset and the groups that it belongs
    to.

    It is recommended to call
    :py:func:`ckan.logic.action.get.package_show`, make the desired changes to
    the result, and then call ``package_update()`` with it.

    Plugins may change the parameters of this function depending on the value
    of the dataset's ``type`` attribute, see the
    :py:class:`~ckan.plugins.interfaces.IDatasetForm` plugin interface.

    For further parameters see
    :py:func:`~ckan.logic.action.create.package_create`.

    :param id: the name or id of the dataset to update
    :type id: string

    :returns: the updated dataset (if ``'return_package_dict'`` is ``True`` in
              the context, which is the default. Otherwise returns just the
              dataset id)
    :rtype: dictionary

    '''
    model = context['model']
    user = context['user']
    name_or_id = data_dict.get("id") or data_dict['name']

    pkg = model.Package.get(name_or_id)
    if pkg is None:
        raise NotFound(_('Package was not found.'))
    context["package"] = pkg
    data_dict["id"] = pkg.id
    data_dict['type'] = pkg.type

    _check_access('package_update', context, data_dict)

    # get the schema
    package_plugin = lib_plugins.lookup_package_plugin(pkg.type)
    if 'schema' in context:
        schema = context['schema']
    else:
        schema = package_plugin.update_package_schema()

    if 'api_version' not in context:
        # check_data_dict() is deprecated. If the package_plugin has a
        # check_data_dict() we'll call it, if it doesn't have the method we'll
        # do nothing.
        check_data_dict = getattr(package_plugin, 'check_data_dict', None)
        if check_data_dict:
            try:
                package_plugin.check_data_dict(data_dict, schema)
            except TypeError:
                # Old plugins do not support passing the schema so we need
                # to ensure they still work.
                package_plugin.check_data_dict(data_dict)

    data, errors = lib_plugins.plugin_validate(package_plugin, context,
                                               data_dict, schema,
                                               'package_update')
    log.debug('package_update validate_errs=%r user=%s package=%s data=%r',
              errors, context.get('user'),
              context.get('package').name if context.get('package') else '',
              data)

    if errors:
        model.Session.rollback()
        raise ValidationError(errors)

    rev = model.repo.new_revision()
    rev.author = user
    if 'message' in context:
        rev.message = context['message']
    else:
        rev.message = _(u'REST API: Update object %s') % data.get("name")

    #avoid revisioning by updating directly
    model.Session.query(model.Package).filter_by(id=pkg.id).update(
        {"metadata_modified": datetime.datetime.utcnow()})
    model.Session.refresh(pkg)

    pkg = model_save.package_dict_save(data, context)

    context_org_update = context.copy()
    context_org_update['ignore_auth'] = True
    context_org_update['defer_commit'] = True
    context_org_update['add_revision'] = False
    _get_action('package_owner_org_update')(context_org_update, {
        'id': pkg.id,
        'organization_id': pkg.owner_org
    })

    # Needed to let extensions know the new resources ids
    model.Session.flush()
    if data.get('resources'):
        for index, resource in enumerate(data['resources']):
            resource['id'] = pkg.resources[index].id

    for item in plugins.PluginImplementations(plugins.IPackageController):
        item.edit(pkg)

        item.after_update(context, data)

    if not context.get('defer_commit'):
        model.repo.commit()

    log.debug('Updated object %s' % pkg.name)

    return_id_only = context.get('return_id_only', False)

    # Make sure that a user provided schema is not used on package_show
    context.pop('schema', None)

    # we could update the dataset so we should still be able to read it.
    context['ignore_auth'] = True
    output = data_dict['id'] if return_id_only \
            else _get_action('package_show')(context, {'id': data_dict['id']})

    return output
Beispiel #55
0
    def history(self, id):
        group_type = self._ensure_controller_matches_group_type(id)
        if 'diff' in request.params or 'selected1' in request.params:
            try:
                params = {'id': request.params.getone('group_name'),
                          'diff': request.params.getone('selected1'),
                          'oldid': request.params.getone('selected2'),
                          }
            except KeyError:
                if 'group_name' in dict(request.params):
                    id = request.params.getone('group_name')
                c.error = \
                    _('Select two revisions before doing the comparison.')
            else:
                params['diff_entity'] = 'group'
                h.redirect_to(controller='revision', action='diff', **params)

        context = {'model': model, 'session': model.Session,
                   'user': c.user,
                   'schema': self._db_to_form_schema()}
        data_dict = {'id': id}
        try:
            c.group_dict = self._action('group_show')(context, data_dict)
            c.group_revisions = self._action('group_revision_list')(context,
                                                                    data_dict)
            # TODO: remove
            # Still necessary for the authz check in group/layout.html
            c.group = context['group']
        except (NotFound, NotAuthorized):
            abort(404, _('Group not found'))

        format = request.params.get('format', '')
        if format == 'atom':
            # Generate and return Atom 1.0 document.
            from webhelpers.feedgenerator import Atom1Feed
            feed = Atom1Feed(
                title=_(u'CKAN Group Revision History'),
                link=h.url_for(
                    group_type + '_read',
                    id=c.group_dict['name']),
                description=_(u'Recent changes to CKAN Group: ') +
                c.group_dict['display_name'],
                language=unicode(get_lang()),
            )
            for revision_dict in c.group_revisions:
                revision_date = h.date_str_to_datetime(
                    revision_dict['timestamp'])
                try:
                    dayHorizon = int(request.params.get('days'))
                except:
                    dayHorizon = 30
                dayAge = (datetime.datetime.now() - revision_date).days
                if dayAge >= dayHorizon:
                    break
                if revision_dict['message']:
                    item_title = u'%s' % revision_dict['message'].\
                        split('\n')[0]
                else:
                    item_title = u'%s' % revision_dict['id']
                item_link = h.url_for(controller='revision', action='read',
                                      id=revision_dict['id'])
                item_description = _('Log message: ')
                item_description += '%s' % (revision_dict['message'] or '')
                item_author_name = revision_dict['author']
                item_pubdate = revision_date
                feed.add_item(
                    title=item_title,
                    link=item_link,
                    description=item_description,
                    author_name=item_author_name,
                    pubdate=item_pubdate,
                )
            feed.content_type = 'application/atom+xml'
            return feed.writeString('utf-8')
        return render(self._history_template(group_type),
                      extra_vars={'group_type': group_type})
    def members(self, id):
        '''
        Modified core method from 'group' controller.
        Added search & sort functionality.

        :param id: id of the organization for which the member list is requested
        :type id: string
        :return: the rendered template
        :rtype: unicode
        '''
        context = self._get_context()

        q, sort = self._find_filter_params()
        reverse = True if sort == u'title desc' else False

        org_meta = org_meta_dao.OrgMetaDao(id, c.user or c.author, c.userobj)
        org_meta.fetch_all()

        try:
            member_list = self._action('member_list')(context, {
                'id': id,
                'object_type': 'user',
                'q': q,
                'user_info': True
            })
            member_list.sort(key=lambda y: y[4].lower(), reverse=reverse)

            member_groups = {}
            for m in member_list:
                role = m[3]
                if not member_groups.get(role):
                    member_groups[role] = []
                member_groups[role].append(m)

            member_groups = collections.OrderedDict(
                sorted(member_groups.items()))

            data_dict = {'id': id}
            data_dict['include_datasets'] = False
            current_user = self._current_user_info(member_list)
            is_sysadmin = c.userobj and c.userobj.sysadmin
            c_params = {
                'sort':
                sort,
                'members': [a[0:4] for a in member_list],
                'member_groups':
                member_groups,
                'org_meta':
                org_meta,
                'current_user':
                current_user,
                'allow_view_right_side':
                is_sysadmin or bool(current_user.get('role')),
                'allow_approve':
                is_sysadmin or current_user.get('role') == 'admin',
                'request_list':
                self._get_member_requests_for_org(id)
            }
            self._set_c_params(c_params)
        except NotAuthorized:
            base.abort(401, _('Unauthorized to view member list %s') % '')
        except NotFound:
            base.abort(404, _('Group not found'))
        except Exception, ex:
            log.error(str(ex))
            base.abort(404, _('Server error'))
Beispiel #57
0
    def _read(self, id, limit, group_type):
        ''' This is common code used by both read and bulk_process'''
        context = {'model': model, 'session': model.Session,
                   'user': c.user,
                   'schema': self._db_to_form_schema(group_type=group_type),
                   'for_view': True, 'extras_as_string': True}

        q = c.q = request.params.get('q', '')
        # Search within group
        if c.group_dict.get('is_organization'):
            fq = 'owner_org:"%s"' % c.group_dict.get('id')
        else:
            fq = 'groups:"%s"' % c.group_dict.get('name')

        c.description_formatted = \
            h.render_markdown(c.group_dict.get('description'))

        context['return_query'] = True

        page = h.get_page_number(request.params)

        # most search operations should reset the page counter:
        params_nopage = [(k, v) for k, v in request.params.items()
                         if k != 'page']
        sort_by = request.params.get('sort', None)

        def search_url(params):
            controller = lookup_group_controller(group_type)
            action = 'bulk_process' if c.action == 'bulk_process' else 'read'
            url = h.url_for(controller=controller, action=action, id=id)
            params = [(k, v.encode('utf-8') if isinstance(v, string_types)
                       else str(v)) for k, v in params]
            return url + u'?' + urlencode(params)

        def drill_down_url(**by):
            return h.add_url_param(alternative_url=None,
                                   controller='group', action='read',
                                   extras=dict(id=c.group_dict.get('name')),
                                   new_params=by)

        c.drill_down_url = drill_down_url

        def remove_field(key, value=None, replace=None):
            controller = lookup_group_controller(group_type)
            return h.remove_url_param(key, value=value, replace=replace,
                                      controller=controller, action='read',
                                      extras=dict(id=c.group_dict.get('name')))

        c.remove_field = remove_field

        def pager_url(q=None, page=None):
            params = list(params_nopage)
            params.append(('page', page))
            return search_url(params)

        try:
            c.fields = []
            c.fields_grouped = {}
            search_extras = {}
            for (param, value) in request.params.items():
                if param not in ['q', 'page', 'sort'] \
                        and len(value) and not param.startswith('_'):
                    if not param.startswith('ext_'):
                        c.fields.append((param, value))
                        q += ' %s: "%s"' % (param, value)
                        if param not in c.fields_grouped:
                            c.fields_grouped[param] = [value]
                        else:
                            c.fields_grouped[param].append(value)
                    else:
                        search_extras[param] = value

            facets = OrderedDict()

            default_facet_titles = {'organization': _('Organizations'),
                                    'groups': _('Groups'),
                                    'tags': _('Tags'),
                                    'res_format': _('Formats'),
                                    'license_id': _('Licenses')}

            for facet in h.facets():
                if facet in default_facet_titles:
                    facets[facet] = default_facet_titles[facet]
                else:
                    facets[facet] = facet

            # Facet titles
            self._update_facet_titles(facets, group_type)

            c.facet_titles = facets

            data_dict = {
                'q': q,
                'fq': fq,
                'include_private': True,
                'facet.field': facets.keys(),
                'rows': limit,
                'sort': sort_by,
                'start': (page - 1) * limit,
                'extras': search_extras
            }

            context_ = dict((k, v) for (k, v) in context.items()
                            if k != 'schema')
            query = get_action('package_search')(context_, data_dict)

            c.page = h.Page(
                collection=query['results'],
                page=page,
                url=pager_url,
                item_count=query['count'],
                items_per_page=limit
            )

            c.group_dict['package_count'] = query['count']

            c.search_facets = query['search_facets']
            c.search_facets_limits = {}
            for facet in c.search_facets.keys():
                limit = int(request.params.get('_%s_limit' % facet,
                            config.get('search.facets.default', 10)))
                c.search_facets_limits[facet] = limit
            c.page.items = query['results']

            c.sort_by_selected = sort_by

        except search.SearchError as se:
            log.error('Group search error: %r', se.args)
            c.query_error = True
            c.page = h.Page(collection=[])

        self._setup_template_variables(context, {'id': id},
                                       group_type=group_type)
    def member_new(self, id):
        '''
        Modified core method from 'group' controller.

        Added the 'user_invite' functionality. Expects POST request.
        If there's an 'email' parameter in the request and the email
        is not associated with any user account, a new user account with that
        email will be created and added as a member with the specific 'role'
        to the organization.
        Otherwise a 'username' is expected as a request parameter

        :param id: id of the organization to which a member should be added.
        :type id: string
        :return: the rendered template
        :rtype: unicode
        '''
        context = self._get_context()

        # self._check_access('group_delete', context, {'id': id})
        try:
            if request.method == 'POST':

                data_dict = clean_dict(
                    dict_fns.unflatten(
                        tuplize_dict(parse_params(request.params))))
                data_dict['id'] = id
                invited = False
                if data_dict.get('email', '').strip() != '' or \
                                data_dict.get('username', '').strip() != '':
                    email = data_dict.get('email')
                    flash_message = None
                    if email:
                        # Check if email is used
                        user_dict = model.User.by_email(email.strip())
                        if user_dict:
                            # Is user deleted?
                            if user_dict[0].state == 'deleted':
                                h.flash_error(
                                    _('This user no longer has an account on HDX'
                                      ))
                                self._redirect_to_this_controller(
                                    action='members', id=id)
                            # Add user
                            data_dict['username'] = user_dict[0].name
                            flash_message = 'That email is already associated with user ' + data_dict[
                                'username'] + ', who has been added as ' + data_dict[
                                    'role']
                        else:
                            user_data_dict = {
                                'email': email,
                                'group_id': id,
                                'role': data_dict['role'],
                                'id': id  # This is something staging/prod need
                            }
                            del data_dict['email']
                            user_dict = self._action('user_invite')(
                                context, user_data_dict)
                            invited = True
                            data_dict['username'] = user_dict['name']
                            h.flash_success(email + ' has been invited as ' +
                                            data_dict['role'])
                    else:
                        flash_message = data_dict[
                            'username'] + ' has been added as ' + data_dict[
                                'role']
                    c.group_dict = self._action('group_member_create')(
                        context, data_dict)

                    user_obj = model.User.get(data_dict['username'])
                    display_name = user_obj.display_name or user_obj.name

                    org_obj = model.Group.get(id)
                    self.notify_admin_users(
                        org_obj, None if invited else [display_name],
                        [email] if invited else None, data_dict['role'])

                    h.flash_success(flash_message)
                    org_obj = model.Group.get(id)
                    analytics.ChangeMemberAnalyticsSender(
                        org_obj.id, org_obj.name).send_to_queue()
                else:
                    h.flash_error(
                        _('''You need to either fill the username or
                        the email of the person you wish to invite'''))
                self._redirect_to_this_controller(action='members', id=id)

            if not request.params['username']:
                abort(404, _('User not found'))
        except NotAuthorized:
            abort(401, _('Unauthorized to add member to group %s') % '')
        except NotFound:
            abort(404, _('Group not found'))
        except ValidationError, e:
            h.flash_error(e.error_summary)
Beispiel #59
0
    def index(self):
        group_type = self._guess_group_type()

        page = h.get_page_number(request.params) or 1
        items_per_page = 21

        context = {'model': model, 'session': model.Session,
                   'user': c.user, 'for_view': True,
                   'with_private': False}

        q = c.q = request.params.get('q', '')
        sort_by = c.sort_by_selected = request.params.get('sort')
        try:
            self._check_access('site_read', context)
            self._check_access('group_list', context)
        except NotAuthorized:
            abort(403, _('Not authorized to see this page'))

        # pass user info to context as needed to view private datasets of
        # orgs correctly
        if c.userobj:
            context['user_id'] = c.userobj.id
            context['user_is_admin'] = c.userobj.sysadmin

        try:
            data_dict_global_results = {
                'all_fields': False,
                'q': q,
                'sort': sort_by,
                'type': group_type or 'group',
            }
            global_results = self._action('group_list')(
                context, data_dict_global_results)
        except ValidationError as e:
            if e.error_dict and e.error_dict.get('message'):
                msg = e.error_dict['message']
            else:
                msg = str(e)
            h.flash_error(msg)
            c.page = h.Page([], 0)
            return render(self._index_template(group_type),
                          extra_vars={'group_type': group_type})

        data_dict_page_results = {
            'all_fields': True,
            'q': q,
            'sort': sort_by,
            'type': group_type or 'group',
            'limit': items_per_page,
            'offset': items_per_page * (page - 1),
            'include_extras': True
        }
        page_results = self._action('group_list')(context,
                                                  data_dict_page_results)

        c.page = h.Page(
            collection=global_results,
            page=page,
            url=h.pager_url,
            items_per_page=items_per_page,
        )

        c.page.items = page_results
        return render(self._index_template(group_type),
                      extra_vars={'group_type': group_type})
Beispiel #60
0
    def bulk_process(self, id):
        ''' Allow bulk processing of datasets for an organization.  Make
        private/public or delete. For organization admins.'''

        group_type = self._ensure_controller_matches_group_type(
            id.split('@')[0])

        # check we are org admin

        context = {'model': model, 'session': model.Session,
                   'user': c.user,
                   'schema': self._db_to_form_schema(group_type=group_type),
                   'for_view': True, 'extras_as_string': True}
        data_dict = {'id': id, 'type': group_type}

        try:
            self._check_access('bulk_update_public', context, {'org_id': id})
            # Do not query for the group datasets when dictizing, as they will
            # be ignored and get requested on the controller anyway
            data_dict['include_datasets'] = False
            c.group_dict = self._action('group_show')(context, data_dict)
            c.group = context['group']
        except NotFound:
            abort(404, _('Group not found'))
        except NotAuthorized:
            abort(403, _('User %r not authorized to edit %s') % (c.user, id))

        if not c.group_dict['is_organization']:
            # FIXME: better error
            raise Exception('Must be an organization')

        # use different form names so that ie7 can be detected
        form_names = set(["bulk_action.public", "bulk_action.delete",
                          "bulk_action.private"])
        actions_in_form = set(request.params.keys())
        actions = form_names.intersection(actions_in_form)
        # If no action then just show the datasets
        if not actions:
            # unicode format (decoded from utf8)
            limit = 500
            self._read(id, limit, group_type)
            c.packages = c.page.items
            return render(self._bulk_process_template(group_type),
                          extra_vars={'group_type': group_type})

        # ie7 puts all buttons in form params but puts submitted one twice
        for key, value in dict(request.params.dict_of_lists()).items():
            if len(value) == 2:
                action = key.split('.')[-1]
                break
        else:
            # normal good browser form submission
            action = actions.pop().split('.')[-1]

        # process the action first find the datasets to perform the action on.
        # they are prefixed by dataset_ in the form data
        datasets = []
        for param in request.params:
            if param.startswith('dataset_'):
                datasets.append(param[8:])

        action_functions = {
            'private': 'bulk_update_private',
            'public': 'bulk_update_public',
            'delete': 'bulk_update_delete',
        }

        data_dict = {'datasets': datasets, 'org_id': c.group_dict['id']}

        try:
            get_action(action_functions[action])(context, data_dict)
        except NotAuthorized:
            abort(403, _('Not authorized to perform bulk update'))
        h.redirect_to(group_type + '_bulk_process', id=id)