Example #1
0
    def __init__(self, title, author, text, slug=None, pub_date=None,
                 last_update=None, comments_enabled=True,
                 pings_enabled=True, status=STATUS_PUBLISHED,
                 parser=None, uid=None, content_type='entry', extra=None):
        app = get_application()
        self.content_type = content_type
        self.title = title
        self.author = author
        if parser is None:
            parser = app.cfg['default_parser']

        self.parser = parser
        self.text = text or u''
        if extra:
            self.extra = dict(extra)
        else:
            self.extra = {}

        self.comments_enabled = comments_enabled
        self.pings_enabled = pings_enabled
        self.status = status

        # set times now, they depend on status being set
        self.touch_times(pub_date)
        if last_update is not None:
            self.last_update = last_update

        # now bind the slug for which we need the times set.
        self.bind_slug(slug)

        # generate a UID if none is given
        if uid is None:
            uid = build_tag_uri(app, self.pub_date, content_type, self.slug)
        self.uid = uid
Example #2
0
    def get_list(self, endpoint=None, page=1, per_page=None,
                 url_args=None, raise_if_empty=True):
        """Return a dict with pagination, the current posts, number of pages,
        total posts and all that stuff for further processing.
        """
        if per_page is None:
            app = get_application()
            per_page = app.cfg['posts_per_page']

        # send the query
        offset = per_page * (page - 1)
        postlist = self.order_by(Post.pub_date.desc()) \
                       .offset(offset).limit(per_page).all()

        # if raising exceptions is wanted, raise it
        if raise_if_empty and (page != 1 and not postlist):
            raise NotFound()

        pagination = Pagination(endpoint, page, per_page,
                                self.count(), url_args)

        return {
            'pagination':       pagination,
            'posts':            postlist
        }
Example #3
0
 def __init__(self, initial=None):
     self.app = get_application()
     if initial is None:
         initial = {}
         for name in self.fields:
             initial[name] = self.app.cfg[name]
     forms.Form.__init__(self, initial)
Example #4
0
    def get_list(self,
                 endpoint=None,
                 page=1,
                 per_page=None,
                 url_args=None,
                 raise_if_empty=True):
        """Return a dict with pagination, the current posts, number of pages,
        total posts and all that stuff for further processing.
        """
        if per_page is None:
            app = get_application()
            per_page = app.cfg['posts_per_page']

        # send the query
        offset = per_page * (page - 1)
        postlist = self.order_by(Post.pub_date.desc()) \
                       .offset(offset).limit(per_page).all()

        # if raising exceptions is wanted, raise it
        if raise_if_empty and (page != 1 and not postlist):
            raise NotFound()

        pagination = Pagination(endpoint, page, per_page, self.count(),
                                url_args)

        return {'pagination': pagination, 'posts': postlist}
Example #5
0
    def __init__(self, post, author, text, email=None, www=None, parent=None,
                 pub_date=None, submitter_ip='0.0.0.0', parser=None,
                 is_pingback=False, status=COMMENT_MODERATED):
        self.post = post
        if isinstance(author, basestring):
            self.user = None
            self._author = author
            self._email = email
            self._www = www
        else:
            assert email is www is None, \
                'email and www can only be provided if the author is ' \
                'an anonymous user'
            self.user = author

        if parser is None:
            parser = get_application().cfg['comment_parser']
        self.parser = parser
        self.text = text or ''
        self.parent = parent
        if pub_date is None:
            pub_date = datetime.utcnow()
        self.pub_date = pub_date
        self.blocked_msg = None
        self.submitter_ip = submitter_ip
        self.is_pingback = is_pingback
        self.status = status
Example #6
0
 def __init__(self, post=None, initial=None):
     app = get_application()
     PostForm.__init__(self, post, forms.fill_dict(initial,
         comments_enabled=app.cfg['comments_enabled'],
         pings_enabled=app.cfg['pings_enabled'],
         ping_links=True
     ))
Example #7
0
def gen_timestamped_slug(slug, content_type, pub_date=None):
    """Generate a timestamped slug, suitable for use as final URL path."""
    from zine.application import get_application
    from zine.i18n import to_blog_timezone
    cfg = get_application().cfg
    if pub_date is None:
        pub_date = datetime.utcnow()
    pub_date = to_blog_timezone(pub_date)

    prefix = cfg['blog_url_prefix'].strip(u'/')
    if prefix:
        prefix += u'/'

    if content_type == 'entry':
        fixed = cfg['fixed_url_date_digits']
        def handle_match(match):
            handler = _slug_parts.get(match.group(1))
            if handler is None:
                return match.group(0)
            return handler(pub_date, slug, fixed)

        full_slug = prefix + _placeholder_re.sub(
            handle_match, cfg['post_url_format'])
    else:
        full_slug = u'%s%s' % (prefix, slug)
    return full_slug
Example #8
0
 def comments_closed(self):
     """True if commenting is no longer possible."""
     app = get_application()
     open_for = app.cfg['comments_open_for']
     if open_for == 0:
         return False
     return self.pub_date + timedelta(days=open_for) < datetime.utcnow()
