def configure(self, request):
        form = FeedImportForm()

        if request.method == 'POST' and form.validate(request.form):
            feed = request.files.get('feed')
            if form.data['download_url']:
                if not form.data['download_url'].endswith('.tpxa'):
                    error = _(u"Don't pass a real feed URL, it should be a "
                              u"regular URL where you're serving the file "
                              u"generated with the textpress_exporter.py script")
                    flash(error, 'error')
                    return self.render_admin_page('import_textpress.html',
                                                  form=form.as_widget(),
                                                  bugs_link=BUGS_LINK)
                try:
                    feed = urllib.urlopen(form.data['download_url'])
                except Exception, e:
                    error = _(u'Error downloading from URL: %s') % e
                    flash(error, 'error')
                    return self.render_admin_page('import_textpress.html',
                                                  form=form.as_widget(),
                                                  bugs_link=BUGS_LINK)
            elif not feed:
                return redirect_to('import/feed')

            try:
                blog = parse_feed(feed)
            except Exception, e:
                log.exception(_(u'Error parsing uploaded file'))
                print repr(e)
                flash(_(u'Error parsing feed: %s') % e, 'error')
Beispiel #2
0
 def context_validate(self, data):
     if data['cache_system'] == 'memcached':
         if not data['memcached_servers']:
             raise ValidationError(_(u'You have to provide at least one '
                                     u'server to use memcached.'))
     elif data['cache_system'] == 'filesystem':
         if not data['filesystem_cache_path']:
             raise ValidationError(_(u'You have to provide cache folder to '
                                     u'use filesystem cache.'))
Beispiel #3
0
 def validator(form, value):
     items = value.split()
     if len(items) > 1:
         raise ValidationError(_(u'You have to enter a valid net address.'))
     items = items[0].split(':')
     if len(items) not in (1, 2):
         raise ValidationError(_(u'You have to enter a valid net address.'))
     elif len(items) == 2 and not items[1].isdigit():
         raise ValidationError(_(u'The port has to be numeric'))
Beispiel #4
0
 def validator(form, value):
     items = value.split()
     if len(items) > 1:
         raise ValidationError(_(u'You have to enter a valid net address.'))
     items = items[0].split(':')
     if len(items) not in (1, 2):
         raise ValidationError(_(u'You have to enter a valid net address.'))
     elif len(items) == 2 and not items[1].isdigit():
         raise ValidationError(_(u'The port has to be numeric'))
Beispiel #5
0
 def validator(form, value):
     if '<' in value or '>' in value:
         raise ValidationError(_(u'Invalid character, < or > are not allowed.'))
     if value == '/':
         raise ValidationError(_(u'URL prefix must not be a sole slash.'))
     if value:
         if value[:1] != '/':
             raise ValidationError(_(u'URL prefix must start with a slash.'))
         if value[-1:] == '/':
             raise ValidationError(_(u'URL prefix must not end with a slash.'))
Beispiel #6
0
 def validator(form, value):
     if '<' in value or '>' in value:
         raise ValidationError(
             _(u'Invalid character, < or > are not allowed.'))
     if value == '/':
         raise ValidationError(_(u'URL prefix must not be a sole slash.'))
     if value:
         if value[:1] != '/':
             raise ValidationError(
                 _(u'URL prefix must start with a slash.'))
         if value[-1:] == '/':
             raise ValidationError(
                 _(u'URL prefix must not end with a slash.'))
Beispiel #7
0
def show_category(req, slug, page=1):
    """Show all posts categoryged with a given category slug.

    Available template variables:

        `posts`:
            a list of post objects we want to display

        `pagination`:
            a pagination object to render a pagination

        `category`
            the category object for this page

    :Template name: ``show_category.html``
    :URL endpoint: ``blog/show_category``
    """
    category = Category.query.filter_by(slug=slug).first(True)
    per_page = req.app.theme.settings['category.per_page']
    data = category.posts.theme_lightweight('category') \
                   .published().get_list(page=page, per_page=per_page,
                                         endpoint='blog/show_category',
                                         url_args=dict(slug=slug))

    add_link('alternate', url_for('blog/atom_feed', category=slug),
             'application/atom+xml', _(u'All posts in category %s') % category.name)
    return render_response('show_category.html', category=category, **data)
Beispiel #8
0
def archive(req, year=None, month=None, day=None, page=1):
    """Render the monthly archives.

    Available template variables:

        `posts`:
            a list of post objects we want to display

        `pagination`:
            a pagination object to render a pagination

        `year` / `month` / `day`:
            integers or None, useful to entitle the page

    :Template name: ``archive.html``
    :URL endpoint: ``blog/archive``
    """
    if not year:
        return render_response('archive.html', month_list=True,
                               **Post.query.published().for_index()
                                     .get_archive_summary())

    url_args = dict(year=year, month=month, day=day)
    per_page = req.app.theme.settings['archive.per_page']
    data = Post.query.theme_lightweight('archive_overview') \
               .published().for_index().date_filter(year, month, day) \
               .get_list(page=page, endpoint='blog/archive',
                         url_args=url_args, per_page=per_page)

    add_link('alternate', url_for('blog/atom_feed', **url_args),
             'application/atom+xml', _(u'Recent Posts Feed'))

    return render_response('archive.html', year=year, month=month, day=day,
                           date=date(year, month or 1, day or 1),
                           month_list=False, **data)
