Example #1
0
def validate_name(meta, itemid):
    """
    Check whether the names are valid.
    Will just return, if they are valid, will raise a NameNotValidError if not.
    """
    names = meta.get(NAME)
    current_namespace = meta.get(NAMESPACE)
    if current_namespace is None:
        raise NameNotValidError(L_("No namespace field in the meta."))
    namespaces = [namespace.rstrip('/') for namespace, _ in app.cfg.namespace_mapping]

    if len(names) != len(set(names)):
        raise NameNotValidError(L_("The names in the name list must be unique."))
    # Item names must not start with '@' or '+', '@something' denotes a field where as '+something' denotes a view.
    invalid_names = [name for name in names if name.startswith(('@', '+'))]
    if invalid_names:
        raise NameNotValidError(L_("Item names (%(invalid_names)s) must not start with '@' or '+'", invalid_names=", ".join(invalid_names)))

    namespaces = namespaces + NAMESPACES_IDENTIFIER  # Also dont allow item names to match with identifier namespaces.
    # Item names must not match with existing namespaces.
    invalid_names = [name for name in names if name.split('/', 1)[0] in namespaces]
    if invalid_names:
        raise NameNotValidError(L_("Item names (%(invalid_names)s) must not match with existing namespaces.", invalid_names=", ".join(invalid_names)))
    query = And([Or([Term(NAME, name) for name in names]), Term(NAMESPACE, current_namespace)])
    # There should be not item existing with the same name.
    if itemid is not None:
        query = And([query, Not(Term(ITEMID, itemid))])  # search for items except the current item.
    with flaskg.storage.indexer.ix[LATEST_REVS].searcher() as searcher:
        results = searcher.search(query)
        duplicate_names = {name for result in results for name in result[NAME] if name in names}
        if duplicate_names:
            raise NameNotValidError(L_("Item(s) named %(duplicate_names)s already exist.", duplicate_names=", ".join(duplicate_names)))
Example #2
0
class Blog(Default):
    itemtype = ITEMTYPE_BLOG
    display_name = L_('Blog')
    description = L_('Blog item')
    order = 0

    class _ModifyForm(Default._ModifyForm):
        meta_form = BlogMetaForm
        meta_template = 'blog/modify_main_meta.html'

    def do_show(self, revid):
        """
        Show a blog item and a list of its blog entries below it.

        If tag GET-parameter is defined, the list of blog entries consists only
        of those entries that contain the tag value in their lists of tags.
        """
        # for now it is just one tag=value, later it could be tag=value1&tag=value2&...
        tag = request.values.get('tag')
        prefix = self.name + u'/'
        current_timestamp = int(time.time())
        terms = [
            Term(WIKINAME, app.cfg.interwikiname),
            # Only blog entry itemtypes
            Term(ITEMTYPE, ITEMTYPE_BLOG_ENTRY),
            # Only sub items of this item
            Prefix(NAME_EXACT, prefix),
        ]
        if tag:
            terms.append(Term(TAGS, tag))
        query = And(terms)

        def ptime_sort_key(searcher, docnum):
            """
            Compute the publication time key for blog entries sorting.

            If PTIME is not defined, we use MTIME.
            """
            fields = searcher.stored_fields(docnum)
            ptime = fields.get(PTIME, fields[MTIME])
            return ptime

        ptime_sort_facet = FunctionFacet(ptime_sort_key)

        revs = flaskg.storage.search(query,
                                     sortedby=ptime_sort_facet,
                                     reverse=True,
                                     limit=None)
        blog_entry_items = [
            Item.create(rev.name, rev_id=rev.revid) for rev in revs
        ]
        return render_template(
            'blog/main.html',
            item_name=self.name,
            fqname=split_fqname(self.name),
            blog_item=self,
            blog_entry_items=blog_entry_items,
            tag=tag,
            item=self,
        )