Example #9
0
def gen_timestamped_slug(slug, content_type, pub_date=None):
    """Generate a timestamped slug, suitable for use as final URL path."""
    from zine.application import get_application
    from zine.i18n import to_blog_timezone
    cfg = get_application().cfg
    if pub_date is None:
        pub_date = datetime.utcnow()
    pub_date = to_blog_timezone(pub_date)

    prefix = cfg['blog_url_prefix'].strip(u'/')
    if prefix:
        prefix += u'/'

    if content_type == 'entry':
        fixed = cfg['fixed_url_date_digits']

        def handle_match(match):
            handler = _slug_parts.get(match.group(1))
            if handler is None:
                return match.group(0)
            return handler(pub_date, slug, fixed)

        full_slug = prefix + _placeholder_re.sub(handle_match,
                                                 cfg['post_url_format'])
    else:
        full_slug = u'%s%s' % (prefix, slug)
    return full_slug
Example #10
0
 def __getitem__(self, name):
     locale = str(get_application().locale)
     if name in self._i18n_values.get(locale, ()):
         return self._i18n_values[locale][name]
     if name in self._values:
         return self._values[name]
     raise KeyError(name)
Example #11
0
 def __getitem__(self, name):
     locale = str(get_application().locale)
     if name in self._i18n_values.get(locale, ()):
         return self._i18n_values[locale][name]
     if name in self._values:
         return self._values[name]
     raise KeyError(name)
Example #12
0
def get_engine():
    """Return the active database engine (the database engine of the active
    application).  If no application is enabled this has an undefined behavior.
    If you are not sure if the application is bound to the active thread, use
    :func:`~zine.application.get_application` and check it for `None`.
    The database engine is stored on the application object as `database_engine`.
    """
    from zine.application import get_application
    return get_application().database_engine
Example #13
0
def gen_slug(text, delim=u'-'):
    """Generates a proper slug for the given text.  It calls either
    `gen_ascii_slug` or `gen_unicode_slug` depending on the application
    configuration.
    """
    from zine.application import get_application
    if get_application().cfg['ascii_slugs']:
        return gen_ascii_slug(text, delim)
    return gen_unicode_slug(text, delim)
Example #14
0
def get_engine():
    """Return the active database engine (the database engine of the active
    application).  If no application is enabled this has an undefined behavior.
    If you are not sure if the application is bound to the active thread, use
    :func:`~zine.application.get_application` and check it for `None`.
    The database engine is stored on the application object as `database_engine`.
    """
    from zine.application import get_application
    return get_application().database_engine
Example #15
0
def gen_slug(text, delim=u'-'):
    """Generates a proper slug for the given text.  It calls either
    `gen_ascii_slug` or `gen_unicode_slug` depending on the application
    configuration.
    """
    from zine.application import get_application
    if get_application().cfg['ascii_slugs']:
        return gen_ascii_slug(text, delim)
    return gen_unicode_slug(text, delim)
Example #16
0
 def log(message, module=None):
     try:
         logger = get_application().log
     except AttributeError:
         warn(UnboundLogging('Tried to log %r but no application '
                             'was bound to the calling thread'
                             % message), stacklevel=2)
         return
     if level >= logger.level:
         logger.log(name, message, module, currentframe(1))
Example #17
0
File: log.py Project: peicheng/zine
 def log(message, module=None):
     try:
         logger = get_application().log
     except AttributeError:
         warn(UnboundLogging('Tried to log %r but no application '
                             'was bound to the calling thread' % message),
              stacklevel=2)
         return
     if level >= logger.level:
         logger.log(name, message, module, currentframe(1))
Example #18
0
 def parser_missing(self):
     """If the parser for this post is not available this property will
     be `True`.  If such as post is edited the text area is grayed out
     and tells the user to reinstall the plugin that provides that
     parser.  Because it doesn't know the name of the plugin, the
     preferred was is telling it the parser which is available using
     the `parser` property.
     """
     app = get_application()
     return self.parser not in app.parsers
Example #19
0
 def parser_missing(self):
     """If the parser for this post is not available this property will
     be `True`.  If such as post is edited the text area is grayed out
     and tells the user to reinstall the plugin that provides that
     parser.  Because it doesn't know the name of the plugin, the
     preferred was is telling it the parser which is available using
     the `parser` property.
     """
     app = get_application()
     return self.parser not in app.parsers
Example #20
0
    def theme_lightweight(self, key):
        """A query for lightweight settings based on the theme.  For example
        to use the lightweight settings for the author overview page you can
        use this query::

            Post.query.theme_lightweight('author_overview')
        """
        theme_settings = get_application().theme.settings
        deferred = theme_settings.get('sql.%s.deferred' % key)
        lazy = theme_settings.get('sql.%s.lazy' % key)
        return self.lightweight(deferred, lazy)
Example #21
0
 def __init__(self, tree):
     self.app = get_application()
     self.tree = tree
     self.tags = []
     self.categories = []
     self.authors = []
     self.posts = []
     self.blog = None
     self.extensions = [extension(self.app, self, tree)
                        for extension in self.app.feed_importer_extensions
                        if self.feed_type in extension.feed_types]