Beispiel #9
0
def show_author(req, username, page=1):
    """Show the user profile of an author / editor or administrator.

    Available template variables:

        `posts`:
            a list of post objects this author wrote and are
            visible on this page

        `pagination`:
            a pagination object to render a pagination

        `user`
            the user object for this author

    :Template name: ``show_author.html``
    :URL endpoint: ``blog/show_author``
    """
    user = User.query.filter_by(username=username).first()
    if user is None or not user.is_author:
        raise NotFound()

    per_page = req.app.theme.settings['author.per_page']
    data = user.posts.theme_lightweight('author').published() \
                     .get_list(page=page, per_page=per_page,
                               endpoint='blog/show_author',
                               url_args=dict(username=user.username))

    add_link('alternate', url_for('blog/atom_feed', author=user.username),
             'application/atom+xml', _(u'All posts written by %s') %
             user.display_name)

    return render_response('show_author.html', user=user, **data)
Beispiel #10
0
def make_import_form(blog):
    user_choices = [('__zine_create_user', _(u'Create new user'))] + [
        (user.id, user.username)
        for user in User.query.order_by('username').all()
    ]

    _authors = dict((author.id, forms.ChoiceField(author.username,
                                                  choices=user_choices))
                    for author in blog.authors)
    _posts = dict((post.id, forms.BooleanField(help_text=post.title)) for post
                  in blog.posts)
    _comments = dict((post.id, forms.BooleanField()) for post
                     in blog.posts)

    class _ImportForm(forms.Form):
        title = forms.BooleanField(lazy_gettext(u'Blog title'),
                                   help_text=blog.title)
        description = forms.BooleanField(lazy_gettext(u'Blog description'),
                                         help_text=blog.description)
        authors = forms.Mapping(_authors)
        posts = forms.Mapping(_posts)
        comments = forms.Mapping(_comments)
        load_config = forms.BooleanField(lazy_gettext(u'Load config values'),
                                         help_text=lazy_gettext(
                                         u'Load the configuration values '
                                         u'from the import.'))

        def perform_import(self):
            from zine.importers import perform_import
            return perform_import(get_application(), blog, self.data,
                                  stream=True)

    _all_true = dict((x.id, True) for x in blog.posts)
    return _ImportForm({'posts': _all_true.copy(),
                        'comments': _all_true.copy()})
Beispiel #11
0
def show_author(req, username, page=1):
    """Show the user profile of an author / editor or administrator.

    Available template variables:

        `posts`:
            a list of post objects this author wrote and are
            visible on this page.

        `pagination`:
            a pagination object to render a pagination

        `user`
            The user object for this author

    :Template name: ``show_author.html``
    :URL endpoint: ``blog/show_author``
    """
    user = User.query.filter_by(username=username).first()
    if user is None or not user.is_author:
        raise NotFound()

    per_page = req.app.theme.settings['author.per_page']
    data = user.posts.theme_lightweight('author').published() \
                     .get_list(page=page, per_page=per_page,
                               endpoint='blog/show_author',
                               url_args=dict(username=user.username))

    add_link('alternate', url_for('blog/atom_feed', author=user.username),
             'application/atom+xml', _(u'All posts written by %s') %
             user.display_name)

    return render_response('show_author.html', user=user, **data)
Beispiel #12
0
 def validator(form, value):
     if '<' in value or '>' in value or '\\' in value:
         raise ValidationError(_(u'Invalid character, <, > or \\ are not '
         'allowed.'))
     if value:
         if value.startswith('/'):
             raise ValidationError(_(u'URL format cannot start with a slash.'))
         if value.find('//') >= 0:
             raise ValidationError(_(u'URL cannot contain //.'))
         if value.find('/../') >= 0 or value.startswith('../'):
             raise ValidationError(_(u'URL cannot contain a reference to'
             'parent path.'))
         for match in _placeholder_re.finditer(value):
             if match.group(1) not in _slug_parts:
                 raise ValidationError(_(u'Unknown format code %s.') %
                                       match.group())
Beispiel #13
0
def show_tag(req, slug, page=1):
    """Show all posts categoryged with a given tag slug.

    Available template variables:

        `posts`:
            a list of post objects we want to display

        `pagination`:
            a pagination object to render a pagination

        `tag`
            the tag object for this page

    :Template name: ``show_tag.html``
    :URL endpoint: ``blog/show_tag``
    """
    tag = Tag.query.filter_by(slug=slug).first(True)
    per_page = req.app.theme.settings['tag.per_page']
    data = tag.posts.theme_lightweight('tag') \
                    .published().get_list(page=page, endpoint='blog/show_tag',
                                          per_page=per_page,
                                          url_args=dict(slug=slug))

    add_link('alternate', url_for('blog/atom_feed', tag=slug),
             'application/atom+xml', _(u'All posts tagged %s') % tag.name)
    return render_response('show_tag.html', tag=tag, **data)
Beispiel #14
0
def flash(msg, type="info"):
    """Add a message to the message flash buffer.

    The default message type is "info", other possible values are
    "add", "remove", "error", "ok" and "configure". The message type affects
    the icon and visual appearance.

    The flashes messages appear only in the admin interface!
    """
    assert type in ("info", "add", "remove", "error", "ok", "configure", "warning")
    if type == "error":
        msg = (u"<strong>%s:</strong> " % _("Error")) + msg
    if type == "warning":
        msg = (u"<strong>%s:</strong> " % _("Warning")) + msg

    local.request.session.setdefault("account/flashed_messages", []).append((type, msg))
Beispiel #15
0
def archive(req, year=None, month=None, day=None, page=1):
    """Render the monthly archives.

    Available template variables:

        `posts`:
            a list of post objects we want to display

        `pagination`:
            a pagination object to render a pagination

        `year` / `month` / `day`:
            integers or None, useful to entitle the page.

    :Template name: ``archive.html``
    :URL endpoint: ``blog/archive``
    """
    if not year:
        return render_response('archive.html', month_list=True,
                               **Post.query.published().for_index()
                                     .get_archive_summary())

    url_args = dict(year=year, month=month, day=day)
    per_page = req.app.theme.settings['archive.per_page']
    data = Post.query.theme_lightweight('archive_overview') \
               .published().for_index().date_filter(year, month, day) \
               .get_list(page=page, endpoint='blog/archive',
                         url_args=url_args, per_page=per_page)

    add_link('alternate', url_for('blog/atom_feed', **url_args),
             'application/atom+xml', _(u'Recent Posts Feed'))

    return render_response('archive.html', year=year, month=month, day=day,
                           date=date(year, month or 1, day or 1),
                           month_list=False, **data)
