예제 #1
0
파일: actions.py 프로젝트: jerrykan/herder
    def handle(self):
        """Retire the context item."""
        # ensure modification comes via POST
        if self.client.env['REQUEST_METHOD'] != 'POST':
            raise roundup.exceptions.Reject(self._('Invalid request'))

        # if we want to view the index template now, then unset the itemid
        # context info (a special-case for retire actions on the index page)
        itemid = self.nodeid
        if self.template == 'index':
            self.client.nodeid = None

        # make sure we don't try to retire admin or anonymous
        if self.classname == 'user' and \
                self.db.user.get(itemid, 'username') in ('admin', 'anonymous'):
            raise ValueError(
                self._('You may not retire the admin or anonymous user'))

        # check permission
        if not self.hasPermission(
                'Retire', classname=self.classname, itemid=itemid):
            raise exceptions.Unauthorised(
                self._('You do not have permission to retire %(class)s') %
                {'class': self.classname})

        # do the retire
        self.db.getclass(self.classname).retire(itemid)
        self.db.commit()

        self.client.add_ok_message(
            self._('%(classname)s %(itemid)s has been retired') % {
                'classname': self.classname.capitalize(),
                'itemid': itemid
            })
예제 #2
0
파일: actions.py 프로젝트: jerrykan/herder
    def newItemPermission(self, props, classname=None):
        """Just check the "Register" permission.
        """
        # registration isn't allowed to supply roles
        if 'roles' in props:
            raise exceptions.Unauthorised(
                self._("It is not permitted to supply roles at registration."))

        # technically already checked, but here for clarity
        return self.hasPermission('Register', classname=classname)
예제 #3
0
파일: actions.py 프로젝트: jerrykan/herder
    def _createnode(self, cn, props):
        """Create a node based on the contents of the form."""
        # check for permission
        if not self.newItemPermission(props, classname=cn):
            raise exceptions.Unauthorised(
                self._('You do not have permission to create %(class)s') %
                {'class': cn})

        # create the node and return its id
        cl = self.db.classes[cn]
        return cl.create(**props)
예제 #4
0
파일: actions.py 프로젝트: jerrykan/herder
    def _changenode(self, cn, nodeid, props):
        """Change the node based on the contents of the form."""
        # check for permission
        if not self.editItemPermission(props, classname=cn, itemid=nodeid):
            raise exceptions.Unauthorised(
                self._('You do not have permission to edit %(class)s') %
                {'class': cn})

        # make the changes
        cl = self.db.classes[cn]
        return cl.set(nodeid, **props)
예제 #5
0
파일: actions.py 프로젝트: jerrykan/herder
    def permission(self):
        """Check whether the user has permission to execute this action.

        True by default. If the permissionType attribute is a string containing
        a simple permission, check whether the user has that permission.
        Subclasses must also define the name attribute if they define
        permissionType.

        Despite having this permission, users may still be unauthorised to
        perform parts of actions. It is up to the subclasses to detect this.
        """
        if (self.permissionType
                and not self.hasPermission(self.permissionType)):
            info = {'action': self.name, 'classname': self.classname}
            raise exceptions.Unauthorised(
                self._('You do not have permission to '
                       '%(action)s the %(classname)s class.') % info)