Example #22
0
    def theme_lightweight(self, key):
        """A query for lightweight settings based on the theme.  For example
        to use the lightweight settings for the author overview page you can
        use this query::

            Post.query.theme_lightweight('author_overview')
        """
        theme_settings = get_application().theme.settings
        deferred = theme_settings.get('sql.%s.deferred' % key)
        lazy = theme_settings.get('sql.%s.lazy' % key)
        return self.lightweight(deferred, lazy)
    def parse_post(self, entry):
        # parse the dates first.
        updated = parse_iso8601(entry.findtext(atom.updated))
        published = entry.findtext(atom.published)
        if published is not None:
            published = parse_iso8601(published)
        else:
            published = updated

        # figure out tags and categories by invoking the
        # callbacks on the extensions first.  If no extension
        # was able to figure out what to do with it, we treat it
        # as category.
        tags, categories = self.parse_categories(entry)

        link = entry.find(atom.link)
        if link is not None:
            link = link.attrib.get('href')

        post_parser = _pickle(entry.findall(textpress.data)[0].text).get('parser', 'html')
        if post_parser not in get_application().parsers:
            post_parser = 'html'

        post = Post(
            entry.findtext(textpress.slug),                 # slug
            _get_text_content(entry.findall(atom.title)),   # title
            link,                                           # link
            published,                                      # pub_date
            self.parse_author(entry),                       # author
            # XXX: the Post is prefixing the intro before the actual
            # content.  This is the default Zine behavior and makes sense
            # for Zine.  However nearly every blog works differently and
            # treats summary completely different from content.  We should
            # think about that.
            _get_html_content(entry.findall(atom.summary)), # intro
            _get_html_content(entry.findall(atom.content)), # body
            tags,                                           # tags
            categories,                                     # categories
            parser=post_parser,
            updated=updated,
            uid=entry.findtext(atom.id)
        )
        post.element = entry
        content_type = entry.findtext(textpress.content_type)
        if content_type not in ('page', 'entry'):
            post.content_type = 'entry'

        # now parse the comments for the post
        self.parse_comments(post)

        for extension in self.extensions:
            extension.postprocess_post(post)

        return post
Example #24
0
 def __init__(self, initial=None):
     self.app = app = get_application()
     self.active_plugins.choices = sorted([(x.name, x.display_name)
                                           for x in app.plugins.values()],
                                          key=lambda x: x[1].lower())
     if initial is None:
         initial = dict(
             active_plugins=[x.name for x in app.plugins.itervalues()
                             if x.active],
             disable_guard=not app.cfg['plugin_guard']
         )
     forms.Form.__init__(self, initial)
Example #25
0
 def __init__(self, tree):
     self.app = get_application()
     self.tree = tree
     self.tags = []
     self.categories = []
     self.authors = []
     self.posts = []
     self.blog = None
     self.extensions = [
         extension(self.app, self, tree)
         for extension in self.app.feed_importer_extensions
         if self.feed_type in extension.feed_types
     ]
Example #26
0
def exception(message=None, module=None, exc_info=None):
    """Logs an error plus the current or given exc info."""
    if exc_info is None:
        exc_info = sys.exc_info()
    try:
        logger = get_application().log
    except AttributeError:
        # no application, write the exception to stderr
        return print_exception(*exc_info)

    if LEVELS['error'] >= logger.level:
        message = (message and message + '\n' or '') + \
                  ''.join(format_exception(*exc_info)) \
                    .decode('utf-8', 'ignore')
        logger.log('error', message, module, currentframe(1))
Example #27
0
    def set_auto_slug(self):
        """Generate a slug for this post."""
        cfg = get_application().cfg
        slug = gen_slug(self.title)
        if not slug:
            slug = to_blog_timezone(self.pub_date).strftime('%H%M')

        full_slug = gen_timestamped_slug(slug, self.content_type,
                                         self.pub_date)

        if full_slug != self.slug:
            while Post.query.autoflush(False).filter_by(slug=full_slug) \
                      .limit(1).count():
                full_slug = increment_string(full_slug)
            self.slug = full_slug
Example #28
0
File: log.py Project: peicheng/zine
def exception(message=None, module=None, exc_info=None):
    """Logs an error plus the current or given exc info."""
    if exc_info is None:
        exc_info = sys.exc_info()
    try:
        logger = get_application().log
    except AttributeError:
        # no application, write the exception to stderr
        return print_exception(*exc_info)

    if LEVELS['error'] >= logger.level:
        message = (message and message + '\n' or '') + \
                  ''.join(format_exception(*exc_info)) \
                    .decode('utf-8', 'ignore')
        logger.log('error', message, module, currentframe(1))
