def on_request(self): session = self.request.session # populate organization (active root elements with no children or # active children (but not their parents)) self.organization.choices = [] self.organization.choices.append( ('', self.request.translate(_("Select one")))) query = session.query(Organization) query = query.filter(Organization.active.is_(True)) query = query.filter(Organization.parent_id.is_(None)) query = query.order_by(Organization.order) for root in query: if root.children: for child in root.children: if child.active: self.organization.choices.append( (child.name, child.title)) else: self.organization.choices.append((root.name, root.title)) # populate categories query = session.query(Category.name, Category.title) query = query.filter(Category.active.is_(True)) query = query.order_by(Category.order) self.category.choices = query.all() # populate issues now = utcnow() layout = Layout(None, self.request) self.issues.choices = [] query = session.query(Issue) query = query.order_by(Issue.date) if self.request.is_private(self.model): query = query.filter(date.today() < Issue.date) # publisher else: query = query.filter(now < Issue.deadline) # editor for issue in query: self.issues.choices.append( (issue.name, layout.format_issue(issue, date_format='date_with_weekday'))) if now >= issue.deadline: self.issues.render_kw['data-hot-issue'] = issue.name # Remove the print only option if not publisher if not self.request.is_private(self.model): self.delete_field('print_only')
def edit_user(self, request, form): """ Edit the role, name and email of a user. Publishers may only edit members. Admins can not be edited. """ layout = Layout(self, request) if self.role != 'member' and not request.is_secret(self): raise HTTPForbidden() if form.submitted(request): form.update_model(self) self.logout_all_sessions(request) request.message(_("User modified."), 'success') return redirect(layout.manage_users_link) if not form.errors: form.apply_model(self) return { 'layout': layout, 'form': form, 'title': self.title, 'subtitle': _("Edit User"), 'cancel': layout.manage_users_link }
def handle_password_reset(self, request, form): layout = Layout(self, request) callout = None show_form = True if form.submitted(request): if form.update_password(request): show_form = False request.message(_("Password changed."), 'success') return redirect(layout.homepage_link) else: form.error_message = _( "Wrong username or password reset link not valid any more." ) log.info( "Failed password reset attempt by {}".format( request.client_addr ) ) if 'token' in request.params: form.token.data = request.params['token'] return { 'layout': layout, 'title': _('Reset password'), 'form': form, 'show_form': show_form, 'callout': callout }
def view_notices_update(self, request, form): """ Updates all notices (of this state): Applies the categories, issues and organization from the meta informations. This view is not used normally and only intended when changing category names in the principal definition, for example. """ layout = Layout(self, request) session = request.session if form.submitted(request): for notice in self.query(): notice.apply_meta(session) request.message(_("Notices updated."), 'success') return redirect(layout.dashboard_or_notices_link) return { 'layout': layout, 'form': form, 'title': _("Update notices"), 'button_text': _("Update"), 'cancel': layout.dashboard_or_notices_link }
def test_issues_pdf_notice(gazette_app): session = gazette_app.session() with freeze_time("2017-01-01 12:00"): notice = GazetteNotice(title='title', text='text', author_place='place', author_date=utcnow(), author_name='author', state='drafted') notice.files.append(pdf_attachment('attachment')) session.add(notice) session.flush() layout = Layout(notice, DummyRequest(session, gazette_app.principal)) pdf = IssuePdf(BytesIO()) pdf.init_a4_portrait() pdf.notice(notice, layout, '1') assert extract_pdf_story(pdf) == [ '1', 'title', 'text', 'place, 1. Januar 2017<br/>author' ] notice.print_only = True pdf.notice(notice, layout, '2') assert extract_pdf_story(pdf) == [ '1', 'title', 'text', 'place, 1. Januar 2017<br/>author', '2', '<i>This official notice is only available in the print version.</i>' ]
def create_notice(self, request, form): """ Create a new notice. If a valid UID of a notice is given (via 'source' query parameter), its values are pre-filled in the form. This view is mainly used by the editors. """ layout = Layout(self, request) user = get_user(request) source = None if self.source: source = self.query().filter(GazetteNotice.id == self.source) source = source.first() if form.submitted(request): notice = self.add( title=form.title.data, text=form.text.data, author_place=form.author_place.data, author_date=form.author_date_utc, author_name=form.author_name.data, organization_id=form.organization.data, category_id=form.category.data, print_only=form.print_only.data if form.print_only else False, at_cost=form.at_cost.data == 'yes', billing_address=form.billing_address.data, user=get_user(request), issues=form.issues.data ) if form.phone_number.data: user.phone_number = form.phone_number.data if source: notice.note = source.note return redirect(request.link(notice)) if not form.errors: if source: form.apply_model(source) if form.print_only: form.print_only.data = False form.phone_number.data = user.phone_number return { 'layout': layout, 'form': form, 'title': _("New Official Notice"), 'helptext': _( "The fields marked with an asterisk * are mandatory fields." ), 'button_text': _("Save"), 'cancel': layout.dashboard_or_notices_link, 'current_issue': layout.current_issue }
def view_organizations_order(self, request): """ Reorder the list of organizations. This view is only visible by a publisher. """ layout = Layout(self, request) roots = self.query().filter(Organization.parent_id.is_(None)) return {'title': _("Organizations"), 'layout': layout, 'roots': roots}
def preview_notice(self, request): """ Preview the notice. """ layout = Layout(self, request) return { 'layout': layout, 'notice': self, 'export': request.link(self, name='preview-pdf') }
def handle_password_reset_request(self, request, form): """ Handles the password reset requests. """ show_form = True callout = None if form.submitted(request): users = UserCollection(request.session) user = users.by_username(form.email.data) if user: url = password_reset_url( user, request, request.link(self, name='reset-password') ) request.app.send_transactional_email( subject=request.translate(_("Password reset")), receivers=(user.username, ), reply_to=request.app.mail['transactional']['sender'], content=render_template( 'mail_password_reset.pt', request, { 'title': request.translate(_("Password reset")), 'model': None, 'url': url, 'layout': MailLayout(self, request) } ) ) else: log.info( "Failed password reset attempt by {}".format( request.client_addr ) ) show_form = False callout = _( ( 'A password reset link has been sent to ${email}, provided an ' 'account exists for this email address.' ), mapping={'email': form.email.data} ) return { 'layout': Layout(self, request), 'title': _('Reset password'), 'form': form, 'show_form': show_form, 'callout': callout }
def handle_notfound(self, request): @request.after def set_status_code(response): response.status_code = self.code # pass along 404 return { 'layout': Layout(self, request), 'title': _("Page not Found"), 'message': _("The page you are looking for could not be found."), }
def test_layout_links(): layout = Layout(None, DummyRequest(None)) assert layout.homepage_link == '/' assert layout.manage_users_link == '/UserCollection/' assert layout.manage_groups_link == '/UserGroupCollection/' assert layout.manage_organizations_link == '/OrganizationCollection/' assert layout.manage_categories_link == '/CategoryCollection/' assert layout.manage_issues_link == '/IssueCollection/' assert layout.manage_notices_link == '/GazetteNoticeCollection/' assert layout.dashboard_link == '/dashboard/' assert layout.dashboard_or_notices_link == '/dashboard/'
def view_dashboard(self, request): """ The dashboard view (for editors). Shows the drafted, submitted and rejected notices, shows warnings and allows to create a new notice. """ layout = Layout(self, request) user_ids, group_ids = get_user_and_group(request) collection = GazetteNoticeCollection(request.session, user_ids=user_ids, group_ids=group_ids) # rejected rejected = collection.for_state('rejected').query().all() if rejected: request.message(_("You have rejected messages."), 'warning') # drafted drafted = collection.for_state('drafted').query().all() now = utcnow() limit = now + timedelta(days=2) past_issues_selected = False deadline_reached_soon = False for notice in drafted: for issue in notice.issue_objects: if issue.deadline < now: past_issues_selected = True elif issue.deadline < limit: deadline_reached_soon = True if past_issues_selected: request.message(_("You have drafted messages with past issues."), 'warning') if deadline_reached_soon: request.message( _("You have drafted messages with issues close to the deadline."), 'warning') # submitted submitted = collection.for_state('submitted').query().all() new_notice = request.link(collection.for_state('drafted'), name='new-notice') return { 'layout': layout, 'title': _("Dashboard"), 'rejected': rejected, 'drafted': drafted, 'submitted': submitted, 'new_notice': new_notice, 'current_issue': layout.current_issue }
def view_archive(self, request): """ The archive. Shows all the weekly PDFs by year. """ layout = Layout(self, request) return { 'layout': layout, 'issues': IssueCollection(request.session).by_years(desc=True) }
def handle_forbidden(self, request): @request.after def set_status_code(response): response.status_code = self.code # pass along 403 return { 'layout': Layout(self, request), 'title': _("Access Denied"), 'message': _( "You are trying to open a page for which you are not authorized." ) }
def on_request(self): session = self.request.session layout = Layout(None, self.request) def title(item): return item.title if item.active else '({})'.format(item.title) # populate organization (root elements with no children or children # (but not their parents)) self.organization.choices = [] self.organization.choices.append( ('', self.request.translate(_("Select one")))) query = session.query(Organization) query = query.filter(Organization.parent_id.is_(None)) query = query.order_by(Organization.order) for root in query: if root.children: for child in root.children: self.organization.choices.append( (child.name, title(child))) else: self.organization.choices.append((root.name, title(root))) # populate categories self.category.choices = [] query = session.query(Category) query = query.order_by(Category.order) for category in query: self.category.choices.append((category.name, title(category))) # populate issues del self.issues.render_kw['data-limit'] self.issues.choices = [] query = session.query(Issue) query = query.order_by(Issue.date) for issue in query: self.issues.choices.append( (issue.name, layout.format_issue(issue, date_format='date_with_weekday')))
def view_user_sessions(self, request): """ View all open browser sessions. This view is only visible by an admin. """ layout = Layout(self, request) return { 'layout': layout, 'title': _('Sessions'), 'users': self.query().all() }
def accept_notice(self, request, form): """ Accept a notice. This view is used by the publishers to accept a submitted notice. Only submitted notices may be accepted. """ layout = Layout(self, request) if self.state != 'submitted' and self.state != 'imported': return { 'layout': layout, 'title': self.title, 'subtitle': _("Accept Official Note"), 'callout': _("Only submitted official notices may be accepted."), 'show_form': False } if (self.state == 'submitted' and (self.expired_issues or self.invalid_category or self.invalid_organization)): return redirect(request.link(self, name='edit')) if form.submitted(request): self.accept(request) request.message(_("Official notice accepted."), 'success') if request.app.principal.on_accept and self.state != 'imported': send_accepted_mail(request, self) self.add_change(request, _("mail sent")) return redirect(layout.dashboard_or_notices_link) return { 'message': _('Do you really want to accept "${item}"?', mapping={'item': self.title}), 'layout': layout, 'form': form, 'title': self.title, 'subtitle': _("Accept Official Note"), 'button_text': _("Accept Official Note"), 'cancel': request.link(self) }
def view_groups(self, request): """ View all the user groups. This view is only visible by an admin. """ layout = Layout(self, request) return { 'layout': layout, 'groups': self.query().all(), 'title': _('Groups'), 'new_group': request.link(self, name='new-group') }
def view_categories(self, request): """ View the list of categories. This view is only visible by an admin. """ layout = Layout(self, request) return { 'title': _("Categories"), 'layout': layout, 'categories': self.query().all(), 'export': request.link(self, name='export'), 'new_category': request.link(self, name='new-category') }
def from_notice(cls, notice, request): """ Create a PDF from a single notice. """ layout = Layout(None, request) result = BytesIO() pdf = cls(result, author=request.app.principal.name) pdf.init_a4_portrait(page_fn=page_fn_footer, page_fn_later=page_fn_header_and_footer) pdf.spacer() pdf.notice(notice, layout) pdf.generate() result.seek(0) return result
def view_notice_attachments(self, request): """ View all attachments to a single notice and allow to drop new attachments. Silently redirects to the notice view if the notice has already been accepted for non-admins. """ layout = Layout(self, request) upload_url = layout.csrf_protected_url(request.link(self, name='upload')) if self.state == 'accepted' or self.state == 'published': if not request.is_secret(self): return redirect(request.link(self)) return { 'layout': layout, 'title': self.title, 'subtitle': _("Attachments"), 'upload_url': upload_url, 'files': self.files, 'notice': self, }
def delete_group(self, request, form): """ Delete a user group. This view is only visible by an admin. """ layout = Layout(self, request) if self.official_notices: request.message(_("There are official notices linked to this group!"), 'warning') if self.users.count(): request.message(_('Only groups without users may be deleted.'), 'alert') return { 'layout': layout, 'title': self.name, 'subtitle': _("Delete Group"), 'show_form': False } if form.submitted(request): UserGroupCollection(request.session).delete(self) request.message(_("Group deleted."), 'success') return redirect(layout.manage_groups_link) return { 'message': _('Do you really want to delete "${item}"?', mapping={'item': self.name}), 'layout': layout, 'form': form, 'title': self.name, 'subtitle': _("Delete Group"), 'button_text': _("Delete Group"), 'button_class': 'alert', 'cancel': layout.manage_groups_link }
def view_organizations(self, request): """ View the list of organizations. This view is only visible by a publisher. """ layout = Layout(self, request) roots = self.query().filter(Organization.parent_id.is_(None)) return { 'title': _("Organizations"), 'layout': layout, 'roots': roots, 'export': request.link(self, name='export'), 'new_organization': request.link(self, name='new-organization'), 'order': request.link(self, name='order') }
def delete_organization(self, request, form): """ Delete a organization. Only unused organizations may be deleted. """ layout = Layout(self, request) session = request.session if self.children or self.in_use: request.message( _("Only unused organizations with no sub-organisations may be " "deleted."), 'alert') return { 'layout': layout, 'title': self.title, 'subtitle': _("Delete Organization"), 'show_form': False } if form.submitted(request): collection = OrganizationCollection(session) collection.delete(self) request.message(_("Organization deleted."), 'success') return redirect(layout.manage_organizations_link) return { 'message': _('Do you really want to delete "${item}"?', mapping={'item': self.title}), 'layout': layout, 'form': form, 'title': self.title, 'subtitle': _("Delete Organization"), 'button_text': _("Delete Organization"), 'button_class': 'alert', 'cancel': layout.manage_organizations_link }
def delete_attachment(self, request, form): """ Delete a notice attachment. """ layout = Layout(self, request) notice = self.linked_official_notices[0] if notice.state == 'accepted' or notice.state == 'published': if not request.is_secret(self): request.message( _("Attachments of accepted notices can not be deleted."), 'alert') return { 'layout': layout, 'title': self.name, 'subtitle': _("Delete"), 'show_form': False } if form.submitted(request): url = request.link(self.linked_official_notices[0], 'attachments') request.session.delete(self) request.message(_("Attachment deleted."), 'success') notice.add_change(request, _("Attachment deleted.")) return redirect(url) return { 'message': _('Do you really want to delete "${item}"?', mapping={'item': self.name}), 'layout': layout, 'form': form, 'title': self.name, 'subtitle': _("Delete"), 'button_text': _("Delete"), 'button_class': 'alert', 'cancel': request.link(self) }
def delete_user(self, request, form): """ Delete a user. Publishers may only edit members. Admins can not be deleted. """ layout = Layout(self, request) if self.role != 'member' and not request.is_secret(self): raise HTTPForbidden() if self.official_notices or self.changes: request.message(_("There are official notices linked to this user!"), 'warning') if form.submitted(request): collection = UserCollection(request.session) user = collection.by_username(self.username) if user.role != 'admin': self.logout_all_sessions(request) collection.delete(self.username) request.message(_("User deleted."), 'success') return redirect(layout.manage_users_link) return { 'message': _('Do you really want to delete "${item}"?', mapping={'item': self.title}), 'layout': layout, 'form': form, 'title': self.title, 'subtitle': _("Delete User"), 'button_text': _("Delete User"), 'button_class': 'alert', 'cancel': layout.manage_users_link }
def handle_login(self, request, form): """ Handles the login requests. """ layout = Layout(self, request) if form.submitted(request): response = self.login_to(request=request, **form.login_data) form.error_message = _("Wrong username or password") else: response = None return response or { 'layout': layout, 'title': _("Login"), 'form': form, 'password_reset_link': request.link( request.app.principal, name='request-password' ), }
def delete_issue(self, request, form): """ Delete a issue. Only unused issues may be deleted. This view is only visible by a publisher. """ layout = Layout(self, request) session = request.session if self.in_use: request.message( _("Only unused issues may be deleted."), 'alert' ) return { 'layout': layout, 'title': self.name, 'subtitle': _("Delete Issue"), 'show_form': False } if form.submitted(request): collection = IssueCollection(session) collection.delete(self) request.message(_("Issue deleted."), 'success') return redirect(layout.manage_issues_link) return { 'message': _( 'Do you really want to delete "${item}"?', mapping={'item': self.name} ), 'layout': layout, 'form': form, 'title': self.name, 'subtitle': _("Delete Issue"), 'button_text': _("Delete Issue"), 'button_class': 'alert', 'cancel': layout.manage_issues_link }
def create_group(self, request, form): """ Create a new user group. This view is only visible by an admin. """ layout = Layout(self, request) if form.submitted(request): self.add(name=form.name.data) request.message(_("Group added."), 'success') return redirect(layout.manage_groups_link) return { 'layout': layout, 'form': form, 'title': _("New Group"), 'cancel': layout.manage_groups_link }
def view_issues(self, request): """ View the list of issues. This view is only visible by a publisher. """ layout = Layout(self, request) today = date.today() past_issues = self.query().filter(Issue.date < today) past_issues = past_issues.order_by(None).order_by(Issue.date.desc()) next_issues = self.query().filter(Issue.date >= today) return { 'title': _("Issues"), 'layout': layout, 'past_issues': past_issues, 'next_issues': next_issues, 'new_issue': request.link(self, name='new-issue'), 'export': request.link(self, name='export'), }