Example #3
0
class ValidJSON(Validator):
    """Validator for JSON
    """
    invalid_json_msg = L_('Invalid JSON.')
    invalid_itemid_msg = L_('Itemid not a proper UUID')
    invalid_namespace_msg = ''

    def validitemid(self, itemid):
        if not itemid:
            self.invalid_itemid_msg = L_("No ITEMID field")
            return False
        return uuid_validator(String(itemid), None)

    def validnamespace(self, current_namespace):
        if current_namespace is None:
            self.invalid_namespace_msg = L_("No namespace field in the meta.")
            return False
        namespaces = [namespace.rstrip('/') for namespace, _ in app.cfg.namespace_mapping]
        if current_namespace not in namespaces:  # current_namespace must be an existing namespace.
            self.invalid_namespace_msg = L_("%(_namespace)s is not a valid namespace.", _namespace=current_namespace)
            return False
        return True

    def validate(self, element, state):
        try:
            meta = json.loads(element.value)
        except:  # catch ANY exception that happens due to unserializing
            return self.note_error(element, state, 'invalid_json_msg')
        if not self.validnamespace(meta.get(NAMESPACE)):
            return self.note_error(element, state, 'invalid_namespace_msg')
        if state[FQNAME].field == ITEMID:
            if not self.validitemid(meta.get(ITEMID, state[FQNAME].value)):
                return self.note_error(element, state, 'invalid_itemid_msg')
        return True
Example #4
0
class BlogEntry(Default):
    itemtype = ITEMTYPE_BLOG_ENTRY
    display_name = L_('Blog entry')
    description = L_('Blog entry item')
    order = 0

    class _ModifyForm(Default._ModifyForm):
        meta_form = BlogEntryMetaForm
        meta_template = 'blog/modify_entry_meta.html'

        @classmethod
        def from_item(cls, item):
            form = super(BlogEntry._ModifyForm, cls).from_item(item)
            # preload PTIME with the current datetime
            if not form['meta_form']['ptime']:
                form['meta_form']['ptime'].set(datetime.utcnow())
            return form

    def do_show(self, revid):
        blog_item_name = self.name.rsplit('/', 1)[0]
        try:
            blog_item = Item.create(blog_item_name)
        except AccessDenied:
            abort(403)
        if not isinstance(blog_item, Blog):
            # The parent item of this blog entry item is not a Blog item.
            abort(403)
        return render_template(
            'blog/entry.html',
            item_name=self.name,
            fqname=blog_item.fqname,
            blog_item=blog_item,
            blog_entry_item=self,
            item=self,
        )
Example #5
0
class Userprofile(Item):
    """
    Currently userprofile is implemented as a contenttype. This is a stub of an
    itemtype implementation of userprofile.
    """
    itemtype = ITEMTYPE_USERPROFILE
    display_name = L_('User profile')
    description = L_('User profile item (not implemented yet!)')
Example #6
0
 def validnamespace(self, current_namespace):
     if current_namespace is None:
         self.invalid_namespace_msg = L_("No namespace field in the meta.")
         return False
     namespaces = [namespace.rstrip('/') for namespace, _ in app.cfg.namespace_mapping]
     if current_namespace not in namespaces:  # current_namespace must be an existing namespace.
         self.invalid_namespace_msg = L_("%(_namespace)s is not a valid namespace.", _namespace=current_namespace)
         return False
     return True
Example #7
0
 def _load(self, item):
     super(TicketUpdateForm, self)._load(item)
     self['submit'].properties['labels'] = {
         'update':
         L_('Update ticket'),
         'update_negate_status':
         (L_('Update & reopen ticket')
          if item.meta.get('closed') else L_('Update & close ticket'))
     }
Example #8
0
class TicketBackRefForm(Form):
    supersedes = BackReference.using(label=L_("Supersedes"))
    required_by = BackReference.using(label=L_("Required By"))
    subscribers = BackReference.using(label=L_("Subscribers"))

    def _load(self, item):
        id_ = item.meta[ITEMID]
        self['supersedes'].set(Term(SUPERSEDED_BY, id_))
        self['required_by'].set(Term(DEPENDS_ON, id_))
        self['subscribers'].set(Term(SUBSCRIPTIONS, id_))