Example #29
0
    def __init__(self, post=None, initial=None):
        self.app = get_application()
        self.post = post

        if post is not None:
            initial = forms.fill_dict(initial,
                title=post.title,
                text=post.text,
                status=post.status,
                pub_date=post.pub_date,
                slug=post.slug,
                author=post.author,
                tags=[x.name for x in post.tags],
                categories=[x.id for x in post.categories],
                parser=post.parser,
                comments_enabled=post.comments_enabled,
                pings_enabled=post.pings_enabled,
                ping_links=not post.parser_missing
            )
        else:
            initial = forms.fill_dict(initial, status=STATUS_DRAFT)

            # if we have a request, we can use the current user as a default
            req = get_request()
            if req and req.user:
                initial['author'] = req.user

        initial.setdefault('parser', self.app.cfg['default_parser'])

        self.author.choices = [x.username for x in User.query.all()]
        self.parser.choices = self.app.list_parsers()
        self.parser_missing = post and post.parser_missing
        if self.parser_missing:
            self.parser.choices.append((post.parser, _('%s (missing)') %
                                        post.parser.title()))

        self.categories.choices = [(c.id, c.name) for c in
                                   Category.query.all()]

        forms.Form.__init__(self, initial)

        # if we have have an old post and the parser is not missing and
        # it was published when the form was created we collect the old
        # posts so that we don't have to ping them another time.
        self._old_links = set()
        if self.post is not None and not self.post.parser_missing and \
           self.post.is_published:
            self._old_links.update(self.post.find_urls())
Example #30
0
 def requires_moderation(self):
     """This is `True` if the comment requires moderation with the
     current moderation settings.  This does not check if the comment
     is already moderated.
     """
     if not self.anonymous:
         return False
     moderate = get_application().cfg['moderate_comments']
     if moderate == MODERATE_ALL:
         return True
     elif moderate == MODERATE_NONE:
         return False
     return db.execute(comments.select(
         (comments.c.author == self._author) &
         (comments.c.email == self._email) &
         (comments.c.status == COMMENT_MODERATED)
     )).fetchone() is None
Example #31
0
 def requires_moderation(self):
     """This is `True` if the comment requires moderation with the
     current moderation settings.  This does not check if the comment
     is already moderated.
     """
     if not self.anonymous:
         return False
     moderate = get_application().cfg['moderate_comments']
     if moderate == MODERATE_ALL:
         return True
     elif moderate == MODERATE_NONE:
         return False
     return db.execute(
         comments.select(
             (comments.c.author == self._author)
             & (comments.c.email == self._email)
             & (comments.c.status == COMMENT_MODERATED))).fetchone() is None
Example #32
0
def bind_privileges(container, privileges):
    """Binds the privileges to the container.  The privileges can be a list
    of privilege names, the container must be a set.  This is called for
    the http roundtrip in the form validation.
    """
    app = get_application()
    current_map = dict((x.name, x) for x in container)
    currently_attached = set(x.name for x in container)
    new_privileges = set(privileges)

    # remove outdated privileges
    for name in currently_attached.difference(new_privileges):
        container.remove(current_map[name])

    # add new privileges
    for name in new_privileges.difference(currently_attached):
        container.add(app.privileges[name])
Example #33
0
def bind_privileges(container, privileges, user=None):
    """Binds the privileges to the container.  The privileges can be a list
    of privilege names, the container must be a set.  This is called for
    the HTTP round-trip in the form validation.
    """
    if not user:
        user = get_request().user
    app = get_application()
    notification_types = app.notification_manager.notification_types
    current_map = dict((x.name, x) for x in container)
    currently_attached = set(x.name for x in container)
    new_privileges = set(privileges)

    # remove out-dated privileges
    for name in currently_attached.difference(new_privileges):
        container.remove(current_map[name])
        # remove any privilege dependencies that are not attached to other
        # privileges
        if current_map[name].dependencies:
            for privilege in current_map[name].dependencies.iter_privileges():
                try:
                    container.remove(privilege)
                except KeyError:
                    # privilege probably already removed
                    pass

        # remove notification subscriptions that required the privilege
        # being deleted.
        for notification in user.notification_subscriptions:
            privs = notification_types[notification.notification_id].privileges
            if current_map[name] in privs.iter_privileges():
                db.session.delete(notification)
                break
            for privilege in current_map[name].dependencies:
                if privilege in privs.iter_privileges():
                    db.session.delete(notification)

    # add new privileges
    for name in new_privileges.difference(currently_attached):
        privilege = app.privileges[name]
        container.add(privilege)
        # add dependable privileges
        if privilege.dependencies:
            for privilege in privilege.dependencies.iter_privileges():
                container.add(privilege)
Example #34
0
def bind_privileges(container, privileges, user=None):
    """Binds the privileges to the container.  The privileges can be a list
    of privilege names, the container must be a set.  This is called for
    the HTTP round-trip in the form validation.
    """
    if not user:
        user = get_request().user
    app = get_application()
    notification_types = app.notification_manager.notification_types
    current_map = dict((x.name, x) for x in container)
    currently_attached = set(x.name for x in container)
    new_privileges = set(privileges)

    # remove out-dated privileges
    for name in currently_attached.difference(new_privileges):
        container.remove(current_map[name])
        # remove any privilege dependencies that are not attached to other
        # privileges
        if current_map[name].dependencies:
            for privilege in current_map[name].dependencies.iter_privileges():
                try:
                    container.remove(privilege)
                except KeyError:
                    # privilege probably already removed
                    pass

        # remove notification subscriptions that required the privilege
        # being deleted.
        for notification in user.notification_subscriptions:
            privs = notification_types[notification.notification_id].privileges
            if current_map[name] in privs.iter_privileges():
                db.session.delete(notification)
                break
            for privilege in current_map[name].dependencies:
                if privilege in privs.iter_privileges():
                    db.session.delete(notification)

    # add new privileges
    for name in new_privileges.difference(currently_attached):
        privilege = app.privileges[name]
        container.add(privilege)
        # add dependable privileges
        if privilege.dependencies:
            for privilege in privilege.dependencies.iter_privileges():
                container.add(privilege)