예제 #6
0
파일: actions.py 프로젝트: jerrykan/herder
    def handle(self):
        """Performs an edit of all of a class' items in one go.

        The "rows" CGI var defines the CSV-formatted entries for the class. New
        nodes are identified by the ID 'X' (or any other non-existent ID) and
        removed lines are retired.
        """
        # ensure modification comes via POST
        if self.client.env['REQUEST_METHOD'] != 'POST':
            raise roundup.exceptions.Reject(self._('Invalid request'))

        # figure the properties list for the class
        cl = self.db.classes[self.classname]
        props_without_id = list(cl.getprops(protected=0))

        # the incoming CSV data will always have the properties in colums
        # sorted and starting with the "id" column
        props_without_id.sort()
        props = ['id'] + props_without_id

        # do the edit
        rows = io_.BytesIO(self.form['rows'].value)
        reader = csv.reader(rows)
        found = {}
        line = 0
        for values in reader:
            line += 1
            if line == 1: continue
            # skip property names header
            if values == props:
                continue

            # extract the itemid
            itemid, values = values[0], values[1:]
            found[itemid] = 1

            # see if the node exists
            if itemid in ('x', 'X') or not cl.hasnode(itemid):
                exists = 0

                # check permission to create this item
                if not self.hasPermission('Create', classname=self.classname):
                    raise exceptions.Unauthorised(
                        self._(
                            'You do not have permission to create %(class)s') %
                        {'class': self.classname})
            elif cl.hasnode(itemid) and cl.is_retired(itemid):
                # If a CSV line just mentions an id and the corresponding
                # item is retired, then the item is restored.
                cl.restore(itemid)
                continue
            else:
                exists = 1

            # confirm correct weight
            if len(props_without_id) != len(values):
                self.client.add_error_message(
                    self._('Not enough values on line %(line)s') %
                    {'line': line})
                return

            # extract the new values
            d = {}
            for name, value in zip(props_without_id, values):
                # check permission to edit this property on this item
                if exists and not self.hasPermission('Edit',
                                                     itemid=itemid,
                                                     classname=self.classname,
                                                     property=name):
                    raise exceptions.Unauthorised(
                        self._('You do not have permission to edit %(class)s')
                        % {'class': self.classname})

                prop = cl.properties[name]
                value = value.strip()
                # only add the property if it has a value
                if value:
                    # if it's a multilink, split it
                    if isinstance(prop, hyperdb.Multilink):
                        value = value.split(':')
                    elif isinstance(prop, hyperdb.Password):
                        value = password.Password(value, config=self.db.config)
                    elif isinstance(prop, hyperdb.Interval):
                        value = date.Interval(value)
                    elif isinstance(prop, hyperdb.Date):
                        value = date.Date(value)
                    elif isinstance(prop, hyperdb.Boolean):
                        value = value.lower() in ('yes', 'true', 'on', '1')
                    elif isinstance(prop, hyperdb.Number):
                        value = float(value)
                    d[name] = value
                elif exists:
                    # nuke the existing value
                    if isinstance(prop, hyperdb.Multilink):
                        d[name] = []
                    else:
                        d[name] = None

            # perform the edit
            if exists:
                # edit existing
                cl.set(itemid, **d)
            else:
                # new node
                found[cl.create(**d)] = 1

        # retire the removed entries
        for itemid in cl.list():
            if itemid not in found:
                # check permission to retire this item
                if not self.hasPermission(
                        'Retire', itemid=itemid, classname=self.classname):
                    raise exceptions.Unauthorised(
                        self._(
                            'You do not have permission to retire %(class)s') %
                        {'class': self.classname})
                cl.retire(itemid)

        # all OK
        self.db.commit()

        self.client.add_ok_message(self._('Items edited OK'))