Example #9
0
class Ticket(Contentful):
    itemtype = ITEMTYPE_TICKET
    display_name = L_('Ticket')
    description = L_('Ticket item')
    submit_template = 'ticket/submit.html'
    modify_template = 'ticket/modify.html'

    def do_show(self, revid):
        if revid != CURRENT:
            # TODO When requesting a historical version, show a readonly view
            abort(403)
        else:
            return self.do_modify()

    def do_modify(self):
        is_new = isinstance(self.content, NonExistentContent)
        closed = self.meta.get('closed')

        Form = TicketSubmitForm if is_new else TicketUpdateForm

        if request.method in ['GET', 'HEAD']:
            form = Form.from_item(self)
        elif request.method == 'POST':
            form = Form.from_request(request)
            if form.validate():
                meta, data = form._dump(self)
                try:
                    self.modify(meta, data)
                except AccessDenied:
                    abort(403)
                else:
                    try:
                        fqname = CompositeName(self.meta[NAMESPACE], ITEMID,
                                               self.meta[ITEMID])
                    except KeyError:
                        fqname = self.fqname
                    return redirect(url_for('.show_item', item_name=fqname))

        # XXX When creating new item, suppress the "foo doesn't exist. Create it?" dummy content
        data_rendered = None if is_new else Markup(self.content._render_data())

        suggested_tags = get_itemtype_specific_tags(ITEMTYPE_TICKET)

        return render_template(
            self.submit_template if is_new else self.modify_template,
            is_new=is_new,
            closed=closed,
            item_name=self.name,
            data_rendered=data_rendered,
            form=form,
            suggested_tags=suggested_tags,
            item=self,
        )
Example #10
0
def themed_error(e):
    item_name = request.view_args.get('item_name', u'')
    if e.code == 403:
        title = L_('Access denied')
        description = L_('You are not allowed to access this resource.')
    else:
        # if we have no special code, we just return the HTTPException instance
        return e
    content = render_template('error.html',
                              item_name=item_name,
                              title=title, description=description)
    return content, e.code
Example #11
0
class TicketForm(BaseModifyForm):
    meta = TicketMetaForm
    backrefs = TicketBackRefForm
    message = OptionalMultilineText.using(label=L_("Message")).with_properties(
        rows=8, cols=80)
    data_file = File.using(optional=True, label=L_('Upload file:'))

    def _load(self, item):
        meta = item.prepare_meta_for_modify(item.meta)
        self['meta'].set(meta, 'duck')
        # XXX need a more explicit way to test for item creation/modification
        if ITEMID in item.meta:
            self['backrefs']._load(item)
Example #12
0
class TicketMetaForm(Form):
    summary = Text.using(label=L_("Summary"), optional=False).with_properties \
            (widget=WIDGET_SEARCH, placeholder=L_("One-line summary"))
    effort = Rating.using(label=L_("Effort"))
    difficulty = Rating.using(label=L_("Difficulty"))
    severity = Rating.using(label=L_("Severity"))
    priority = Rating.using(label=L_("Priority"))
    tags = Tags.using(optional=True)
    assigned_to = OptionalUserReference.using(label=L_("Assigned To"))
    superseded_by = OptionalTicketReference.using(label=L_("Superseded By"))
    depends_on = OptionalTicketReference.using(label=L_("Depends On"))