Example #35
0
 def __init__(self, subject=None, text='', to_addrs=None):
     self.app = app = get_application()
     self.subject = u' '.join(subject.splitlines())
     self.text = text
     from_addr = app.cfg['blog_email']
     if not from_addr:
         from_addr = 'noreply@' + urlparse(app.cfg['blog_url']) \
                 [1].split(':')[0]
     self.from_addr = u'%s <%s>' % (
         app.cfg['blog_title'],
         from_addr
     )
     self.to_addrs = []
     if isinstance(to_addrs, basestring):
         self.add_addr(to_addrs)
     else:
         for addr in to_addrs:
             self.add_addr(addr)
Example #36
0
    def __init__(self,
                 title,
                 author,
                 text,
                 slug=None,
                 pub_date=None,
                 last_update=None,
                 comments_enabled=True,
                 pings_enabled=True,
                 status=STATUS_PUBLISHED,
                 parser=None,
                 uid=None,
                 content_type='entry',
                 extra=None):
        app = get_application()
        self.content_type = content_type
        self.title = title
        self.author = author
        if parser is None:
            parser = app.cfg['default_parser']

        self.parser = parser
        self.text = text or u''
        if extra:
            self.extra = dict(extra)
        else:
            self.extra = {}

        self.comments_enabled = comments_enabled
        self.pings_enabled = pings_enabled
        self.status = status

        # set times now, they depend on status being set
        self.touch_times(pub_date)
        if last_update is not None:
            self.last_update = last_update

        # now bind the slug for which we need the times set.
        self.bind_slug(slug)

        # generate a UID if none is given
        if uid is None:
            uid = build_tag_uri(app, self.pub_date, content_type, self.slug)
        self.uid = uid
Example #37
0
File: net.py Project: peicheng/zine
def open_url(url, data=None, timeout=None,
             allow_internal_requests=True, **kwargs):
    """This function parses the URL and opens the connection.  The
    following protocols are supported:

    -   `http`
    -   `https`

    Per default requests to Zine itself trigger an internal request.  This
    can be disabled by setting `allow_internal_requests` to False.
    """
    app = get_application()
    if timeout is None:
        timeout = app.cfg['default_network_timeout']
    parts = urlparse.urlsplit(url)
    if app is not None:
        blog_url = urlparse.urlsplit(app.cfg['blog_url'])
        if allow_internal_requests and \
           parts.scheme in ('http', 'https') and \
           blog_url.netloc == parts.netloc and \
           parts.path.startswith(blog_url.path):
            path = parts.path[len(blog_url.path):].lstrip('/')
            method = kwargs.pop('method', None)
            if method is None:
                method = data is not None and 'POST' or 'GET'
            make_response = lambda *a: URLResponse(url, *a)
            return app.perform_subrequest(path.decode('utf-8'),
                                          url_decode(parts.query),
                                          method, data, timeout=timeout,
                                          response_wrapper=make_response,
                                          **kwargs)
    handler = _url_handlers.get(parts.scheme)
    if handler is None:
        raise URLError('unsupported URL schema %r' % parts.scheme)
    if isinstance(data, basestring):
        data = StringIO(data)
    try:
        obj = handler(parts, timeout, **kwargs)
        return obj.open(data)
    except Exception, e:
        if not isinstance(e, NetException):
            e = NetException('%s: %s' % (e.__class__.__name__, str(e)))
        raise e
    def parse_comments(self, post):
        comments = {}
        unresolved_parents = {}

        for element in post.element.findall(textpress.comment):
            author = element.find(textpress.author)
            dependency = author.attrib.get('dependency')
            if dependency is not None:
                author = self._get_author(author)
                email = www = None
            else:
                email = author.findtext(textpress.email)
                www = author.findtext(textpress.uri)
                author = author.findtext(textpress.name)

            body = element.findall(textpress.data)
            if body:
                pickled = _pickle(body[0].text)
                body = pickled.get('raw_body', u'')

                comment_parser = pickled.get('parser', 'html')
                if comment_parser not in get_application().parsers:
                    comment_parser = 'html'

            comment = Comment(
                author, body, email, www, None,
                parse_iso8601(element.findtext(textpress.published)),
                element.findtext(textpress.submitter_ip), comment_parser,
                _to_bool(element.findtext(textpress.is_pingback)),
                int(element.findtext(textpress.status)),
                element.findtext(textpress.blocked_msg),
                _parser_data(element.findtext(textpress.parser_data))
            )
            comments[int(element.findtext(textpress.id))] = comment
            parent = element.findtext(textpress.parent)
            if parent is not None or '':
                unresolved_parents[comment] = int(parent)

        for comment, parent_id in unresolved_parents.iteritems():
            comment.parent = comments[parent_id]

        return comments.values()