Beispiel #16
0
def show_category(req, slug, page=1):
    """Show all posts categoryged with a given category slug.

    Available template variables:

        `posts`:
            a list of post objects we want to display

        `pagination`:
            a pagination object to render a pagination

        `category`
            the category object for this page.

    :Template name: ``show_category.html``
    :URL endpoint: ``blog/show_category``
    """
    category = Category.query.filter_by(slug=slug).first(True)
    per_page = req.app.theme.settings['category.per_page']
    data = category.posts.theme_lightweight('category') \
                   .published().get_list(page=page, per_page=per_page,
                                         endpoint='blog/show_category',
                                         url_args=dict(slug=slug))

    add_link('alternate', url_for('blog/atom_feed', category=slug),
             'application/atom+xml', _(u'All posts in category %s') % category.name)
    return render_response('show_category.html', category=category, **data)
Beispiel #17
0
def add_config_link(req, navigation_bar):
    """Add a link to the reStructuredText options page"""
    if req.user.has_privilege(BLOG_ADMIN):
        for link_id, url, title, children in navigation_bar:
            if link_id == 'options':
                children.insert(2, ('restructuredtext',
                                    url_for('restructuredtext_parser/config'),
                                    _('reStructuredText')))
Beispiel #18
0
def is_valid_header_level(message=None):
    """Ensure level is between 1 and 6, inclusive."""
    if message is None:
        message = _('Header level must be between 1 and 6.')

    def validate(form, level):
        if not isinstance(level, int) or level < 1 or level > 6:
            raise ValidationError(message)
Beispiel #19
0
 def validate_slug(self, value):
     """Make sure the slug is unique."""
     query = Category.query.filter_by(slug=value)
     if self.category is not None:
         query = query.filter(Category.id != self.category.id)
     existing = query.first()
     if existing is not None:
         raise ValidationError(_('This slug is already in use'))
Beispiel #20
0
    def configure(self, request):
        form = WordPressImportForm()

        if request.method == 'POST' and form.validate(request.form):
            dump = request.files.get('dump')
            if form.data['download_url']:
                try:
                    dump = open_url(form.data['download_url']).stream
                except Exception, e:
                    error = _(u'Error downloading from URL: %s') % e
            elif not dump:
                return redirect_to('import/wordpress')

            try:
                blog = parse_feed(dump)
            except Exception, e:
                log.exception(_(u'Error parsing uploaded file'))
                flash(_(u'Error parsing uploaded file: %s') % e, 'error')
Beispiel #21
0
def flash(msg, type='info'):
    """Add a message to the message flash buffer.

    The default message type is "info", other possible values are
    "add", "remove", "error", "ok" and "configure". The message type affects
    the icon and visual appearance.

    The flashes messages appear only in the admin interface!
    """
    assert type in \
        ('info', 'add', 'remove', 'error', 'ok', 'configure', 'warning')
    if type == 'error':
        msg = (u'<strong>%s:</strong> ' % _('Error')) + msg
    if type == 'warning':
        msg = (u'<strong>%s:</strong> ' % _('Warning')) + msg

    local.request.session.setdefault('admin/flashed_messages', []).\
            append((type, msg))
Beispiel #22
0
def flash(msg, type='info'):
    """Add a message to the message flash buffer.

    The default message type is "info", other possible values are
    "add", "remove", "error", "ok" and "configure". The message type affects
    the icon and visual appearance.

    The flashes messages appear only in the admin interface!
    """
    assert type in \
        ('info', 'add', 'remove', 'error', 'ok', 'configure', 'warning')
    if type == 'error':
        msg = (u'<strong>%s:</strong> ' % _('Error')) + msg
    if type == 'warning':
        msg = (u'<strong>%s:</strong> ' % _('Warning')) + msg

    local.request.session.setdefault('admin/flashed_messages', []).\
            append((type, msg))
Beispiel #23
0
 def validator(form, value):
     if '<' in value or '>' in value or '\\' in value:
         raise ValidationError(
             _(u'Invalid character, <, > or \\ are not '
               'allowed.'))
     if value:
         if value.startswith('/'):
             raise ValidationError(
                 _(u'URL format cannot start with a slash.'))
         if value.find('//') >= 0:
             raise ValidationError(_(u'URL cannot contain //.'))
         if value.find('/../') >= 0 or value.startswith('../'):
             raise ValidationError(
                 _(u'URL cannot contain a reference to'
                   'parent path.'))
         for match in _placeholder_re.finditer(value):
             if match.group(1) not in _slug_parts:
                 raise ValidationError(
                     _(u'Unknown format code %s.') % match.group())
Beispiel #24
0
    def configure(self, request):
        form = FeedImportForm()

        if request.method == 'POST' and form.validate(request.form):
            feed = request.files.get('feed')
            if form.data['download_url']:
                try:
                    feed = open_url(form.data['download_url']).stream
                except Exception, e:
                    log.exception(_('Error downloading feed'))
                    flash(_(u'Error downloading from URL: %s') % e, 'error')
            if not feed:
                return redirect_to('import/feed')

            try:
                blog = parse_feed(feed)
            except Exception, e:
                log.exception(_(u'Error parsing uploaded file'))
                flash(_(u'Error parsing feed: %s') % e, 'error')