Example #13
0
def send_notifications(app, fqname, **kwargs):
    """ Send mail notifications to subscribers on item change

    :param app: local proxy app
    :param fqname: fqname of the changed item
    :param kwargs: key/value pairs that contain extra information about the item
                   required in order to create a notification
    """
    action = kwargs.get('action')
    revs = get_item_last_revisions(app, fqname) if action not in [
        DESTROY_REV, DESTROY_ALL, ] else []
    notification = Notification(app, fqname, revs, **kwargs)
    try:
        content_diff = notification.get_content_diff()
    except Exception:
        # current user has likely corrupted an item or fixed a corrupted item
        # if current item is corrupt, another exception will occur in a downstream script
        content_diff = [u'- ' + _('An error has occurred, the current or prior revision of this item may be corrupt.')]
    meta_diff = notification.get_meta_diff()

    u = flaskg.user
    meta = kwargs.get('meta') if action in [DESTROY_REV, DESTROY_ALL, ] else revs[0].meta._meta
    subscribers = {subscriber for subscriber in get_subscribers(**meta) if
                   subscriber.itemid != u.itemid}
    subscribers_locale = {subscriber.locale for subscriber in subscribers}
    for locale in subscribers_locale:
        with force_locale(locale):
            txt_msg, html_msg = notification.render_templates(content_diff, meta_diff)
            subject = L_('[%(moin_name)s] Update of "%(fqname)s" by %(user_name)s',
                         moin_name=app.cfg.interwikiname, fqname=unicode(fqname), user_name=u.name0)
            subscribers_emails = [subscriber.email for subscriber in subscribers
                                  if subscriber.locale == locale]
            sendmail(subject, txt_msg, to=subscribers_emails, html=html_msg)
Example #14
0
class Reference(Select.with_properties(empty_label=L_(u'(None)')).validated_by(ValidReference())):
    """
    A metadata property that points to another item selected out of the
    Results of a search query.
    """
    @class_cloner
    def to(cls, query, query_args={}):
        cls._query = query
        cls._query_args = query_args
        return cls

    @classmethod
    def _get_choice_specs(cls):
        revs = flaskg.storage.search(cls._query, **cls._query_args)
        label_getter = cls.properties['label_getter']
        choices = [(rev.meta[ITEMID], label_getter(rev)) for rev in revs]
        if cls.optional:
            choices.append((u'', cls.properties['empty_label']))
        return choices

    def __init__(self, value=Unspecified, **kw):
        super(Reference, self).__init__(value, **kw)
        # NOTE There is a slight chance of two instances of the same Reference
        # subclass having different set of choices when the storage changes
        # between their initialization.
        choice_specs = self._get_choice_specs()
        self.properties['choice_specs'] = choice_specs
        self.valid_values = [id_ for id_, name in choice_specs]
Example #15
0
def themed_error(e):
    item_name = request.view_args.get('item_name', u'')
    if e.code == 403:
        title = L_('Access Denied')
        description = L_('You are not allowed to access this resource.')
        if e.description.startswith(' '):
            # leading blank indicates supplemental info, not standard werkzeug message
            description += e.description
    else:
        # if we have no special code, we just return the HTTPException instance
        return e
    content = render_template('error.html',
                              item_name=item_name,
                              title=title,
                              description=description)
    return content, e.code
Example #16
0
class ACLValidator(Validator):
    """
    Meta Validator - currently used for validating ACLs only
    """
    acl_fail_msg = L_("The ACL string is invalid")

    def validate(self, element, state):
        return True
Example #17
0
 def rename(self, name, comment=u''):
     """
     rename this item to item <name> (replace current name by another name in the NAME list)
     """
     fqname = CompositeName(self.fqname.namespace, self.fqname.field, name)
     if flaskg.storage.get_item(**fqname.query):
         raise NameNotUniqueError(L_("An item named %s already exists in the namespace %s." % (name, fqname.namespace)))
     return self._rename(name, comment, action=ACTION_RENAME)
Example #18
0
def modify_acl(item_name):
    fqname = split_fqname(item_name)
    item = Item.create(item_name)
    meta = dict(item.meta)
    new_acl = request.form.get(fqname.fullname)
    is_valid = acl_validate(new_acl)
    if is_valid:
        if new_acl in ('Empty', ''):
            meta[ACL] = ''
        elif new_acl == 'None' and ACL in meta:
            del(meta[ACL])
        else:
            meta[ACL] = new_acl
        item._save(meta=meta)
        flash(L_("Success! ACL saved.<br>Item: %(item_name)s<br>ACL: %(acl_rule)s", item_name=fqname.fullname, acl_rule=new_acl), "info")
    else:
        flash(L_("Nothing changed, invalid ACL.<br>Item: %(item_name)s<br>ACL: %(acl_rule)s", item_name=fqname.fullname, acl_rule=new_acl), "error")
    return redirect(url_for('.item_acl_report'))