Example #39
0
def redirect(url,
             code=302,
             allow_external_redirect=False,
             force_scheme_change=False):
    """Return a redirect response.  Like Werkzeug's redirect but this
    one checks for external redirects too.  If a redirect to an external
    target was requested `BadRequest` is raised unless
    `allow_external_redirect` was explicitly set to `True`.

    Leading slashes are ignored which makes it unsuitable to redirect
    to URLs returned from `url_for` and others.  Use `redirect_to`
    to redirect to arbitrary endpoints or `_redirect` to redirect to
    unchecked resources outside the URL root.

    By default the redirect will not change the URL scheme of the current
    request (if there is one).  This behavior can be changed by setting
    the force_scheme_change to False.
    """
    # leading slashes are ignored, if we redirect to "/foo" or "foo"
    # does not matter, in both cases we want to be below our blog root.
    url = url.lstrip('/')

    if not allow_external_redirect:
        #: check if the url is on the same server
        #: and make it an external one
        try:
            url = check_external_url(get_application(), url)
        except ValueError:
            raise BadRequest()

    # keep the current URL schema if we have an active request if we
    # should.  If https enforcement is set we suppose that the blog_url
    # is already set to an https value.
    request = get_request()
    if request and not force_scheme_change and \
       not request.app.cfg['force_https']:
        url = request.environ['wsgi.url_scheme'] + ':' + url.split(':', 1)[1]

    return _redirect(url, code)
Example #40
0
def make_config_form():
    """Returns the form for the configuration editor."""
    app = get_application()
    fields = {}
    values = {}
    use_default_label = lazy_gettext(u'Use default value')

    for category in app.cfg.get_detail_list():
        items = {}
        values[category['name']] = category_values = {}
        for item in category['items']:
            items[item['name']] = forms.Mapping(
                value=item['field'],
                use_default=forms.BooleanField(use_default_label)
            )
            category_values[item['name']] = {
                'value':        item['value'],
                'use_default':  False
            }
        fields[category['name']] = forms.Mapping(**items)

    class _ConfigForm(forms.Form):
        values = forms.Mapping(**fields)
        cfg = app.cfg

        def apply(self):
            t = self.cfg.edit()
            for category, items in self.data['values'].iteritems():
                for key, d in items.iteritems():
                    if category != 'zine':
                        key = '%s/%s' % (category, key)
                    if d['use_default']:
                        t.revert_to_default(key)
                    else:
                        t[key] = d['value']
            t.commit()

    return _ConfigForm({'values': values})
Example #41
0
def redirect(url, code=302, allow_external_redirect=False):
    """Return a redirect response.  Like Werkzeug's redirect but this
    one checks for external redirects too.  If a redirect to an external
    target was requested `BadRequest` is raised unless
    `allow_external_redirect` was explicitly set to `True`.

    Leading slashes are ignored which makes it unsuitable to redirect
    to URLs returned from `url_for` and others.  Use `redirect_to`
    to redirect to arbitrary endpoints or `_redirect` to redirect to
    unchecked resources outside the URL root.
    """
    # leading slashes are ignored, if we redirect to "/foo" or "foo"
    # does not matter, in both cases we want to be below our blog root.
    url = url.lstrip('/')

    if not allow_external_redirect:
        #: check if the url is on the same server
        #: and make it an external one
        try:
            url = check_external_url(get_application(), url)
        except ValueError:
            raise BadRequest()
    return _redirect(url, code)
Example #42
0
def redirect(url, code=302, allow_external_redirect=False,
             force_scheme_change=False):
    """Return a redirect response.  Like Werkzeug's redirect but this
    one checks for external redirects too.  If a redirect to an external
    target was requested `BadRequest` is raised unless
    `allow_external_redirect` was explicitly set to `True`.

    Leading slashes are ignored which makes it unsuitable to redirect
    to URLs returned from `url_for` and others.  Use `redirect_to`
    to redirect to arbitrary endpoints or `_redirect` to redirect to
    unchecked resources outside the URL root.

    By default the redirect will not change the URL scheme of the current
    request (if there is one).  This behavior can be changed by setting
    the force_scheme_change to False.
    """
    # leading slashes are ignored, if we redirect to "/foo" or "foo"
    # does not matter, in both cases we want to be below our blog root.
    url = url.lstrip('/')

    if not allow_external_redirect:
        #: check if the url is on the same server
        #: and make it an external one
        try:
            url = check_external_url(get_application(), url)
        except ValueError:
            raise BadRequest()

    # keep the current URL schema if we have an active request if we
    # should.  If https enforcement is set we suppose that the blog_url
    # is already set to an https value.
    request = get_request()
    if request and not force_scheme_change and \
       not request.app.cfg['force_https']:
        url = request.environ['wsgi.url_scheme'] + ':' + url.split(':', 1)[1]

    return _redirect(url, code)
