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')
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.'))
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'))
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.'))
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.'))
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)
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)
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)
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()})
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)
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())
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)
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))
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)
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)
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')))
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)
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'))
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')
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))
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())
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')
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
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')
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
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 {}
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} )
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
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)
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)
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())
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))
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())
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)
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 })
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())
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()))
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.'))
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() ) )
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()))
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)
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)
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)
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)
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)
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')
def __init__(self, tree): raise FeedImportError(_('Importing of RSS feeds is currently ' 'not possible.'))
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')
def validator(form, value): if not value.strip(): raise ValidationError(_(u'The text must not be empty.'))
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
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'))