Example #19
0
class AdvancedSearchForm(Form):
    q = Search
    summary = Text.using(label=L_("Summary"), optional=False).with_properties \
            (widget=WIDGET_SEARCH, placeholder=L_("Find Tickets"))
    effort = Rating.using(label=L_("Effort"))
    difficulty = Rating.using(label=L_("Difficulty"))
    severity = Rating.using(label=L_("Severity"))
    priority = Rating.using(label=L_("Priority"))
    tags = Tags.using(optional=True)
    assigned_to = OptionalUserReference.using(label=L_("Assigned To"))
    author = OptionalUserReference.using(label=L_("Author"))
Example #20
0
class ValidReference(Validator):
    """
    Validator for Reference
    """
    invalid_reference_msg = L_('Invalid Reference.')

    def validate(self, element, state):
        if element.value not in element.valid_values:
            return self.note_error(element, state, 'invalid_reference_msg')
        return True
Example #21
0
    def __init__(self, username=None, priv=None, item=None):
        if None in (username, priv, item):
            message = _("Permission denied!")
        else:
            username = username or L_("You")
            message = _("%(username)s may not %(priv)s '%(item)s'.",
                        username=username, priv=_(priv), item=item)
            # XXX add _('...') for all privs elsewhere for extraction

        AccessError.__init__(self, message)
Example #22
0
    class _ModifyForm(BaseModifyForm):
        """
        ModifyForm (the form used on +modify view), sans the content part.
        Combined dynamically with the ModifyForm of the Content subclass in
        Contentful.ModifyForm.

        Subclasses of Contentful should generally override this instead of
        ModifyForm.
        """
        meta_form = BaseMetaForm
        extra_meta_text = JSON.using(
            label=L_("Extra MetaData (JSON)")).with_properties(rows=ROWS_META,
                                                               cols=COLS)
        meta_template = 'modify_meta.html'

        def _load(self, item):
            """
            Load metadata and data from :item into :self. Used by
            BaseModifyForm.from_item.
            """
            meta = item.prepare_meta_for_modify(item.meta)
            # Default value of `policy` argument of Flatland.Dict.set's is
            # 'strict', which causes KeyError to be thrown when meta contains
            # meta keys that are not present in self['meta_form']. Setting
            # policy to 'duck' suppresses this behavior.
            if 'acl' not in meta:
                meta['acl'] = "None"

            self['meta_form'].set(meta, policy='duck')
            for k in self['meta_form'].field_schema_mapping.keys(
            ) + IMMUTABLE_KEYS:
                meta.pop(k, None)
            self['extra_meta_text'].set(item.meta_dict_to_text(meta))
            self['content_form']._load(item.content)

        def _dump(self, item):
            """
            Dump useful data out of :self. :item contains the old item and
            should not be the primary data source; but it can be useful in case
            the data in :self is not sufficient.

            :returns: a tuple (meta, data, contenttype_guessed, comment),
                      suitable as arguments of the same names to pass to
                      item.modify
            """
            # Since the metadata form for tickets is an incomplete one, we load the
            # original meta and update it with those from the metadata editor
            # e.g. we get PARENTID in here
            meta = item.meta_filter(item.prepare_meta_for_modify(item.meta))
            meta.update(self['meta_form'].value)
            meta.update(item.meta_text_to_dict(self['extra_meta_text'].value))
            data, contenttype_guessed = self['content_form']._dump(
                item.content)
            comment = self['comment'].value
            return meta, data, contenttype_guessed, comment
Example #23
0
class ACLValidator(Validator):
    """
    Meta Validator - currently used for validating ACLs only
    """
    acl_fail_msg = L_("The ACL string is invalid.")

    def validate(self, element, state):
        if acl_validate(element) is True:
            return True
        flash(L_("The ACL string is invalid."), "error")
        return self.note_error(element, state, 'acl_fail_msg')