Example #43
0
def parse(input_data, parser=None, reason='unknown'):
    """Generate a doc tree out of the data provided.  If we are not in unbound
    mode the `process-doc-tree` event is sent so that plugins can modify
    the tree in place. The reason is useful for plugins to find out if they
    want to render it or now. For example a normal blog post would have the
    reason 'post', a comment 'comment', an isolated page from a plugin maybe
    'page' etc.
    """
    input_data = u'\n'.join(input_data.splitlines())
    app = get_application()
    if parser is None:
        try:
            parser = app.parsers[app.cfg['default_parser']]
        except KeyError:
            # the plugin that provided the default parser is not
            # longer available.  reset the config value to the builtin
            # parser and parse afterwards.
            t = app.cfg.edit()
            t.revert_to_default('default_parser')
            t.commit()
            parser = app.parsers[app.cfg['default_parser']]
    else:
        try:
            parser = app.parsers[parser]
        except KeyError:
            raise ValueError('parser %r does not exist' % (parser, ))

    tree = parser.parse(input_data, reason)

    #! allow plugins to alter the doctree.
    for callback in iter_listeners('process-doc-tree'):
        item = callback(tree, input_data, reason)
        if item is not None:
            tree = item

    return tree
Example #44
0
def parse(input_data, parser=None, reason='unknown'):
    """Generate a doc tree out of the data provided.  If we are not in unbound
    mode the `process-doc-tree` event is sent so that plugins can modify
    the tree in place. The reason is useful for plugins to find out if they
    want to render it or now. For example a normal blog post would have the
    reason 'post', a comment 'comment', an isolated page from a plugin maybe
    'page' etc.
    """
    input_data = u'\n'.join(input_data.splitlines())
    app = get_application()
    if parser is None:
        try:
            parser = app.parsers[app.cfg['default_parser']]
        except KeyError:
            # the plugin that provided the default parser is not
            # longer available.  reset the config value to the builtin
            # parser and parse afterwards.
            t = app.cfg.edit()
            t.revert_to_default('default_parser')
            t.commit()
            parser = app.parsers[app.cfg['default_parser']]
    else:
        try:
            parser = app.parsers[parser]
        except KeyError:
            raise ValueError('parser %r does not exist' % (parser,))

    tree = parser.parse(input_data, reason)

    #! allow plugins to alter the doctree.
    for callback in iter_listeners('process-doc-tree'):
        item = callback(tree, input_data, reason)
        if item is not None:
            tree = item

    return tree
Example #45
0
    def __init__(self,
                 post,
                 author,
                 text,
                 email=None,
                 www=None,
                 parent=None,
                 pub_date=None,
                 submitter_ip='0.0.0.0',
                 parser=None,
                 is_pingback=False,
                 status=COMMENT_MODERATED):
        self.post = post
        if isinstance(author, basestring):
            self.user = None
            self._author = author
            self._email = email
            self._www = www
        else:
            assert email is www is None, \
                'email and www can only be provided if the author is ' \
                'an anonymous user'
            self.user = author

        if parser is None:
            parser = get_application().cfg['comment_parser']
        self.parser = parser
        self.text = text or ''
        self.parent = parent
        if pub_date is None:
            pub_date = datetime.utcnow()
        self.pub_date = pub_date
        self.blocked_msg = None
        self.submitter_ip = submitter_ip
        self.is_pingback = is_pingback
        self.status = status
Example #46
0
 def privilege(self):
     return get_application().privileges.get(self.name)
Example #47
0
 def for_index(self):
     """Return all the types for the index."""
     types = get_application().cfg['index_content_types']
     if len(types) == 1:
         return self.filter_by(content_type=types[0])
     return self.filter(Post.content_type.in_(types))
Example #48
0
 def _privileges(self):
     return get_application().content_type_privileges[self.content_type]
Example #49
0
 def privilege(self):
     return get_application().privileges.get(self.name)
Example #50
0
 def as_dict(self):
     result = self._values.copy()
     result.update(self._i18n_values.get(str(get_application().locale), {}))
     return result
Example #51
0
 def for_index(self):
     """Return all the types for the index."""
     types = get_application().cfg['index_content_types']
     if len(types) == 1:
         return self.filter_by(content_type=types[0].strip())
     return self.filter(Post.content_type.in_([x.strip() for x in types]))