Beispiel #25
0
    def configure(self, request):
        form = FeedImportForm()

        if request.method == 'POST' and form.validate(request.form):
            feed = request.files.get('feed')
            if form.data['download_url']:
                try:
                    feed = open_url(form.data['download_url']).stream
                except Exception, e:
                    log.exception(_('Error downloading feed'))
                    flash(_(u'Error downloading from URL: %s') % e, 'error')
            if not feed:
                return redirect_to('import/feed')

            try:
                blog = parse_feed(feed)
            except Exception, e:
                log.exception(_(u'Error parsing uploaded file'))
                flash(_(u'Error parsing feed: %s') % e, 'error')
Beispiel #26
0
 def __init__(self, message_or_exception):
     if isinstance(message_or_exception, basestring):
         message = message_or_exception
         error = None
     else:
         message = _(u'Could not save configuration file: %s') % \
                   str(message_or_exception).decode('utf-8', 'ignore')
         error = message_or_exception
     InternalError.__init__(self, message)
     self.original_exception = error
Beispiel #27
0
    def configure(self, request):
        form = WordPressImportForm()

        if request.method == 'POST' and form.validate(request.form):
            dump = request.files.get('dump')
            if form.data['download_url']:
                try:
                    dump = open_url(form.data['download_url']).stream
                except Exception, e:
                    log.exception(_('Error downloading feed'))
                    flash(_(u'Error downloading from URL: %s') % e, 'error')
            if not dump:
                return redirect_to('import/wordpress')

            try:
                blog = parse_feed(dump)
            except Exception, e:
                raise
                log.exception(_(u'Error parsing uploaded file'))
                flash(_(u'Error parsing uploaded file: %s') % e, 'error')
Beispiel #28
0
def parse_feed(fd):
    tree = etree.parse(fd).getroot()
    if tree.tag == 'rss':
        parser_class = RSSParser
    elif tree.tag == atom.feed:
        parser_class = AtomParser
    else:
        raise FeedImportError(_('Unknown feed uploaded.'))
    parser = parser_class(tree)
    parser.parse()
    return parser.blog
Beispiel #29
0
 def process_result_value(self, value, dialect):
     from zine.utils.zeml import load_parser_data
     try:
         return load_parser_data(value)
     except ValueError: # Parser data invalid. Database corruption?
         from zine.i18n import _
         from zine.utils import log
         log.exception(_(u'Error when loading parsed data from database. '
                         u'Maybe the database was manually edited and got '
                         u'corrupted? The system returned an empty value.'))
         return {}
Beispiel #30
0
 def process_result_value(self, value, dialect):
     from zine.utils.zeml import load_parser_data
     try:
         return load_parser_data(value)
     except ValueError: # Parser data invalid. Database corruption?
         from zine.i18n import _
         from zine.utils import log
         log.exception(_(u'Error when loading parsed data from database. '
                         u'Maybe the database was manually edited and got '
                         u'corrupted? The system returned an empty value.'))
         return {}
Beispiel #31
0
def parse_feed(fd):
    tree = etree.parse(fd).getroot()
    if tree.tag == 'rss':
        parser_class = RSSParser
    elif tree.tag == atom.feed:
        parser_class = AtomParser
    else:
        raise FeedImportError(_('Unknown feed uploaded.'))
    parser = parser_class(tree)
    parser.parse()
    return parser.blog
Beispiel #32
0
def make_setup_error(exc_info=None):
    """Create a new SetupError for the last exception and log it."""
    if exc_info is None:
        exc_info = sys.exc_info()

    # log the exception
    log.exception(_(u"Plugin setup error"), "pluginsystem", exc_info)
    exc_type, exc_value, tb = exc_info

    # if the exception is already a SetupError we only
    # have to return it unchanged.
    if isinstance(exc_value, SetupError):
        return exc_value

    # otherwise create an error message for it and return a new
    # exception.
    error, (filename, line) = summarize_exception(exc_info)
    return SetupError(
        _(u"Exception happend on setup: " u"%(error)s (%(file)s, line %(line)d)")
        % {"error": escape(error), "file": filename, "line": line}
    )
Beispiel #33
0
 def __init__(self, user=None, initial=None):
     if user is not None:
         initial = forms.fill_dict(initial,
             username=user.username,
             real_name=user.real_name,
             display_name=user._display_name,
             description=user.description,
             email=user.email,
             www=user.www,
             privileges=[x.name for x in user.own_privileges],
             groups=[g.name for g in user.groups],
             is_author=user.is_author
         )
     _UserBoundForm.__init__(self, user, initial)
     self.display_name.choices = [
         (u'$username', user and user.username or _('Username')),
         (u'$real_name', user and user.real_name or _('Realname'))
     ]
     self.privileges.choices = self.app.list_privileges()
     self.groups.choices = [g.name for g in Group.query.all()]
     self.password.required = user is None
Beispiel #34
0
def list_documented_plugins(app):
    """Return a list of all documented plugins."""
    plugins = []
    for plugin in app.plugins.itervalues():
        if plugin.is_documented:
            plugins.append('<li><a href="%s">%s</a></li>' % (
                url_for('admin/help', page='plugins/%s/' % plugin.name),
                escape(plugin.display_name)
            ))
    if not plugins:
        return u'<ul><li>%s</li></ul>' % _('no documented plugins installed.')
    return '<ul>%s</ul>' % '\n'.join(plugins)
Beispiel #35
0
def list_documented_plugins(app):
    """Return a list of all documented plugins."""
    plugins = []
    for plugin in app.plugins.itervalues():
        if plugin.is_documented:
            plugins.append(
                '<li><a href="%s">%s</a></li>' %
                (url_for('admin/help', page='plugins/%s/' % plugin.name),
                 escape(plugin.display_name)))
    if not plugins:
        return u'<ul><li>%s</li></ul>' % _('no documented plugins installed.')
    return '<ul>%s</ul>' % '\n'.join(plugins)
