""" Implements the adding/editing/removing of pages. """
import morepath
from webob.exc import HTTPForbidden, HTTPNotFound
from onegov.core.elements import BackLink, Link
from onegov.core.security import Private
from onegov.org import _, OrgApp
from onegov.org.forms.page import MovePageForm, PageUrlForm, PageForm
from onegov.org.layout import EditorLayout, PageLayout
from onegov.org.management import PageNameChange
from onegov.org.models import Clipboard, Editor
from onegov.org.models.organisation import Organisation
from onegov.page import PageCollection
from typing import cast, TYPE_CHECKING
if TYPE_CHECKING:
from onegov.core.types import RenderData
from onegov.form import Form
from onegov.org.models import News, Topic
from onegov.org.request import OrgRequest
from onegov.page import Page
from webob import Response
@OrgApp.form(model=Editor, template='form.pt', permission=Private,
form=get_form_class)
[docs]
def handle_page_form(
self: Editor,
request: 'OrgRequest',
form: 'Form',
# FIXME: This is really bad, they should all use the same layout
layout: EditorLayout | PageLayout | None = None
) -> 'RenderData | Response':
if self.action == 'new':
return handle_new_page(self, request, form, layout=layout)
if self.action == 'new-root':
return handle_new_root_page(self, request, form, layout=layout)
elif self.action == 'edit':
return handle_edit_page(self, request, form, layout=layout)
elif self.action == 'change-url':
return handle_change_page_url(self, request, form, layout=layout)
elif self.action == 'paste':
clipboard = Clipboard.from_session(request)
src = clipboard.get_object()
clipboard.clear()
return handle_new_page(self, request, form, src, layout)
elif self.action == 'sort':
return morepath.redirect(request.link(self, 'sort'))
elif self.action == 'move':
return handle_move_page(self, request, form, layout=layout)
else:
raise NotImplementedError
[docs]
def handle_new_page(
self: Editor,
request: 'OrgRequest',
form: 'Form',
src: object | None = None,
layout: EditorLayout | PageLayout | None = None
) -> 'RenderData | Response':
assert self.page is not None
page = cast('Topic | News', self.page)
site_title = page.trait_messages[self.trait]['new_page_title']
if layout:
layout.site_title = site_title # type:ignore[union-attr]
if form.submitted(request):
pages = PageCollection(request.session)
added = cast('Topic | News', pages.add(
parent=page,
title=form['title'].data,
type=page.type,
meta={'trait': self.trait}
))
form.populate_obj(added)
request.success(added.trait_messages[self.trait]['new_page_added'])
return morepath.redirect(request.link(added))
if src:
form.process(obj=src)
layout = layout or EditorLayout(self, request, site_title)
layout.editmode_links[1] = Link(
text=_('Cancel'),
url=request.link(self.page),
attrs={'class': 'cancel-link'}
)
return {
'layout': layout,
'title': site_title,
'form': form,
'form_width': 'large'
}
[docs]
def handle_new_root_page(
self: Editor,
request: 'OrgRequest',
form: 'Form',
layout: EditorLayout | PageLayout | None = None
) -> 'RenderData | Response':
site_title = _('New Topic')
if layout:
layout.site_title = site_title # type:ignore[union-attr]
if form.submitted(request):
pages = PageCollection(request.session)
page = pages.add(
parent=None, # root page
title=form['title'].data,
type='topic',
meta={'trait': 'page'},
)
form.populate_obj(page)
request.success(_('Added a new topic'))
return morepath.redirect(request.link(page))
if not request.POST:
form.process(obj=self.page)
layout = layout or EditorLayout(self, request, site_title)
layout.edit_mode = True
layout.editmode_links[1] = Link(
text=_('Cancel'),
url=request.class_link(Organisation),
attrs={'class': 'cancel-link'}
)
return {
'layout': layout,
'title': site_title,
'form': form,
'form_width': 'large'
}
[docs]
def handle_edit_page(
self: Editor,
request: 'OrgRequest',
form: 'Form',
layout: EditorLayout | PageLayout | None = None
) -> 'RenderData | Response':
assert self.page is not None
site_title = self.page.trait_messages[self.trait]['edit_page_title']
layout = layout or EditorLayout(self, request, site_title)
layout.site_title = site_title # type:ignore[union-attr]
layout.edit_mode = True
layout.editmode_links[1] = BackLink(attrs={'class': 'cancel-link'})
if self.page.deletable and self.page.trait == 'link':
edit_links = self.page.get_edit_links(request)
layout.editbar_links = list(filter(
lambda link: getattr(link, 'text', '') == _('Delete'), edit_links
))
if form.submitted(request):
form.populate_obj(self.page)
request.success(_('Your changes were saved'))
return morepath.redirect(request.link(self.page))
elif not request.POST:
form.process(obj=self.page)
return {
'layout': layout,
'title': site_title,
'form': form,
'form_width': 'large'
}
[docs]
def handle_move_page(
self: Editor,
request: 'OrgRequest',
form: 'Form',
layout: EditorLayout | PageLayout | None = None
) -> 'RenderData | Response':
assert self.page is not None
layout = layout or PageLayout(self.page, request)
site_title = self.page.trait_messages[self.trait]['move_page_title']
layout.site_title = site_title # type:ignore[union-attr]
if form.submitted(request):
form.update_model(self.page) # type:ignore[attr-defined]
request.success(_('Your changes were saved'))
return morepath.redirect(request.link(self.page))
return {
'layout': layout,
'title': site_title,
'helptext': _('Moves the topic and all its sub topics to the '
'given destination.'),
'form': form,
}
[docs]
def handle_change_page_url(
self: Editor,
request: 'OrgRequest',
form: 'Form',
layout: EditorLayout | PageLayout | None = None
) -> 'RenderData | Response':
if not request.is_admin:
return HTTPForbidden()
assert self.page is not None
page = cast('Topic | News', self.page)
subpage_count = 0
def count_page(page: 'Page') -> None:
nonlocal subpage_count
subpage_count += 1
for child in page.children:
count_page(child)
for child in page.children:
count_page(child)
messages = [
_('Stable URLs are important. Here you can change the '
'path to your site independently from the title.'),
_('A total of ${number} subpages are affected.',
mapping={'number': subpage_count})
]
if form.submitted(request):
migration = PageNameChange.from_form(page, form)
link_count = migration.execute(test=form['test'].data)
if not form['test'].data:
# FIXME: I am not sure this is actually necessary
# the execution of the migration should cause
# a change in the pages tables, which in turn
# will clear these caches. But I suppose it doesn't
# hurt to clear them twice...
for prop_name in ('root_pages', 'pages_tree', 'homepage_pages'):
prop = getattr(request.__class__, prop_name)
for cache_key in prop.used_cache_keys:
request.app.cache.delete(cache_key)
request.success(_('Your changes were saved'))
@request.after
def must_revalidate(response: 'Response') -> None:
response.headers.add('cache-control', 'must-revalidate')
response.headers.add('cache-control', 'max-age=0, public')
response.headers['expires'] = '0'
return morepath.redirect(request.link(self.page))
messages.append(
_('${count} links will be replaced by this action.',
mapping={'count': link_count}))
elif not request.POST:
form.process(obj=self.page)
site_title = _('Change URL')
return {
'layout': layout or EditorLayout(self, request, site_title),
'form': form,
'title': site_title,
'callout': ' '.join(request.translate(m) for m in messages)
}
@OrgApp.html(
model=Editor,
template='sort.pt',
name='sort',
permission=Private
)
[docs]
def view_topics_sort(
self: Editor,
request: 'OrgRequest',
layout: EditorLayout | None = None
) -> 'RenderData':
if self.page is None:
raise HTTPNotFound()
page = cast('Topic | News', self.page)
layout = layout or EditorLayout(self, request, 'sort')
return {
'title': _('Sort'),
'layout': layout,
'page': page,
'pages': page.children
}