Example #24
0
class TextChaValid(Validator):
    """Validator for TextChas
    """
    textcha_incorrect_msg = L_('The entered TextCha was incorrect.')
    textcha_invalid_msg = L_(
        'The TextCha question is invalid or has expired. Please try again.')

    def validate(self, element, state):
        textcha = TextCha(element.parent)

        if textcha.is_enabled():
            if textcha.answer_re is None:
                textcha.init_qa()
                textcha.amend_form()
                element.set("")
                return self.note_error(element, state, 'textcha_invalid_msg')
            if textcha.answer_re.match(element.value.strip()) is None:
                return self.note_error(element, state, 'textcha_incorrect_msg')

        return True
Example #25
0
def message_markup(message):
    """
    Add a heading with author and timestamp to message (aka ticket description).
    """
    timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    heading = L_('{author} wrote on {timestamp}:').format(
        author=flaskg.user.name[0], timestamp=timestamp)
    message = u'{heading}\n\n{message}'.format(heading=heading,
                                               message=message)
    return u"""{{{{{{#!wiki moin-ticket
%(message)s
}}}}}}""" % dict(message=message)
Example #26
0
class ValidSearch(Validator):
    """Validator for a valid search form
    """
    too_short_query_msg = L_('Search query too short.')

    def validate(self, element, state):
        if element['q'].value is None:
            # no query, nothing to search for
            return False
        if len(element['q'].value) < 2:
            return self.note_error(element, state, 'too_short_query_msg')
        return True
Example #27
0
class BaseMetaForm(Form):

    itemtype = RequiredText.using(label=L_("Item type")).with_properties(
        placeholder=L_("Item type"))
    contenttype = RequiredText.using(label=L_("Content type")).with_properties(
        placeholder=L_("Content type"))
    # Flatland doesn't distinguish between empty value and nonexistent value, use None for noneexistent and Empty for empty
    acl = RequiredText.using(label=L_("ACL")).with_properties(placeholder=L_(
        "Access Control List - Use 'None' for default")).validated_by(
            ACLValidator())
    summary = OptionalText.using(label=L_("Summary")).with_properties(
        placeholder=L_("One-line summary of the item"))
    name = Names
    tags = Tags
Example #28
0
class BaseMetaForm(Form):

    itemtype = RequiredText.using(label=L_("Item type")).with_properties(placeholder=L_("Item type"))
    contenttype = RequiredText.using(label=L_("Content type")).with_properties(placeholder=L_("Content type"))
    acl = RequiredText.using(label=L_("ACL")).with_properties(placeholder=L_("Access Control List")).validated_by(ACLValidator())
    # Disabled - Flatland doesn't distinguish emtpy value and nonexistent
    # value, while an emtpy acl and no acl have different semantics
    # acl = OptionalText.using(label=L_('ACL')).with_properties(placeholder=L_("Access Control List"))
    summary = OptionalText.using(label=L_("Summary")).with_properties(placeholder=L_("One-line summary of the item"))
    name = Names
    tags = Tags
Example #29
0
class TicketSubmitForm(TicketForm):
    submit_label = L_("Submit ticket")

    def _dump(self, item):
        # initial metadata for Ticket-itemtyped item
        meta = {
            ITEMTYPE: item.itemtype,
            # XXX support other markups
            CONTENTTYPE: 'text/x.moin.wiki;charset=utf-8',
            'closed': False,
        }
        meta.update(self['meta'].value)
        return meta, message_markup(self['message'].value)
Example #30
0
def test_text():
    # test for gettext
    result = _('test_text')
    assert result == 'test_text'

    # test for lazy_gettext
    result = L_('test_lazy_text')
    assert result == u'test_lazy_text'

    # test for ngettext
    result1 = N_('text1', 'text2', 1)
    assert result1 == 'text1'
    result2 = N_('text1', 'text2', 2)
    assert result2 == 'text2'