Beispiel #36
0
def show_restructuredtext_config(req):
    """Show reStructuredText Parser configuration options."""
    form = ConfigurationForm(initial=dict(
        initial_header_level=req.app.cfg[CFG_HEADER_LEVEL]))

    if req.method == 'POST' and form.validate(req.form):
        if form.has_changed:
            req.app.cfg.change_single(CFG_HEADER_LEVEL,
                                      form['initial_header_level'])
            flash(_('reStructuredText Parser settings saved.'), 'ok')
    return render_admin_response('admin/restructuredtext_options.html',
                                 'options.restructuredtext',
                                 form=form.as_widget())
Beispiel #37
0
 def validator(form, value):
     if '<' in value or '>' in value or '\\' in value:
         raise ValidationError(
             _(u'Invalid character, <, > or \\ are not '
               'allowed.'))
     if value:
         if value.startswith('/'):
             raise ValidationError(
                 _(u'URL format cannot start with a slash.'))
         if value.find('//') >= 0:
             raise ValidationError(_(u'URL cannot contain //.'))
         if value.find('/../') >= 0 or value.startswith('../'):
             raise ValidationError(
                 _(u'URL cannot contain a reference to'
                   'parent path.'))
         for match in re.findall('%.*?%', value):
             if match not in [
                     '%year%', '%month%', '%day%', '%hour%', '%minute%',
                     '%second%'
             ]:
                 raise ValidationError(_(u'Unknown format code %s.' %
                                         match))
Beispiel #38
0
def profile(request):
    form = EditProfileForm(request.user)
    if request.method == 'POST':
        if request.form.get('cancel'):
            return form.redirect('account/index')
        elif request.form.get('delete'):
            return redirect_to('account/delete')
        elif form.validate(request.form):
            form.save_changes()
            db.commit()
            flash(_(u'Your profile was updated successfully.'), 'info')
            return form.redirect('account/index')
    return render_account_response('account/edit_profile.html', 'profile',
                                   form=form.as_widget())
Beispiel #39
0
def dispatch_content_type(req):
    """Show the post for a specific content type."""
    slug = req.path[1:]

    # feed for the post
    if slug.endswith('/feed.atom'):
        slug = slug[:-10]
        want_feed = True
    else:
        want_feed = False

    post = Post.query.filter_by(slug=slug).first()

    if post is None:
        # if the post does not exist, check if a post with a trailing slash
        # exists.  If it does, redirect to that post.  This is allows users
        # to emulate folders and to get relative links working.
        if not slug.endswith('/'):
            real_post = Post.query.filter_by(slug=slug + '/').first()
            if real_post is None:
                raise NotFound()
            # if we want the feed, we don't want a redirect
            elif want_feed:
                post = real_post
            else:
                return redirect_to(real_post)
        else:
            raise NotFound()

    # make sure the current user can access that page.
    if not post.can_read():
        raise Forbidden()

    # feed requested?  jump to the feed page
    if want_feed:
        return atom_feed(req, post=post)

    # create the comment form
    form = NewCommentForm(post, req.user)
    if post.comments_enabled or post.comments:
        add_link('alternate', post.comment_feed_url, 'application/atom+xml',
                 _(u'Comments Feed'))

    # now dispatch to the correct view
    handler = req.app.content_type_handlers.get(post.content_type)
    if handler is None:
        log.warn('No handler for the content type %r found.' % post.content_type)
        raise NotFound()

    return handler(req, post, form)
Beispiel #40
0
def dispatch_content_type(req):
    """Show the post for a specific content type."""
    slug = req.path[1:]

    # feed for the post
    if slug.endswith('/feed.atom'):
        slug = slug[:-10]
        want_feed = True
    else:
        want_feed = False

    post = Post.query.filter_by(slug=slug).first()

    if post is None:
        # if the post does not exist, check if a post with a trailing slash
        # exists.  If it does, redirect to that post.  This is allows users
        # to emulate folders and to get relative links working.
        if not slug.endswith('/'):
            real_post = Post.query.filter_by(slug=slug + '/').first()
            if real_post is None:
                raise NotFound()
            # if we want the feed, we don't want a redirect
            elif want_feed:
                post = real_post
            else:
                return redirect_to(real_post)
        else:
            raise NotFound()

    # make sure the current user can access that page.
    if not post.can_read():
        raise Forbidden()

    # feed requested?  jump to the feed page
    if want_feed:
        return atom_feed(req, post=post)

    # create the comment form
    form = NewCommentForm(post, req.user)
    if post.comments_enabled or post.comments:
        add_link('alternate', post.comment_feed_url, 'application/atom+xml',
                 _(u'Comments Feed'))

    # now dispatch to the correct view
    handler = req.app.content_type_handlers.get(post.content_type)
    if handler is None:
        log.warn('No handler for the content type %r found.' % post.content_type)
        raise NotFound()

    return handler(req, post, form)
Beispiel #41
0
def profile(request):
    form = EditProfileForm(request.user)
    if request.method == 'POST':
        if request.form.get('cancel'):
            return form.redirect('account/index')
        elif request.form.get('delete'):
            return redirect_to('account/delete')
        elif form.validate(request.form):
            form.save_changes()
            db.commit()
            flash(_(u'Your profile was updated successfully.'), 'info')
            return form.redirect('account/index')
    return render_account_response('account/edit_profile.html',
                                   'profile',
                                   form=form.as_widget())