Example #52
0
def send_notification(type, message, user=Ellipsis):
    """Convenience function.  Get the application object and deliver the
    notification to it's NotificationManager.

    The message must be a valid ZEML formatted message.  The following
    top-level elements are available for marking up the message:

    title
        The title of the notification.  Some systems may only transmit this
        part of the message.

    summary
        An optional quick summary.  If the text is short enough it can be
        omitted and the system will try to transmit the longtext in that
        case.  The upper limit for the summary should be around 100 chars.

    details
        If given this may either contain a paragraph with textual information
        or an ordered or unordered list of text or links.  The general markup
        rules apply.

    longtext
        The full text of this notification.  May contain some formattings.

    actions
        If given this may contain an unordered list of action links.  These
        links may be transmitted together with the notification.

    Additionally if there is an associated page with the notification,
    somewhere should be a link element with a "selflink" class.  This can be
    embedded in the longtext or actions (but any other element too).

    Example markup::

        <title>New comment on "Foo bar baz"</title>
        <summary>Mr. Miracle wrote a new comment: "This is awesome."</summary>
        <details>
          <ul>
            <li><a href="http://miracle.invalid/">Mr. Miracle</a>
            <li><a href="mailto:[email protected]">E-Mail</a>
          </ul>
        </details>
        <longtext>
          <p>This is awesome.  Keep it up!
          <p>Love your work
        </longtext>
        <actions>
          <ul>
            <li><a href="http://.../link" class="selflink">all comments</a>
            <li><a href="http://.../?action=delete">delete it</a>
            <li><a href="http://.../?action=approve">approve it</a>
          </ul>
        </actions>

    Example plaintext rendering (e-mail)::

        Subject: New comment on "Foo bar baz"

        Mr. Miracle             http://miracle.invalid/
        E-Mail                  [email protected]

        > This is awesome.   Keep it up!
        > Love your work.

        Actions:
          - delete it           http://.../?action=delete
          - approve it          http://.../?action=approve

    Example IM notification rendering (jabber)::

        New comment on "Foo bar baz."  Mr. Miracle wrote anew comment:
        "This is awesome".  http://.../link
    """
    get_application().notification_manager.send(
        Notification(type, message, user)
    )
Example #53
0
    def generate(self, **options):
        """This method generates the pagination.  It accepts some
        keyword arguments that override the theme pagination settings.
        These arguments have the same name as the theme setting variables
        without the `pagination.` prefix.
        """
        from zine.application import url_for, get_application, \
             DEFAULT_THEME_SETTINGS

        if self._skip_theme_defaults:
            settings = DEFAULT_THEME_SETTINGS
        else:
            settings = get_application().theme.settings

        def _getopt(name):
            value = options.pop(name, None)
            if value is not None:
                return value
            return settings['pagination.' + name]
        normal = _getopt('normal')
        active = _getopt('active')
        commata = _getopt('commata')
        ellipsis = _getopt('ellipsis')
        threshold = _getopt('threshold')
        left_threshold = _getopt('left_threshold')
        right_threshold = _getopt('right_threshold')
        prev_link = _getopt('prev_link')
        next_link = _getopt('next_link')
        gray_prev_link = _getopt('gray_prev_link')
        gray_next_link = _getopt('gray_next_link')
        simple = _getopt('simple')
        if options:
            raise TypeError('generate() got an unexpected keyword '
                            'argument %r' % iter(options).next())

        was_ellipsis = False
        result = []
        prev = None
        next = None
        get_link = lambda x: url_for(self.endpoint, page=x,
                                     per_page=self.per_page,
                                     post_id=self.post_id, **self.url_args)

        if simple:
            result.append(active % {
                'url':      get_link(self.page),
                'page':     self.page
            })
            if self.page > 1:
                prev = self.page - 1
            if self.page < self.pages:
                next = self.page + 1
        else:
            for num in xrange(1, self.pages + 1):
                if num == self.page:
                    was_ellipsis = False
                if num - 1 == self.page:
                    next = num
                if num + 1 == self.page:
                    prev = num
                if num <= left_threshold or \
                   num > self.pages - right_threshold or \
                   abs(self.page - num) < threshold:
                    if result and result[-1] != ellipsis:
                        result.append(commata)
                    link = get_link(num)
                    template = num == self.page and active or normal
                    result.append(template % {
                        'url':      link,
                        'page':     num
                    })
                elif not was_ellipsis:
                    was_ellipsis = True
                    result.append(ellipsis)

        if next_link:
            if next is not None:
                result.append(u' <a href="%s" class="next">%s</a>' %
                              (get_link(next), _(u'Next »')))
            elif gray_next_link:
                result.append(u' <span class="disabled next">%s</span>' %
                              _(u'Next »'))
        if prev_link:
            if prev is not None:
                result.insert(0, u'<a href="%s" class="prev">%s</a> ' %
                              (get_link(prev), _(u'« Previous')))
            elif gray_prev_link:
                result.insert(0, u'<span class="disabled prev">%s</span> ' %
                              _(u'« Previous'))

        return u''.join(result)
Example #54
0
 def _privileges(self):
     return get_application().content_type_privileges[self.content_type]
Example #55
0
def make_external_url(path):
    """Return an external url for the given path."""
    return urljoin(get_application().cfg['blog_url'], path.lstrip('/'))
Example #56
0
def _strip_url(url):
    """Strip an URL so that only the path is left."""
    cfg = get_application().cfg
    if url.startswith(cfg['blog_url']):
        url = url[len(cfg['blog_url']):]
    return url.lstrip('/')
Example #57
0
def _strip_url(url):
    """Strip an URL so that only the path is left."""
    cfg = get_application().cfg
    if url.startswith(cfg['blog_url']):
        url = url[len(cfg['blog_url']):]
    return url.lstrip('/')