예제 #7
0
파일: actions.py 프로젝트: jerrykan/herder
    def handle(self):
        """Mangle some of the form variables.

        Set the form ":filter" variable based on the values of the filter
        variables - if they're set to anything other than "dontcare" then add
        them to :filter.

        Handle the ":queryname" variable and save off the query to the user's
        query list.

        Split any String query values on whitespace and comma.

        """
        self.fakeFilterVars()
        queryname = self.getQueryName()

        # editing existing query name?
        old_queryname = self.getFromForm('old-queryname')

        # handle saving the query params
        if queryname:
            # parse the environment and figure what the query _is_
            req = templating.HTMLRequest(self.client)

            url = self.getCurrentURL(req)

            key = self.db.query.getkey()
            if key:
                # edit the old way, only one query per name
                try:
                    qid = self.db.query.lookup(old_queryname)
                    if not self.hasPermission('Edit', 'query', itemid=qid):
                        raise exceptions.Unauthorised(
                            self._(
                                "You do not have permission to edit queries"))
                    self.db.query.set(qid, klass=self.classname, url=url)
                except KeyError:
                    # create a query
                    if not self.hasPermission('Create', 'query'):
                        raise exceptions.Unauthorised(
                            self._(
                                "You do not have permission to store queries"))
                    qid = self.db.query.create(name=queryname,
                                               klass=self.classname,
                                               url=url)
            else:
                # edit the new way, query name not a key any more
                # see if we match an existing private query
                uid = self.db.getuid()
                qids = self.db.query.filter(None, {
                    'name': old_queryname,
                    'private_for': uid
                })
                if not qids:
                    # ok, so there's not a private query for the current user
                    # - see if there's one created by them
                    qids = self.db.query.filter(None, {
                        'name': old_queryname,
                        'creator': uid
                    })

                if qids and old_queryname:
                    # edit query - make sure we get an exact match on the name
                    for qid in qids:
                        if old_queryname != self.db.query.get(qid, 'name'):
                            continue
                        if not self.hasPermission('Edit', 'query', itemid=qid):
                            raise exceptions.Unauthorised(
                                self.
                                _("You do not have permission to edit queries"
                                  ))
                        self.db.query.set(qid,
                                          klass=self.classname,
                                          url=url,
                                          name=queryname)
                else:
                    # create a query
                    if not self.hasPermission('Create', 'query'):
                        raise exceptions.Unauthorised(
                            self._(
                                "You do not have permission to store queries"))
                    qid = self.db.query.create(name=queryname,
                                               klass=self.classname,
                                               url=url,
                                               private_for=uid)

            # and add it to the user's query multilink
            queries = self.db.user.get(self.userid, 'queries')
            if qid not in queries:
                queries.append(qid)
                self.db.user.set(self.userid, queries=queries)

            # commit the query change to the database
            self.db.commit()
예제 #8
0
파일: actions.py 프로젝트: jerrykan/herder
    def handle(self):
        ''' Export the specified search query as CSV. '''
        # figure the request
        request = templating.HTMLRequest(self.client)
        filterspec = request.filterspec
        sort = request.sort
        group = request.group
        columns = request.columns
        klass = self.db.getclass(request.classname)

        # check if all columns exist on class
        # the exception must be raised before sending header
        props = klass.getprops()
        for cname in columns:
            if cname not in props:
                # TODO raise exceptions.NotFound(.....) does not give message
                # so using SeriousError instead
                self.client.response_code = 404
                raise exceptions.SeriousError(
                    self._('Column "%(column)s" not found on %(class)s') % {
                        'column': cgi.escape(cname),
                        'class': request.classname
                    })

        # full-text search
        if request.search_text:
            matches = self.db.indexer.search(
                re.findall(r'\b\w{2,25}\b', request.search_text), klass)
        else:
            matches = None

        h = self.client.additional_headers
        h['Content-Type'] = 'text/csv; charset=%s' % self.client.charset
        # some browsers will honor the filename here...
        h['Content-Disposition'] = 'inline; filename=query.csv'

        self.client.header()

        if self.client.env['REQUEST_METHOD'] == 'HEAD':
            # all done, return a dummy string
            return 'dummy'

        wfile = self.client.request.wfile
        if self.client.charset != self.client.STORAGE_CHARSET:
            wfile = codecs.EncodedFile(wfile, self.client.STORAGE_CHARSET,
                                       self.client.charset, 'replace')

        writer = csv.writer(wfile)
        self.client._socket_op(writer.writerow, columns)

        # and search
        for itemid in klass.filter(matches, filterspec, sort, group):
            row = []
            for name in columns:
                # check permission to view this property on this item
                if not self.hasPermission('View',
                                          itemid=itemid,
                                          classname=request.classname,
                                          property=name):
                    raise exceptions.Unauthorised(
                        self._('You do not have permission to view %(class)s')
                        % {'class': request.classname})
                row.append(str(klass.get(itemid, name)))
            self.client._socket_op(writer.writerow, row)

        return '\n'