Beispiel #42
0
def make_setup_error(exc_info=None):
    """Create a new SetupError for the last exception and log it."""
    if exc_info is None:
        exc_info = sys.exc_info()

    # log the exception
    log.exception(_(u'Plugin setup error'), 'pluginsystem', exc_info)
    exc_type, exc_value, tb = exc_info

    # if the exception is already a SetupError we only
    # have to return it unchanged.
    if isinstance(exc_value, SetupError):
        return exc_value

    # otherwise create an error message for it and return a new
    # exception.
    error, (filename, line) = summarize_exception(exc_info)
    return SetupError(
        _(u'Exception happend on setup: '
          u'%(error)s (%(file)s, line %(line)d)') % {
              'error': escape(error),
              'file': filename,
              'line': line
          })
Beispiel #43
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())
Beispiel #44
0
 def __init__(self, comment, initial=None):
     _CommentBoundForm.__init__(self, comment, forms.fill_dict(initial,
         author=comment.author,
         email=comment.email,
         www=comment.www,
         text=comment.text,
         pub_date=comment.pub_date,
         parser=comment.parser,
         blocked=comment.blocked,
         blocked_msg=comment.blocked_msg
     ))
     self.parser.choices = self.app.list_parsers()
     self.parser_missing = comment.parser_missing
     if self.parser_missing and comment.parser is not None:
         self.parser.choices.append((comment.parser, _('%s (missing)') %
                                     comment.parser.title()))
Beispiel #45
0
class LoginForm(forms.Form):
    """The form for the login page."""
    user = forms.ModelField(User, 'username', required=True, messages=dict(
        not_found=lazy_gettext(u'User “%(value)s” does not exist.'),
        required=lazy_gettext(u'You have to enter a username.')
    ), on_not_found=lambda user:
        log.warning(_(u'Failed login attempt, user “%s” does not exist')
                      % user, 'auth')
    )
    password = forms.TextField(widget=forms.PasswordInput)
    permanent = forms.BooleanField()

    def context_validate(self, data):
        if not data['user'].check_password(data['password']):
            log.warning(_(u'Failed login attempt from “%s”, invalid password')
                        % data['user'].username, 'auth')
            raise ValidationError(_('Incorrect password.'))
Beispiel #46
0
def notification_settings(request):
    """Allow the user to change his notification settings."""
    form = make_notification_form(request.user)
    if request.method == 'POST' and form.validate(request.form):
        form.apply()
        db.commit()
        flash(_('Notification settings changed.'), 'configure')
        return form.redirect('account/notification_settings')

    return render_account_response('account/notification_settings.html',
        'notifications', form=form.as_widget(), systems=sorted(
            request.app.notification_manager.systems.values(),
            key=lambda x: x.name.lower()
        ), notification_types=sorted(
            request.app.notification_manager.types(request.user),
            key=lambda x: x.description.lower()
        )
    )
Beispiel #47
0
def notification_settings(request):
    """Allow the user to change his notification settings."""
    form = make_notification_form(request.user)
    if request.method == 'POST' and form.validate(request.form):
        form.apply()
        db.commit()
        flash(_('Notification settings changed.'), 'configure')
        return form.redirect('account/notification_settings')

    return render_account_response(
        'account/notification_settings.html',
        'notifications',
        form=form.as_widget(),
        systems=sorted(request.app.notification_manager.systems.values(),
                       key=lambda x: x.name.lower()),
        notification_types=sorted(request.app.notification_manager.types(
            request.user),
                                  key=lambda x: x.description.lower()))
Beispiel #48
0
def index(req, page=1):
    """Render the most recent posts.

    Available template variables:

        `posts`:
            a list of post objects we want to display

        `pagination`:
            a pagination object to render a pagination

    :Template name: ``index.html``
    :URL endpoint: ``blog/index``
    """
    data = Post.query.published().for_index().get_list(endpoint='blog/index',
                                                       page=page)

    add_link('alternate', url_for('blog/atom_feed'), 'application/atom+xml',
             _(u'Recent Posts Feed'))
    return render_response('index.html', **data)
Beispiel #49
0
def index(req, page=1):
    """Render the most recent posts.

    Available template variables:

        `posts`:
            a list of post objects we want to display

        `pagination`:
            a pagination object to render a pagination

    :Template name: ``index.html``
    :URL endpoint: ``blog/index``
    """
    data = Post.query.theme_lightweight('index').published() \
               .for_index().get_list(endpoint='blog/index',
                                     page=page)

    add_link('alternate', url_for('blog/atom_feed'), 'application/atom+xml',
             _(u'Recent Posts Feed'))
    return render_response('index.html', **data)
Beispiel #50
0
def summarize_exception(exc_info):
    def _to_unicode(x):
        try:
            return unicode(x)
        except UnicodeError:
            return str(x).encode('utf-8', 'replace')

    exc_type, exc_value, tb = exc_info
    if isinstance(exc_type, basestring):
        prefix = _to_unicode(exc_type)
    else:
        prefix = _to_unicode(exc_type.__name__)
    message = _to_unicode(exc_value)

    filename = tb.tb_frame.f_globals.get('__file__')
    if filename is None:
        filename = _(u'unkown file')
    else:
        filename = _to_unicode(filename)
        if filename.endswith('.pyc'):
            filename = filename[:-1]

    return u'%s: %s' % (prefix, message), (filename, tb.tb_lineno)
Beispiel #51
0
def summarize_exception(exc_info):
    def _to_unicode(x):
        try:
            return unicode(x)
        except UnicodeError:
            return str(x).encode('utf-8', 'replace')

    exc_type, exc_value, tb = exc_info
    if isinstance(exc_type, basestring):
        prefix = _to_unicode(exc_type)
    else:
        prefix = _to_unicode(exc_type.__name__)
    message = _to_unicode(exc_value)

    filename = tb.tb_frame.f_globals.get('__file__')
    if filename is None:
        filename = _(u'unkown file')
    else:
        filename = _to_unicode(filename)
        if filename.endswith('.pyc'):
            filename = filename[:-1]

    return u'%s: %s' % (prefix, message), (filename, tb.tb_lineno)
