misc: remove root directory changes (#72822)

This commit is contained in:
Frédéric Péters 2022-12-29 11:03:00 +01:00
parent af815ab7e5
commit 280c79a94a
2 changed files with 1 additions and 506 deletions

View File

@ -1,212 +1,14 @@
import urllib.parse
from quixote import get_publisher, get_response, get_request, redirect, get_session
from quixote.directory import Directory
from quixote.html import TemplateIO, htmltext
from wcs.qommon import _
from wcs.qommon.misc import get_variadic_url, simplify
import os
import re
import string
try:
import lasso
except ImportError:
pass
import wcs import wcs
import wcs.root import wcs.root
from wcs import qommon from wcs import qommon
from wcs.forms.root import RootDirectory as FormsRootDirectory
from wcs.qommon import N_, get_cfg, get_logger
from wcs.qommon import template
from wcs.qommon import errors
from wcs.qommon.form import *
from wcs.qommon import logger
from wcs.roles import logged_users_role
from wcs.qommon import emails
from wcs.qommon.sms import SMS
from wcs.categories import Category
from wcs.formdef import FormDef
from wcs.data_sources import NamedDataSource
from wcs.qommon.tokens import Token
from wcs.qommon.admin.emails import EmailsDirectory
from wcs.qommon.admin.texts import TextsDirectory
import wcs.forms.root import wcs.forms.root
from wcs.workflows import Workflow
from wcs.forms.preview import PreviewDirectory from wcs.forms.preview import PreviewDirectory
from .saml2 import Saml2Directory from .saml2 import Saml2Directory
OldRootDirectory = wcs.root.RootDirectory OldRootDirectory = wcs.root.RootDirectory
import wcs.qommon.ident.password
import wcs.qommon.ident.idp
def category_get_homepage_position(self):
if hasattr(self, 'homepage_position') and self.homepage_position:
return self.homepage_position
if self.url_name == 'consultations':
return '2nd'
return '1st'
Category.get_homepage_position = category_get_homepage_position
def category_get_limit(self):
if hasattr(self, 'limit') and self.limit is not None:
return self.limit
return 7
Category.get_limit = category_get_limit
Category.TEXT_ATTRIBUTES = ['name', 'url_name', 'description', 'homepage_position']
Category.INT_ATTRIBUTES = ['position', 'limit']
OldRegisterDirectory = wcs.root.RegisterDirectory
class AlternateRegisterDirectory(OldRegisterDirectory):
def _q_traverse(self, path):
return OldRegisterDirectory._q_traverse(self, path)
def _q_index(self):
get_logger().info('register')
ident_methods = get_cfg('identification', {}).get('methods', [])
if len(ident_methods) == 0:
idps = get_cfg('idp', {})
if len(idps) == 0:
return template.error_page(_('Authentication subsystem is not yet configured.'))
ident_methods = ['idp'] # fallback to old behaviour; saml.
if len(ident_methods) == 1:
method = ident_methods[0]
else:
method = 'password'
return wcs.qommon.ident.register(method)
OldLoginDirectory = wcs.root.LoginDirectory
class AlternateLoginDirectory(OldLoginDirectory):
def _q_traverse(self, path):
return OldLoginDirectory._q_traverse(self, path)
def _q_index(self):
get_logger().info('login')
ident_methods = get_cfg('identification', {}).get('methods', [])
if get_request().form.get('ReturnUrl'):
get_request().form['next'] = get_request().form.pop('ReturnUrl')
if 'IsPassive' in get_request().form and 'idp' in ident_methods:
# if isPassive is given in query parameters, we restrict ourselves
# to saml login.
ident_methods = ['idp']
if len(ident_methods) > 1 and 'idp' in ident_methods:
# if there is more than one identification method, and there is a
# possibility of SSO, if we got there as a consequence of an access
# unauthorized url on admin/ or backoffice/, then idp auth method
# is chosen forcefully.
after_url = get_request().form.get('next')
if after_url:
root_url = get_publisher().get_root_url()
after_path = urllib.parse.urlparse(after_url)[2]
after_path = after_path[len(root_url) :]
if after_path.startswith(str('admin')) or after_path.startswith(str('backoffice')):
ident_methods = ['idp']
# don't display authentication system choice
if len(ident_methods) == 1:
method = ident_methods[0]
try:
return wcs.qommon.ident.login(method)
except KeyError:
get_logger().error('failed to login with method %s' % method)
return errors.TraversalError()
if sorted(ident_methods) == ['idp', 'password']:
r = TemplateIO(html=True)
get_response().breadcrumb.append(('login', _('Login')))
identities_cfg = get_cfg('identities', {})
form = Form(enctype='multipart/form-data', id='login-form', use_tokens=False)
if identities_cfg.get('email-as-username', False):
form.add(StringWidget, 'username', title=_('Email'), size=25, required=True)
else:
form.add(StringWidget, 'username', title=_('Username'), size=25, required=True)
form.add(PasswordWidget, 'password', title=_('Password'), size=25, required=True)
form.add_submit('submit', _('Connect'))
if form.is_submitted() and not form.has_errors():
tmp = wcs.qommon.ident.password.MethodDirectory().login_submit(form)
if not form.has_errors():
return tmp
r += htmltext('<div id="login-password">')
r += get_session().display_message()
r += form.render()
base_url = get_publisher().get_root_url()
r += htmltext('<p><a href="%sident/password/forgotten">%s</a></p>') % (
base_url,
_('Forgotten password ?'),
)
r += htmltext('</div>')
# XXX: this part only supports a single IdP
r += htmltext('<div id="login-sso">')
r += TextsDirectory.get_html_text('aq-sso-text')
form = Form(enctype='multipart/form-data', action='%sident/idp/login' % base_url)
form.add_hidden('method', 'idp')
for kidp, idp in get_cfg('idp', {}).items():
p = lasso.Provider(
lasso.PROVIDER_ROLE_IDP,
misc.get_abs_path(idp['metadata']),
misc.get_abs_path(idp.get('publickey')),
None,
)
form.add_hidden('idp', p.providerId)
break
form.add_submit('submit', _('Connect'))
r += form.render()
r += htmltext('</div>')
get_request().environ['REQUEST_METHOD'] = 'GET'
r += htmltext(
"""<script type="text/javascript">
document.getElementById('login-form')['username'].focus();
</script>"""
)
return r.getvalue()
else:
return OldLoginDirectory._q_index(self)
OldIdentDirectory = wcs.root.IdentDirectory
class AlternateIdentDirectory(OldIdentDirectory):
def _q_traverse(self, path):
return OldIdentDirectory._q_traverse(self, path)
class AlternatePreviewDirectory(PreviewDirectory):
def _q_traverse(self, path):
return super(AlternatePreviewDirectory, self)._q_traverse(path)
class AlternateRootDirectory(OldRootDirectory): class AlternateRootDirectory(OldRootDirectory):
_q_exports = [ _q_exports = [
@ -240,318 +42,11 @@ class AlternateRootDirectory(OldRootDirectory):
'actions', 'actions',
] ]
register = AlternateRegisterDirectory()
login = AlternateLoginDirectory()
ident = AlternateIdentDirectory()
saml = Saml2Directory() saml = Saml2Directory()
code = wcs.forms.root.TrackingCodesDirectory() code = wcs.forms.root.TrackingCodesDirectory()
preview = AlternatePreviewDirectory()
def get_substitution_variables(self):
return {'links': ''}
def _q_traverse(self, path):
self.feed_substitution_parts()
response = get_response()
if not hasattr(response, 'filter'):
response.filter = {}
response.filter['auquotidien'] = True
if not path or (path[0] not in ('api', 'backoffice') and not get_request().is_json()):
# api & backoffice have no use for a side box
response.filter['gauche'] = lambda x: self.box_side(path)
get_publisher().substitutions.feed(self)
response.breadcrumb = [('', _('Home'))]
if not self.admin:
self.admin = get_publisher().admin_directory_class()
if not self.backoffice:
self.backoffice = get_publisher().backoffice_directory_class()
return super()._q_traverse(path)
def json(self):
return FormsRootDirectory().json()
def categories(self):
return FormsRootDirectory().categories()
def _q_index(self):
if get_request().is_json():
return FormsRootDirectory().json()
root_url = get_publisher().get_root_url()
if get_request().user and get_request().user.anonymous and get_request().user.lasso_dump:
return redirect('%smyspace/new' % root_url)
redirect_url = get_cfg('misc', {}).get('homepage-redirect-url')
if redirect_url:
return redirect(
misc.get_variadic_url(redirect_url, get_publisher().substitutions.get_context_variables())
)
template.html_top()
r = TemplateIO(html=True)
get_response().filter['is_index'] = True
r += htmltext('<div id="centre">')
r += self.box_services(position='1st')
r += htmltext('</div>')
r += htmltext('<div id="droite">')
r += self.myspace_snippet()
r += self.box_services(position='2nd')
r += self.consultations()
r += htmltext('</div>')
user = get_request().user
if user and user.can_go_in_backoffice():
get_response().filter['backoffice'] = True
return r.getvalue()
def services(self):
template.html_top()
return self.box_services(level=2)
def box_services(self, level=3, position=None):
## Services
if get_request().user and get_request().user.roles:
accepted_roles = get_request().user.roles
else:
accepted_roles = []
cats = Category.select(order_by='name')
cats = [x for x in cats if x.url_name != 'consultations']
Category.sort_by_position(cats)
all_formdefs = FormDef.select(
lambda x: not x.is_disabled() or x.disabled_redirection, order_by='name'
)
if position:
t = self.display_list_of_formdefs(
[x for x in cats if x.get_homepage_position() == position], all_formdefs, accepted_roles
)
else:
t = self.display_list_of_formdefs(cats, all_formdefs, accepted_roles)
if not t:
return
r = TemplateIO(html=True)
if position == '2nd':
r += htmltext('<div id="services-2nd">')
else:
r += htmltext('<div id="services">')
if level == 2:
r += htmltext('<h2>%s</h2>') % _('Services')
else:
r += htmltext('<h3>%s</h3>') % _('Services')
if 'auquotidien-welcome-in-services' in get_response().filter.get('keywords', []):
homepage_text = TextsDirectory.get_html_text('aq-home-page')
if homepage_text:
r += htmltext('<div id="home-page-intro">')
r += homepage_text
r += htmltext('</div>')
r += htmltext('<ul>')
r += t
r += htmltext('</ul>')
r += htmltext('</div>')
return r.getvalue()
def display_list_of_formdefs(self, cats, all_formdefs, accepted_roles):
r = TemplateIO(html=True)
for category in cats:
if category.url_name == 'consultations':
self.consultations_category = category
continue
formdefs = [x for x in all_formdefs if str(x.category_id) == str(category.id)]
formdefs_advertise = []
for formdef in formdefs[:]:
if formdef.is_disabled(): # is a redirection
continue
if not formdef.roles:
continue
if not get_request().user:
if formdef.always_advertise:
formdefs_advertise.append(formdef)
formdefs.remove(formdef)
continue
if logged_users_role().id in formdef.roles:
continue
for q in accepted_roles:
if q in formdef.roles:
break
else:
if formdef.always_advertise:
formdefs_advertise.append(formdef)
formdefs.remove(formdef)
if not formdefs and not formdefs_advertise:
continue
keywords = {}
for formdef in formdefs:
for keyword in formdef.keywords_list:
keywords[keyword] = True
r += htmltext('<li id="category-%s" data-keywords="%s">') % (
category.url_name,
' '.join(keywords),
)
r += htmltext('<strong>')
r += htmltext('<a href="%s/">') % category.url_name
r += category.name
r += htmltext('</a></strong>\n')
r += category.get_description_html_text()
r += htmltext('<ul>')
limit = category.get_limit()
for formdef in formdefs[:limit]:
r += htmltext('<li data-keywords="%s">') % ' '.join(formdef.keywords_list)
classes = []
if formdef.is_disabled() and formdef.disabled_redirection:
classes.append('redirection')
r += htmltext('<a class="%s" href="%s/%s/">%s</a>') % (
' '.join(classes),
category.url_name,
formdef.url_name,
formdef.name,
)
r += htmltext('</li>\n')
if len(formdefs) < limit:
for formdef in formdefs_advertise[: limit - len(formdefs)]:
r += htmltext('<li class="required-authentication">')
r += htmltext('<a href="%s/%s/">%s</a>') % (
category.url_name,
formdef.url_name,
formdef.name,
)
r += htmltext('<span> (%s)</span>') % _('authentication required')
r += htmltext('</li>\n')
if (len(formdefs) + len(formdefs_advertise)) > limit:
r += htmltext('<li class="all-forms"><a href="%s/" title="%s">%s</a></li>') % (
category.url_name,
_('Access to all forms of the "%s" category') % category.name,
_('Access to all forms in this category'),
)
r += htmltext('</ul>')
r += htmltext('</li>\n')
return r.getvalue()
def consultations(self):
cats = [x for x in Category.select() if x.url_name == 'consultations']
if not cats:
return
consultations_category = cats[0]
formdefs = FormDef.select(
lambda x: (
str(x.category_id) == str(consultations_category.id)
and (not x.is_disabled() or x.disabled_redirection)
),
order_by='name',
)
if not formdefs:
return
## Consultations
r = TemplateIO(html=True)
r += htmltext('<div id="consultations">')
r += htmltext('<h3>%s</h3>') % _('Consultations')
r += consultations_category.get_description_html_text()
r += htmltext('<ul>')
for formdef in formdefs:
r += htmltext('<li>')
r += htmltext('<a href="%s/%s/">%s</a>') % (
consultations_category.url_name,
formdef.url_name,
formdef.name,
)
r += htmltext('</li>')
r += htmltext('</ul>')
r += htmltext('</div>')
return r.getvalue()
def box_side(self, path):
r = TemplateIO(html=True)
root_url = get_publisher().get_root_url()
if (
path == ['']
and 'include-tracking-code-form' in get_response().filter.get('keywords', [])
and self.has_anonymous_access_codes()
):
r += htmltext('<form id="follow-form" action="%scode/load">') % root_url
r += htmltext('<h3>%s</h3>') % _('Tracking code')
r += htmltext('<input size="12" name="code" placeholder="%s"/>') % _('ex: RPQDFVCD')
r += htmltext('<input type="submit" value="%s"/>') % _('Load')
r += htmltext('</form>')
cats = Category.select(order_by='name')
cats = [x for x in cats if x.url_name != 'consultations' and x.get_homepage_position() == 'side']
Category.sort_by_position(cats)
if cats:
r += htmltext('<div id="side-services">')
r += htmltext('<h3>%s</h3>') % _('Services')
r += htmltext('<ul>')
for cat in cats:
r += htmltext('<li><a href="%s/">%s</a></li>') % (cat.url_name, cat.name)
r += htmltext('</ul>')
r += htmltext('</div>')
v = r.getvalue()
if v:
r = TemplateIO(html=True)
r += htmltext('<div id="sidebox">')
r += v
r += htmltext('</div>')
return r.getvalue()
return None
def has_anonymous_access_codes(self):
return any((x for x in FormDef.select() if x.enable_tracking_codes))
def myspace_snippet(self):
r = TemplateIO(html=True)
r += htmltext('<div id="myspace">')
r += htmltext('<h3>%s</h3>') % _('My Space')
r += htmltext('<ul>')
if get_request().user and not get_request().user.anonymous:
r += htmltext(' <li><a href="myspace/" id="member">%s</a></li>') % _(
'Access to your personal space'
)
r += htmltext(' <li><a href="logout" id="logout">%s</a></li>') % _('Logout')
else:
r += htmltext(' <li><a href="register/" id="inscr">%s</a></li>') % _('Registration')
r += htmltext(' <li><a href="login/" id="login">%s</a></li>') % _('Login')
r += htmltext('</ul>')
r += htmltext('</div>')
return r.getvalue()
from qommon.publisher import get_publisher_class from qommon.publisher import get_publisher_class
get_publisher_class().root_directory_class = AlternateRootDirectory
get_publisher_class().after_login_url = 'myspace/' get_publisher_class().after_login_url = 'myspace/'
get_publisher_class().use_sms_feature = True get_publisher_class().use_sms_feature = True
TextsDirectory.register(
'aq-sso-text',
N_('Connecting with Identity Provider'),
default=N_(
'''<h3>Connecting with Identity Provider</h3>
<p>You can also use your identity provider to connect.
</p>'''
),
)
TextsDirectory.register('aq-home-page', N_('Home Page'), wysiwyg=True)

View File

@ -81,4 +81,4 @@ def test_with_superuser():
create_superuser() create_superuser()
app = login(get_app(pub)) app = login(get_app(pub))
# this makes sure the extension loaded properly # this makes sure the extension loaded properly
resp = app.get('/backoffice/settings/texts/aq-home-page', status=200) assert 'auquotidien' in pub.translation_domains