Beispiel #52
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)
Beispiel #53
0
def render_account_response(template_name, _active_menu_item=None, **values):
    """Works pretty much like the normal `render_response` function but
    it emits some events to collect navigation items and injects that
    into the template context. This also gets the flashes messages from
    the user session and injects them into the template context after the
    plugins have provided theirs in the `before-account-response-rendered`
    event.

    The second parameter can be the active menu item if wanted. For example
    ``'account.notifications'`` would show the notifications button in the
    account submenu. If the menu is a standalone menu like the dashboard (no
    child items) you can also just use ``'dashboard'`` to highlight that.
    """
    request = get_request()

    # set up the core navigation bar
    navigation_bar = [
        ('dashboard', url_for('account/index'), _(u'Dashboard'), []),
        ('profile', url_for('account/profile'), _(u'Profile'), []),
        ('notifications', url_for('account/notification_settings'),
         _(u'Notifications'), [])
    ]

    # add the about items to the navigation bar
    system_items = [
        ('about', url_for('account/about_zine'), _(u'About'))
    ]
    if request.user.is_admin:
        # Current documentation is addressed for admins
        system_items.append(('help', url_for('account/help'), _(u'Help')))

    navigation_bar.append(('system', system_items[0][1], _(u'System'),
                           system_items))

    #! allow plugins to extend the navigation bar
    emit_event('modify-account-navigation-bar', request, navigation_bar)

    # find out which is the correct menu and submenu bar
    active_menu = active_submenu = None
    if _active_menu_item is not None:
        p = _active_menu_item.split('.')
        if len(p) == 1:
            active_menu = p[0]
        else:
            active_menu, active_submenu = p
    for id, url, title, subnavigation_bar in navigation_bar:
        if id == active_menu:
            break
    else:
        subnavigation_bar = []


    #! used to flash messages, add links to stylesheets, modify the admin
    #! context etc.
    emit_event('before-account-response-rendered', request, values)

    # the admin variables is pushed into the context after the event was
    # sent so that plugins can flash their messages. If we would emit the
    # event afterwards all flashes messages would appear in the request
    # after the current request.
    values['account'] = {
        'user_can_enter_admin_panel':
                        request.user.has_privilege(ENTER_ADMIN_PANEL),
        'navbar': [{
            'id':       id,
            'url':      url,
            'title':    title,
            'active':   active_menu == id
        } for id, url, title, children in navigation_bar],
        'ctxnavbar': [{
            'id':       id,
            'url':      url,
            'title':    title,
            'active':   active_submenu == id
        } for id, url, title in subnavigation_bar],
        'messages': [{
            'type':     type,
            'msg':      msg
        } for type, msg in request.session.pop('account/flashed_messages', [])],
        'active_pane': _active_menu_item
    }
    return render_response(template_name, **values)
Beispiel #54
0
def _perform_import(app, blog, d):
    # import models here because they have the same names as our
    # importer objects this module exports
    from zine.models import User, Tag, Category, Post, Comment
    author_mapping = {}
    tag_mapping = {}
    category_mapping = {}

    def prepare_author(author):
        """Adds an author to the author mapping and returns it."""
        if author.id not in author_mapping:
            author_rewrite = d['authors'][author.id]
            if author_rewrite != '__zine_create_user':
                user = User.query.get(int(author_rewrite))
            else:
                query = User.query.filter_by(username=author.username)
                user = query.first()
                if user is None:
                    user = User(author.username, None, author.email,
                                author.real_name, author.description,
                                author.www, author.is_author)
                    if author.pw_hash:
                        user.pw_hash = author.pw_hash
                    user.own_privileges.update(author.privileges)
            author_mapping[author.id] = user
        return author_mapping[author.id]

    def prepare_tag(tag):
        """Get a tag for a tag."""
        t = tag_mapping.get(tag.slug)
        if t is not None:
            return t
        t = Tag.query.filter_by(slug=tag.slug).first()
        if t is not None:
            tag_mapping[tag.slug] = t
            return t
        t = Tag.query.filter_by(name=tag.name).first()
        if t is not None:
            tag_mapping[tag.slug] = t
            return t
        t = tag_mapping[tag.id] = Tag(tag.name, tag.slug)
        return t

    def prepare_category(category):
        """Get a category for a category."""
        c = category_mapping.get(category.slug)
        if c is not None:
            return c
        c = Category.query.filter_by(slug=category.slug).first()
        if c is not None:
            category_mapping[category.slug] = c
            return c
        c = Category.query.filter_by(name=category.name).first()
        if c is not None:
            category_mapping[category.slug] = c
            return c
        c = category_mapping[category.id] = Category(category.name,
                                                     category.description,
                                                     category.slug)
        return c

    # start debug output
    yield u'<ul>'

    # update blog configuration if user wants that
    if d['title']:
        app.cfg.change_single('blog_title', blog.title)
        yield u'<li>%s</li>\n' % _('set blog title from dump')
    if d['description']:
        app.cfg.change_single('blog_tagline', blog.description)
        yield u'<li>%s</li>\n' % _('set blog tagline from dump')

    # convert the posts now
    for old_post in blog.posts:
        # in theory that will never happen because there are no
        # checkboxes for already imported posts on the form, but
        # who knows what users manage to do and also skip posts
        # we don't want converted
        if old_post.already_imported or not d['posts'][old_post.id]:
            continue

        slug = old_post.slug
        while Post.query.autoflush(False).filter_by(slug=slug) \
                  .limit(1).count():
            slug = increment_string(slug)
        post = Post(old_post.title, prepare_author(old_post.author),
                    old_post.text, slug, old_post.pub_date,
                    old_post.updated, old_post.comments_enabled,
                    old_post.pings_enabled, parser=old_post.parser,
                    uid=old_post.uid, content_type=old_post.content_type,
                    status=old_post.status, extra=old_post.extra)
        if old_post.parser_data is not None:
            post.parser_data.clear()
            post.parser_data.update(old_post.parser_data)
        yield u'<li><strong>%s</strong>' % escape(post.title)

        for tag in old_post.tags:
            post.tags.append(prepare_tag(tag))
            yield u'.'

        for category in old_post.categories:
            post.categories.append(prepare_category(category))
            yield u'.'

        # now the comments if user wants them.
        if d['comments'][old_post.id]:
            to_create = set(old_post.comments)
            created = {}

            def _create_comment(comment):
                parent = None
                if comment.parent is not None:
                    if comment.parent in created:
                        parent = created[comment.parent]
                    else:
                        parent = _create_comment(comment.parent)
                    to_create.discard(comment.parent)
                if isinstance(comment.author, Author):
                    author = prepare_author(comment.author)
                else:
                    author = comment.author
                rv = Comment(post, author, comment.body,
                             comment.author_email, comment.author_url, parent,
                             comment.pub_date, comment.remote_addr,
                             comment.parser, comment.is_pingback,
                             comment.status)
                if comment.blocked_msg:
                    rv.blocked_msg = comment.blocked_msg
                created[comment] = rv
                return rv

            while to_create:
                _create_comment(to_create.pop())
                yield u'.'

        yield u' <em>%s</em></li>\n' % _('done')

    # send to the database
    yield u'<li>%s' % _('Committing transaction...')
    db.commit()

    # write config if we have
    if d['load_config']:
        yield u'<li>%s' % _('Updating configuration...')
        t = app.cfg.edit()
        for key, value in blog.config.iteritems():
            if key in t and key not in ignored_config_keys:
                t.set_from_string(key, value)
        t.commit()

    yield u' <em>%s</em></li></ul>' % _('done')
Beispiel #55
0
 def __init__(self, tree):
     raise FeedImportError(_('Importing of RSS feeds is currently '
                             'not possible.'))
Beispiel #56
0
def populate_feed(req, feed, author=None, year=None, month=None, day=None,
              category=None, tag=None, post=None):
    """Renders an atom feed requested.

    :URL endpoint: ``blog/atom_feed``
    """
    # the feed only contains published items
    query = Post.query.lightweight(lazy=('comments',)).published()

    # feed for a category
    if category is not None:
        category = Category.query.filter_by(slug=category).first(True)
        query = query.filter(Post.categories.contains(category))

    # feed for a tag
    if tag is not None:
        tag = Tag.query.filter_by(slug=tag).first(True)
        query = query.filter(Post.tags.contains(tag))

    # feed for an author
    if author is not None:
        author = User.query.filter_by(username=author).first(True)
        query = query.filter(Post.author == author)

    # feed for dates
    if year is not None:
        query = query.for_index().date_filter(year, month, day)

    # if no post slug is given we filter the posts by the cretereons
    # provided and pass them to the feed builder.  This will only return
    # a feed for posts with a content type listed in `index_content_types`
    if post is None:
        for post in query.for_index().order_by(Post.pub_date.desc()) \
                         .limit(15).all():
            alt_title = '%s @ %s' % (post.author.display_name, post.pub_date)
            feed.add_item(post.title or alt_title,
                          url_for(post, _external=True), unicode(post.body),
                          author_name=post.author.display_name,
                          pubdate=post.pub_date, unique_id=post.uid)

    # otherwise we create a feed for all the comments of a post.
    # the function is called this way by `dispatch_content_type`.
    else:
        comment_num = 1
        for comment in post.comments:
            if not comment.visible:
                continue
            uid = build_tag_uri(req.app, comment.pub_date, 'comment',
                                comment.id)
            title = _(u'Comment %(num)d on %(post)s') % {
                'num':  comment_num,
                'post': post.title
            }
            author = {'name': comment.author}
            if comment.www:
                author['uri'] = comment.www
            feed.add_item(title, url_for(comment, _external=True),
                          unicode(comment.body), author_name=author,
                          pubdate=comment.pub_date, unique_id=uid)
            comment_num += 1

    return feed.writeString('utf-8')
Beispiel #57
0
 def validator(form, value):
     if not value.strip():
         raise ValidationError(_(u'The text must not be empty.'))
Beispiel #58
0
                try:
                    feed = open_url(form.data['download_url']).stream
                except Exception, e:
                    log.exception(_('Error downloading feed'))
                    flash(_(u'Error downloading from URL: %s') % e, 'error')
            if not feed:
                return redirect_to('import/feed')

            try:
                blog = parse_feed(feed)
            except Exception, e:
                log.exception(_(u'Error parsing uploaded file'))
                flash(_(u'Error parsing feed: %s') % e, 'error')
            else:
                self.enqueue_dump(blog)
                flash(_(u'Added imported items to queue.'))
                return redirect_to('admin/import')

        return self.render_admin_page('admin/import_feed.html',
                                      form=form.as_widget())


class Extension(object):
    """Extensions are instanciated for each parsing process."""
    feed_types = frozenset()

    def __init__(self, app, parser, root):
        self.app = app
        self.parser = parser
        self.root = root
Beispiel #59
0
 def validator(form, value):
     if len(value) > 200:
         raise ValidationError(_(u'The slug is too long'))
     elif value.startswith('/'):
         raise ValidationError(_(u'The slug must not start with a slash'))