From cad89bf3088178c34e165d30e96009c0d739554d Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Fri, 26 Nov 2010 23:46:45 +0100 Subject: [PATCH 01/22] Load internal modules --- extra/ifef.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/extra/ifef.py b/extra/ifef.py index cb8f33f..c74ce54 100644 --- a/extra/ifef.py +++ b/extra/ifef.py @@ -6,6 +6,8 @@ import modules.configuration import authentic.admin.root import modules.afterjobs import modules.callback +import modules.admin_settings +import modules.qommon_template get_publisher_class().register_translation_domain('ifef') authentic.admin.root.register_page('afterjobs', From 8b9684950f09fb50e4fed7d7a454ef2f473a6383 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Fri, 26 Nov 2010 23:47:10 +0100 Subject: [PATCH 02/22] Overload RootDirectory.get_idp_sso_list --- extra/modules/root.ptl | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/extra/modules/root.ptl b/extra/modules/root.ptl index 0faddb1..c8135ef 100644 --- a/extra/modules/root.ptl +++ b/extra/modules/root.ptl @@ -1,6 +1,7 @@ from qommon import get_cfg, get_logger from quixote import redirect from authentic.form import * +from quixote.html import htmltext import qommon.errors as errors import qommon.template as template import authentic.identities as identities @@ -10,6 +11,8 @@ import authentic.admin.configuration as configuration import urllib2 import captcha from callback import BatchJob, XmlRpcAction +import lasso +import authentic.misc as misc schema = (('PART_EMP', _('Particulier-employeur')), (_('Salarie du Particulier-employeur'), @@ -198,5 +201,29 @@ RecaptchaOptions = { "theme" : "white", "lang" : "fr" }; get_response().add_after_job('''Send registration to Neogia, email = %r,\ classification = %r''' % (email, classification), BatchJob(action)) + def get_idp_sso_list [html] (self): + if not get_cfg('providers', {}).items(): + return '' + + '' + from qommon.publisher import get_publisher_class get_publisher_class().root_directory_class = IfefRootDirectory From a09f6c32196a843a3148aae1aaca102525e77ac3 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Fri, 26 Nov 2010 23:47:29 +0100 Subject: [PATCH 03/22] Add new variables to our them --- theme/template.ezt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/theme/template.ezt b/theme/template.ezt index 63d4a68..49ee159 100644 --- a/theme/template.ezt +++ b/theme/template.ezt @@ -3,13 +3,15 @@ [page_title] - - [script] + [if-any more_css] + [for more_css] + [end][end][if-any script] + [script][end] - +
-

[site_name]

- [if-any breadcrumb][end] +

[site_name]

[if-any breadcrumb] + [end]

[if-any title][title][else][site_name][end]

[prelude] From 5fce7961f14a17f102182620b002fedec0699b87 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Fri, 26 Nov 2010 23:48:02 +0100 Subject: [PATCH 04/22] Add new modules --- extra/modules/admin_settings.py | 45 ++++++++++++++++++++++++++++++++ extra/modules/qommon_template.py | 29 ++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 extra/modules/admin_settings.py create mode 100644 extra/modules/qommon_template.py diff --git a/extra/modules/admin_settings.py b/extra/modules/admin_settings.py new file mode 100644 index 0000000..9e05695 --- /dev/null +++ b/extra/modules/admin_settings.py @@ -0,0 +1,45 @@ +import authentic.admin.settings as settings +from authentic.form import * +import qommon.template +from qommon import get_cfg +from quixote import get_publisher, redirect + +STYLESHEET_URL = 'stylesheet_url' +REFERER_PREFIX_URL = 'referer_prefix_url' +THEME = 'theme' + +class NewLibertyProviderUI(settings.LibertyProviderUI): + def edit_form(self): + form = super(NewLibertyProviderUI, self).edit_form() + form.add(StringWidget, STYLESHEET_URL, + title=_('Customized stylesheet URL'), + value=self.lp.get(STYLESHEET_URL, ''), + hint=_('The URL must be https if authentic also use https')) + form.add(StringWidget, REFERER_PREFIX_URL, + title=_('Prefix URL of the HTTP_REFERER'), + value=self.lp.get(REFERER_PREFIX_URL, ''), + hint=_('Used to find which service initiated a request to Authentic')) + names = [None] + [x[0] for x in qommon.template.get_themes().iteritems()] + form.add(SingleSelectWidget, THEME, + title=_('Custom theme'), + value=self.lp.get(THEME,None), + hint=_('Theme to use when an interaction is initated by this service'), + options=names) + return form + def edit_submit(self): + return super(NewLibertyProviderUI, self).edit_submit() + +class NewLibertyProvidersDir(settings.LibertyProvidersDir): + def submit_new(self, form, key_provider_id=None): + lpk, error = super(NewLibertyProvidersDir, self).submit_new(form, + key_provider_id) + if not error and form.get_widget(STYLESHEET_URL): + v = {} + for k in (STYLESHEET_URL, REFERER_PREFIX_URL, THEME): + v[k] = form.get_widget(k).parse() + get_cfg('providers').get(lpk).update(v) + get_publisher().write_cfg() + return lpk, error + +settings.LibertyProviderUI = NewLibertyProviderUI +settings.LibertyProvidersDir = NewLibertyProvidersDir diff --git a/extra/modules/qommon_template.py b/extra/modules/qommon_template.py new file mode 100644 index 0000000..7d8634a --- /dev/null +++ b/extra/modules/qommon_template.py @@ -0,0 +1,29 @@ +import qommon.template as template +from quixote import get_request +from qommon import get_cfg +from admin_settings import STYLESHEET_URL, REFERER_PREFIX_URL + +__old_decorate = template.decorate + +def decorate(body, response): + request = get_request() + referer = request.environ.get('HTTP_REFERER') + + print 'REFERER', referer + more_css = [] + body_class = [] + if referer: + requesting_service = None + for key, value in get_cfg('providers', {}).iteritems(): + referer_prefix_url = value.get(REFERER_PREFIX_URL, '') + if request.form.get('service') == key or \ + referer_prefix_url and referer.startswith(referer_prefix_url): + requesting_service = key + break + body_class.append(requesting_service) + if requesting_service and value.get(STYLESHEET_URL): + more_css.append(value.get(STYLESHEET_URL)) + response.filter.update({'more_css': more_css, 'body_class': body_class}) + return __old_decorate(body, response) + +template.decorate = decorate From 8a089f67e16c768838000dd3cb9f94ac8422517c Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Sat, 27 Nov 2010 00:34:03 +0100 Subject: [PATCH 05/22] Overload redirection to login page in saml module --- extra/modules/saml.ptl | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 extra/modules/saml.ptl diff --git a/extra/modules/saml.ptl b/extra/modules/saml.ptl new file mode 100644 index 0000000..e3125ae --- /dev/null +++ b/extra/modules/saml.ptl @@ -0,0 +1,25 @@ +from quixote import get_request, get_response, redirect, get_field, get_publisher +from qommon import get_cfg, get_logger +from qommon.misc import get_provider_key + +import authentic.login_token as login_token +import authentic.liberty.saml as saml +from authentic.misc import redirect_with_return_url + +class NewRootDirectory(saml.RootDirectory): + def invoke_login(login, query): + request_id = login.request.iD + provider_key = get_provider_key(login.remoteProviderId) + provider_cfg = get_cfg('providers').get(provider_key, {}) + + login_url = get_request().environ['SCRIPT_NAME'] + '/login' + token = login_token.LoginToken(request_id) + token.query = query + token.store() + args = [('okURL', '/saml/continueSSO?id=%s' % request_id), + ('cancelURL', '/saml/failSSO?id=%s' % request_id), + ('LoginToken', request_id), + ('service',provider_key)] + if provider_cfg.get('theme'): + args.append(('theme', providers_cfg['theme'])) + return redirect_with_return_url(login_url, args) From 37839894573524fcc2359204b66ce02d878367bd Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Tue, 30 Nov 2010 10:13:08 +0100 Subject: [PATCH 06/22] Add target to package just the theme --- Makefile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Makefile b/Makefile index 56f6a52..3ad98f8 100644 --- a/Makefile +++ b/Makefile @@ -18,3 +18,11 @@ package: upload: dput lupin-$(DISTRIBUTION) ../$(PACKAGE)_$(VERSION)_*.changes git push origin + +ifef.zip: theme/* + ln -sf theme ifef + zip -r ifef ifef + rm ifef + +clean: + -rm -f ifef.zip From f1cef9d6db5c8f1b20d2f749de52b722f21ae968 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Tue, 30 Nov 2010 10:13:34 +0100 Subject: [PATCH 07/22] Fix missing argument to invoke_login --- extra/modules/saml.ptl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extra/modules/saml.ptl b/extra/modules/saml.ptl index e3125ae..c1d8fef 100644 --- a/extra/modules/saml.ptl +++ b/extra/modules/saml.ptl @@ -7,7 +7,7 @@ import authentic.liberty.saml as saml from authentic.misc import redirect_with_return_url class NewRootDirectory(saml.RootDirectory): - def invoke_login(login, query): + def invoke_login(self, login, query): request_id = login.request.iD provider_key = get_provider_key(login.remoteProviderId) provider_cfg = get_cfg('providers').get(provider_key, {}) From ec3701f9993535ae2912d53d40c57575140a859d Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Tue, 30 Nov 2010 10:15:39 +0100 Subject: [PATCH 08/22] Update translation --- po/fr.po | 102 +++++++++++++++++++++++++++++++++++++++++----------- po/ifef.pot | 98 ++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 160 insertions(+), 40 deletions(-) diff --git a/po/fr.po b/po/fr.po index f6a8aab..1043eb0 100644 --- a/po/fr.po +++ b/po/fr.po @@ -13,10 +13,34 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=n>1;\n" -#: ../extra/ifef.py:13 ../extra/modules/afterjobs.ptl:32 +#: ../extra/ifef.py:15 ../extra/modules/afterjobs.ptl:32 msgid "Neogia registration jobs" msgstr "Appel d'enregistrement à Neogia" +#: ../extra/modules/admin_settings.py:15 +msgid "Customized stylesheet URL" +msgstr "" + +#: ../extra/modules/admin_settings.py:17 +msgid "The URL must be https if authentic also use https" +msgstr "" + +#: ../extra/modules/admin_settings.py:19 +msgid "Prefix URL of the HTTP_REFERER" +msgstr "" + +#: ../extra/modules/admin_settings.py:21 +msgid "Used to find which service initiated a request to Authentic" +msgstr "" + +#: ../extra/modules/admin_settings.py:24 +msgid "Custom theme" +msgstr "" + +#: ../extra/modules/admin_settings.py:26 +msgid "Theme to use when an interaction is initated by this service" +msgstr "" + #: ../extra/modules/configuration.py:6 msgid "XMLRPC Neogia registration URL" msgstr "" @@ -29,67 +53,67 @@ msgstr "" msgid "Condition d'utilisation" msgstr "Conditions d'utilisation" -#: ../extra/modules/root.ptl:14 +#: ../extra/modules/root.ptl:17 msgid "Particulier-employeur" msgstr "" -#: ../extra/modules/root.ptl:15 +#: ../extra/modules/root.ptl:18 msgid "Salarie du Particulier-employeur" msgstr "Salarié du Particulier-employeur" -#: ../extra/modules/root.ptl:16 ../extra/modules/root.ptl:19 +#: ../extra/modules/root.ptl:19 ../extra/modules/root.ptl:22 msgid "inscrit" msgstr "" -#: ../extra/modules/root.ptl:17 ../extra/modules/root.ptl:20 +#: ../extra/modules/root.ptl:20 ../extra/modules/root.ptl:23 msgid "pas inscrit en formation" msgstr "" -#: ../extra/modules/root.ptl:18 +#: ../extra/modules/root.ptl:21 msgid "Assistante(e) maternel(le)" msgstr "" -#: ../extra/modules/root.ptl:21 +#: ../extra/modules/root.ptl:24 msgid "Formateur ou directeur d'un organisme de formation" msgstr "" -#: ../extra/modules/root.ptl:22 +#: ../extra/modules/root.ptl:25 msgid "labellise par l'Institut-Fepem" msgstr "labellisé par l'Institut-Fepem" -#: ../extra/modules/root.ptl:23 +#: ../extra/modules/root.ptl:26 msgid "souhaitant etre labellise" msgstr "souhaitant être labellisé" -#: ../extra/modules/root.ptl:24 +#: ../extra/modules/root.ptl:27 msgid "pas encore labellise" msgstr "pas encore labéllisé" -#: ../extra/modules/root.ptl:25 +#: ../extra/modules/root.ptl:28 msgid "Formateur consultant souhaitant entrer dans la communaute" msgstr "Formateur consultant souhaitant entrer dans la communauté" -#: ../extra/modules/root.ptl:26 +#: ../extra/modules/root.ptl:29 msgid "Salarie d'un organisme public, d'une association ou d'une organisation" msgstr "" -#: ../extra/modules/root.ptl:27 +#: ../extra/modules/root.ptl:30 msgid "partenaire de l'Institut-Fepem" msgstr "" -#: ../extra/modules/root.ptl:28 +#: ../extra/modules/root.ptl:31 msgid "souhaitant devenir partenaire" msgstr "" -#: ../extra/modules/root.ptl:29 +#: ../extra/modules/root.ptl:32 msgid "pas encore partenaire" msgstr "" -#: ../extra/modules/root.ptl:30 +#: ../extra/modules/root.ptl:33 msgid "Un professionnel de la formation" msgstr "" -#: ../extra/modules/root.ptl:31 +#: ../extra/modules/root.ptl:34 msgid "" "Une personne interessee par le sujet de la professionnalisation de l'emploi " "familial" @@ -97,11 +121,11 @@ msgstr "" "Une personne interessée par le sujet de la professionnalisation de l'emploi " "familial" -#: ../extra/modules/root.ptl:69 +#: ../extra/modules/root.ptl:72 msgid "Username" msgstr "Identifiant" -#: ../extra/modules/root.ptl:71 +#: ../extra/modules/root.ptl:72 msgid "" "C'est cet identifiant qui vous permettra de vous connecter au portail de " "l'IFEF. Il peut contenir seulement des lettres et des chiffres." @@ -136,9 +160,45 @@ msgid "Cancel" msgstr "Annuler" #: ../extra/modules/root.ptl:180 ../extra/modules/root.ptl:181 +======= +#: ../extra/modules/root.ptl:76 +msgid "Password" +msgstr "Mot de passe" + +#: ../extra/modules/root.ptl:78 +msgid "A password will be mailed to you." +msgstr "Un mot de passe vous sera envoyé par courriel." + +#: ../extra/modules/root.ptl:85 ../extra/modules/root.ptl:107 +msgid "Vous etes" +msgstr "Vous êtes" + +#: ../extra/modules/root.ptl:121 +msgid "Vous devez choisir une classification" +msgstr "" + +#: ../extra/modules/root.ptl:148 +msgid "J'accepte les conditions d'utilisation" +msgstr "" + +#: ../extra/modules/root.ptl:157 +msgid "Submit" +msgstr "Valider" + +#: ../extra/modules/root.ptl:158 +msgid "Cancel" +msgstr "Annuler" + +#: ../extra/modules/root.ptl:178 ../extra/modules/root.ptl:179 +>>>>>>> Update translation:po/fr.po msgid "Registration" msgstr "Inscription" +#: ../extra/modules/root.ptl:219 +#, python-format +msgid "Log on %s" +msgstr "" + #: ../extra/modules/afterjobs.ptl:38 #, python-format msgid "Run job %s" @@ -296,11 +356,11 @@ msgstr "" #: ../extra/modules/store.py:71 msgid "votre page web personnelle" -msgstr "" +msgstr "Votre page web personelle" #: ../extra/modules/store.py:72 msgid "Adresse telephonie IP" -msgstr "" +msgstr "Addresse de téléphonie par internet" #: ../extra/modules/store.py:73 msgid "exemple: skype, sip, gtalk" diff --git a/po/ifef.pot b/po/ifef.pot index fde31d0..2e489a4 100644 --- a/po/ifef.pot +++ b/po/ifef.pot @@ -16,10 +16,34 @@ msgstr "" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" -#: ../extra/ifef.py:13 ../extra/modules/afterjobs.ptl:32 +#: ../extra/ifef.py:15 ../extra/modules/afterjobs.ptl:32 msgid "Neogia registration jobs" msgstr "" +#: ../extra/modules/admin_settings.py:15 +msgid "Customized stylesheet URL" +msgstr "" + +#: ../extra/modules/admin_settings.py:17 +msgid "The URL must be https if authentic also use https" +msgstr "" + +#: ../extra/modules/admin_settings.py:19 +msgid "Prefix URL of the HTTP_REFERER" +msgstr "" + +#: ../extra/modules/admin_settings.py:21 +msgid "Used to find which service initiated a request to Authentic" +msgstr "" + +#: ../extra/modules/admin_settings.py:24 +msgid "Custom theme" +msgstr "" + +#: ../extra/modules/admin_settings.py:26 +msgid "Theme to use when an interaction is initated by this service" +msgstr "" + #: ../extra/modules/configuration.py:6 msgid "XMLRPC Neogia registration URL" msgstr "" @@ -32,77 +56,77 @@ msgstr "" msgid "Condition d'utilisation" msgstr "" -#: ../extra/modules/root.ptl:14 +#: ../extra/modules/root.ptl:17 msgid "Particulier-employeur" msgstr "" -#: ../extra/modules/root.ptl:15 +#: ../extra/modules/root.ptl:18 msgid "Salarie du Particulier-employeur" msgstr "" -#: ../extra/modules/root.ptl:16 ../extra/modules/root.ptl:19 +#: ../extra/modules/root.ptl:19 ../extra/modules/root.ptl:22 msgid "inscrit" msgstr "" -#: ../extra/modules/root.ptl:17 ../extra/modules/root.ptl:20 +#: ../extra/modules/root.ptl:20 ../extra/modules/root.ptl:23 msgid "pas inscrit en formation" msgstr "" -#: ../extra/modules/root.ptl:18 +#: ../extra/modules/root.ptl:21 msgid "Assistante(e) maternel(le)" msgstr "" -#: ../extra/modules/root.ptl:21 +#: ../extra/modules/root.ptl:24 msgid "Formateur ou directeur d'un organisme de formation" msgstr "" -#: ../extra/modules/root.ptl:22 +#: ../extra/modules/root.ptl:25 msgid "labellise par l'Institut-Fepem" msgstr "" -#: ../extra/modules/root.ptl:23 +#: ../extra/modules/root.ptl:26 msgid "souhaitant etre labellise" msgstr "" -#: ../extra/modules/root.ptl:24 +#: ../extra/modules/root.ptl:27 msgid "pas encore labellise" msgstr "" -#: ../extra/modules/root.ptl:25 +#: ../extra/modules/root.ptl:28 msgid "Formateur consultant souhaitant entrer dans la communaute" msgstr "" -#: ../extra/modules/root.ptl:26 +#: ../extra/modules/root.ptl:29 msgid "Salarie d'un organisme public, d'une association ou d'une organisation" msgstr "" -#: ../extra/modules/root.ptl:27 +#: ../extra/modules/root.ptl:30 msgid "partenaire de l'Institut-Fepem" msgstr "" -#: ../extra/modules/root.ptl:28 +#: ../extra/modules/root.ptl:31 msgid "souhaitant devenir partenaire" msgstr "" -#: ../extra/modules/root.ptl:29 +#: ../extra/modules/root.ptl:32 msgid "pas encore partenaire" msgstr "" -#: ../extra/modules/root.ptl:30 +#: ../extra/modules/root.ptl:33 msgid "Un professionnel de la formation" msgstr "" -#: ../extra/modules/root.ptl:31 +#: ../extra/modules/root.ptl:34 msgid "" "Une personne interessee par le sujet de la professionnalisation de l'emploi " "familial" msgstr "" -#: ../extra/modules/root.ptl:69 +#: ../extra/modules/root.ptl:72 msgid "Username" msgstr "" -#: ../extra/modules/root.ptl:71 +#: ../extra/modules/root.ptl:72 msgid "" "C'est cet identifiant qui vous permettra de vous connecter au portail de " "l'IFEF. Il peut contenir seulement des lettres et des chiffres." @@ -137,9 +161,45 @@ msgid "Cancel" msgstr "" #: ../extra/modules/root.ptl:180 ../extra/modules/root.ptl:181 +======= +#: ../extra/modules/root.ptl:76 +msgid "Password" +msgstr "" + +#: ../extra/modules/root.ptl:78 +msgid "A password will be mailed to you." +msgstr "" + +#: ../extra/modules/root.ptl:85 ../extra/modules/root.ptl:107 +msgid "Vous etes" +msgstr "" + +#: ../extra/modules/root.ptl:121 +msgid "Vous devez choisir une classification" +msgstr "" + +#: ../extra/modules/root.ptl:148 +msgid "J'accepte les conditions d'utilisation" +msgstr "" + +#: ../extra/modules/root.ptl:157 +msgid "Submit" +msgstr "" + +#: ../extra/modules/root.ptl:158 +msgid "Cancel" +msgstr "" + +#: ../extra/modules/root.ptl:178 ../extra/modules/root.ptl:179 +>>>>>>> Update translation:po/ifef.pot msgid "Registration" msgstr "" +#: ../extra/modules/root.ptl:219 +#, python-format +msgid "Log on %s" +msgstr "" + #: ../extra/modules/afterjobs.ptl:38 #, python-format msgid "Run job %s" From d9824f9c9dcb9a2d99010ae89e2ba88cac29cb2f Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Wed, 1 Dec 2010 14:52:10 +0100 Subject: [PATCH 09/22] Add CUSTOM_DOMAIN to SAMLv2 service settings --- extra/modules/admin_settings.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/extra/modules/admin_settings.py b/extra/modules/admin_settings.py index 9e05695..d7ef9af 100644 --- a/extra/modules/admin_settings.py +++ b/extra/modules/admin_settings.py @@ -6,6 +6,7 @@ from quixote import get_publisher, redirect STYLESHEET_URL = 'stylesheet_url' REFERER_PREFIX_URL = 'referer_prefix_url' +CUSTOM_DOMAIN = 'custom_domain' THEME = 'theme' class NewLibertyProviderUI(settings.LibertyProviderUI): @@ -25,6 +26,11 @@ class NewLibertyProviderUI(settings.LibertyProviderUI): value=self.lp.get(THEME,None), hint=_('Theme to use when an interaction is initated by this service'), options=names) + form.add(StringWidget, CUSTOM_DOMAIN, + title=_('Custom theme'), + value=self.lp.get(CUSTOM_DOMAIN,None), + hint=_('If the IdP is published on this domain, the used theme will the custom theme for this service.'), + options=names) return form def edit_submit(self): return super(NewLibertyProviderUI, self).edit_submit() @@ -35,7 +41,7 @@ class NewLibertyProvidersDir(settings.LibertyProvidersDir): key_provider_id) if not error and form.get_widget(STYLESHEET_URL): v = {} - for k in (STYLESHEET_URL, REFERER_PREFIX_URL, THEME): + for k in (STYLESHEET_URL, REFERER_PREFIX_URL, THEME, CUSTOM_DOMAIN): v[k] = form.get_widget(k).parse() get_cfg('providers').get(lpk).update(v) get_publisher().write_cfg() From 00c67cf7a2576b144e1edf8f43b1338bce904908 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Wed, 1 Dec 2010 16:15:03 +0100 Subject: [PATCH 10/22] Add customization of the theme based upon the domain name --- extra/modules/qommon_template.py | 36 ++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/extra/modules/qommon_template.py b/extra/modules/qommon_template.py index 7d8634a..20e7931 100644 --- a/extra/modules/qommon_template.py +++ b/extra/modules/qommon_template.py @@ -1,28 +1,38 @@ import qommon.template as template -from quixote import get_request +from quixote import get_request, get_publisher from qommon import get_cfg -from admin_settings import STYLESHEET_URL, REFERER_PREFIX_URL +from admin_settings import STYLESHEET_URL, REFERER_PREFIX_URL, THEME, \ + CUSTOM_DOMAIN __old_decorate = template.decorate def decorate(body, response): request = get_request() referer = request.environ.get('HTTP_REFERER') + domain = request.environ.get('SERVER_NAME') - print 'REFERER', referer more_css = [] body_class = [] - if referer: - requesting_service = None - for key, value in get_cfg('providers', {}).iteritems(): - referer_prefix_url = value.get(REFERER_PREFIX_URL, '') - if request.form.get('service') == key or \ - referer_prefix_url and referer.startswith(referer_prefix_url): - requesting_service = key - break + theme = None + stylesheet_url = None + requesting_service = None + + for key, value in get_cfg('providers', {}).iteritems(): + custom_domain = value.get(CUSTOM_DOMAIN) + referer_prefix_url = value.get(REFERER_PREFIX_URL) + if custom_domain == domain or \ + request.form.get('service') == key or \ + (referer_prefix_url and referer and referer.startswith(referer_prefix_url)): + theme = value.get(THEME) + requesting_service = key + stylesheet_url = value.get(STYLESHEET_URL) + break + if requesting_service: body_class.append(requesting_service) - if requesting_service and value.get(STYLESHEET_URL): - more_css.append(value.get(STYLESHEET_URL)) + if stylesheet_url: + more_css.append(stylesheet_url) + if theme: + get_publisher().cfg['branding']['theme'] = theme response.filter.update({'more_css': more_css, 'body_class': body_class}) return __old_decorate(body, response) From 2f95b12bfca893cc239a944e26b10bbad3b4281a Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Fri, 17 Dec 2010 15:23:07 +0100 Subject: [PATCH 11/22] Add theme for centre ressources --- theme/centreressources/authentic.css | 152 +++++++++++++++++++++ theme/centreressources/bg_button.gif | Bin 0 -> 153 bytes theme/centreressources/breadcrumb.jpg | Bin 0 -> 4559 bytes theme/centreressources/desc.xml | 6 + theme/centreressources/logo-fepem.jpg | Bin 0 -> 31082 bytes theme/centreressources/logoportail.png | Bin 0 -> 15164 bytes theme/centreressources/navbar.jpg | Bin 0 -> 542 bytes theme/centreressources/template.ezt | 25 ++++ theme/centreressources/template.iframe.ezt | 16 +++ 9 files changed, 199 insertions(+) create mode 100644 theme/centreressources/authentic.css create mode 100644 theme/centreressources/bg_button.gif create mode 100644 theme/centreressources/breadcrumb.jpg create mode 100644 theme/centreressources/desc.xml create mode 100644 theme/centreressources/logo-fepem.jpg create mode 100644 theme/centreressources/logoportail.png create mode 100644 theme/centreressources/navbar.jpg create mode 100644 theme/centreressources/template.ezt create mode 100644 theme/centreressources/template.iframe.ezt diff --git a/theme/centreressources/authentic.css b/theme/centreressources/authentic.css new file mode 100644 index 0000000..aa09aff --- /dev/null +++ b/theme/centreressources/authentic.css @@ -0,0 +1,152 @@ +#top h1 { +background: transparent url(logo-fepem.jpg) no-repeat; +height:193px; +width:460px; +text-indent:1000em; +overflow: hidden; +} + +div#page { +width: 460px; +margin: 40px auto 0px auto; +font: 80%/1.4 arial, sans-serif; +} + +div#content { +background: #f5f4f4; +padding: 20px 30px; +margin-top: 10px; +-webkit-border-radius: 8px; +-moz-border-radius: 8px; +} + +input { +padding: 3px 5px; +border: 1px solid #cccccc; +color:#666666; +width:200px; +} + +textarea { +font: 120% Arial, Helvetica, sans-serif; +border: solid 1px #cccccc; +padding: 5px; +color:#666666; +} + +textarea:focus, input[type="text"]:focus, input[type="password"]:focus { +border: 1px solid #4690d6; +color:#333333; +} + +div.widget div.title { +font-size: 12px; +color:#000; +} + +span.required { +color: #d61589; +margin-left: 5px; +} + +div.widget { +margin-bottom: 1em; +} + +div.SubmitWidget input, input[type=submit] { +font: 12px/100% Arial, Helvetica, sans-serif; +color: #ffffff; +background:#4690d6 url(bg_button.gif) left top repeat-x; +border: 1px solid #4690d6; +width: auto; +height: 25px; +padding: 2px 8px 2px 8px; +margin:10px; +cursor: pointer; +} + +div.SubmitWidget input:hover, input[type=submit]:hover { +background: #405F90; +border-color: #FFF; +} + +input[type="submit"][name="submit"] { +font-weight: bold; +} + +#breadcrumb { +background:transparent url(breadcrumb.jpg) repeat-x; +list-style:none; +height:55px; +overflow:hidden; +margin:0; +padding:0px; +} + +#breadcrumb p { +margin: 0px 0px 0px 10px; +line-height: 29px; +} + +#breadcrumb p a { +color: black; +text-decoration: none; +} + +#footer { +text-align: center; +color: #666; +font-size: 80%; +} + +#content h1 { +margin-top: 0; +color: #d61589; +font-size: 20px; +} + +div.buttons div.SubmitWidget, +div.buttons div.SubmitWidget div.content { +display: inline; +} + +div.buttons br { +display: none; +} + +.error-notice { +font-size:11px; +font-weight:bold; +} + +div.errornotice { +background: #fd6; +border: 1px solid #ffae15; +margin: 0em 1em 1em 1em; +padding: 5px; +-moz-border-radius: 6px; +-webkit-border-radius:6px; +border-radius: 6px; +-moz-box-shadow: 0 0 4px rgba(0,0,0,0.75); +-webkit-box-shadow: 0 0 4px rgba(0,0,0,0.75); +} + +div.error { +color: orange; +font-weight: bold; +} + +#forgot-password, +#forgot-identifier, +#register { +margin: 3px 0px; +} + +#forgot-password, +#forgot-identifier, +#register { +font-size: 11px; +color: #0379aa +} + +.cr_hidden { display: none; } \ No newline at end of file diff --git a/theme/centreressources/bg_button.gif b/theme/centreressources/bg_button.gif new file mode 100644 index 0000000000000000000000000000000000000000..31cb897e4c887c39a711915b2a9f7b21b6fd56da GIT binary patch literal 153 zcmZ?wbhEHbWMq(LIKseS8#~E9X_iaoa>vvKb_vrhLR(z2S2(9Hv5M$4^{=*xnP3yu zXY5_-l)lJ3xY5+F(k^bQu~&(8M7L}9DifbF%dmFyz0>z&! nU;!Nv39^%cRqR0u$MuUmGsFZ07U&2CiS|uZd2 literal 0 HcmV?d00001 diff --git a/theme/centreressources/breadcrumb.jpg b/theme/centreressources/breadcrumb.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6b053eb6ec127957111bb66c9d5a1d2cd04d8bb5 GIT binary patch literal 4559 zcmeH}cUV)|w!rr}C%q6tLIk9Q-UI|B1dtkfkt)psA&C%5fDk||h=`1WV?jg(6~uz5 z1C9lhu>j(zBZGnk1$Ba6R7S?ZQEaHWCpuSgzBhBBsKWQ9cfMxpNj0X{r-4r-+2&+_61x(6y( zt^C8-?9clD710G*QW*dc8XvG1igE-fm!O=UFUwNu$53V_r70O!o`Wex14LOv$;lsi z+6SAD?4e{~h6GIktc*EJm?2bh7s{*haz!X3!6=vJiADJ+Uqjg_JvT#)@+*{?86tiT z02qo=FB1t;P@ax5MH&^sLwP0u1WNKGPMX9r(Lyv%0C<^M1yXTxii~Y0FlXC3IdR$k zqWpA`OlB3v7o_o}LN+fmBa1I7K&$oP%t|kyrpzrHO)|&UiNmq7vqsbZN&a&2lht2? zvbG;Bo`rv?8N}-Tto!WyS(jN40CybC&HB%}q^$t7t^`11;Iq!C4ggvS04zKR@4EED~5N6Z%R1t-&YDKZi;CSu6MZlsmS!XaPSrUB*@>RgjsUnJZ=I zWbp+ew$&e-_-{K-3Tsjv(;`F*L{gCi-6|UGGO;8XEw@A{mWeYZY_a6eD*QLyCdHt{ zk9~~-WA}T&Ot1#3mzeK06lR{k@97`=xd`yOR<{loah0Ix%* zaE>^ctyJ?OqS%65X`YhNZ$dd>0SV9m6KDcmU>uqPY@N5S*pWH=jM1ed^T;Vp1Id=PGdJK#(3byyBR zhQ|?rkP#N5k60mGgolJ8@yG&1iWDMcNEK3#G$C!sdE_c0M@Es?7#xOyVPmW?E*O8z zY>WVtjVZ*e!Bk`RVOlU{QJtLFJ24p94I9W_CB5xrdB3~d6kzZ5PC>9hRWiBO` zvX;_7X{X$xJfl*nCR7h%2Va57OK{$o>1*q9j7zsw)9YX27L{EKm8*8ks4mjM9oJ{ zs8*u3OYN-MeFnlXWbhaQ#tO!6#yQ3?6UQ`V1~5~Z<;-uGJM2cA`lh_pn5yBSA<`(*Xx6x{@kW!a>7$vZ zxk0m4^Ntoq%R(z$D_?7e);X=wsm!S^Qw3AYrXHR;pbcwVXh&!-*50Mvtv#-ztK+AW zrL$G%tj?$|OV>j;Rku?2wC;!=L(ff5tXHXbM(<~~I@^<-&fdcAVvp%->-+2H>hIL= z(Vs9dHJEKsY|w0Q$B<;mHC$j=Y1nBvW~65nVzk8QfYE?4(U@zTV!YY-yzxsDQG=r|+IV zXsc!$Xj^RCX8W6+g`L2z#_pOu)!x^Bnf-D5r!y>Ph-U1Vanpg}5aRHaL#M+#jw46P zY2u7H8aXC9ZgcGCGPz;gHQWnMSSK&1WlkrZUORJ~WzL74e|52PNpson@@S^%Owr8U zGw-_^xbj_hy2{XV^ z3|t!66+{V&45|*g7im8LjprKhTNWIJWDdGH57yfgl-JI6J{Eg9rj%~E<7x} zCj3E!ZN!p@^O20mgvkAoFQUAn)=JvX{B`elrFOht@5)+Tmo?4>xZ zIC0$Zczk?x{NDKSIX-hX%^9BSFt>E>jd`Z?7S6jkUu%B){8I_E1b)JiL~LSA;{L?9 z{7`;9e>^E5X}!pz<*%dFBYdA4)*rtC3kkaTYj zlrt~qxQvNjrn+;@a#!XK<+r?p&h3 zWciYzrJhT77D9zdg`LX`mz69VF7hkdx16**ZTXMI(~B#LpRb5n(N>~UQdDw(rQgZ} zr7ES;(*CbpzN%e?StVZ8v)X?3*46LJgk?XJPcN@3f4fGo=7+Cszuxk-V(o&pJ?l8@ zcC5#(m#n|B!F|KN3VOw&iu)TwHnvo)Yy->gzRLHO#C11!`t59~H>t1Mh1-?4>%s2m z-RJjk_cZR++FQQ&U4x`SzAs{5SEFNN<9_Y^>khyNau19goO|%fH{RbIZ?bCI+01G# zKLift9{Ty)#BXmL4m#Xp4- z`n!gc`X_5nsh?VRnsmD4wBpR7Gvn=9?V}yZ9rrsEItS0jp1sx;-qmw1=-h?xeZN0@ z-t+vK3$7PVUUa^A;s@>zt(Q2LTDl#&kNxQIMpgyR2sQ0k(k=>({BfcY7exCF5@Z+q<6QiZS z(0|$X#O%q@Up;>99*Z3teky&c_-)NI&1VhIXFTs54;#PxBICux%d%IRulBv>zCQmZ z=FP*2{I~eGTi%(!YkME`{`T8-GetX~ z695HE)W7^h+k+vET{ry2p9+r{da2!Mk*MQy%;7v zTuoiyc7vB^_VS1%Lwg>5^6Nf0D+T}sb%W^$9k_#uUz5~d$HwkIbFXNc=gP`DvMOI> z26t)11%hvJtcd?A|CkVQud{6A>GMa}f7R$e?OAYx(|oTs(OyPLIOkqiB>XN#0b&Qv zwCjpF*_LM>{M0b+VK>BEeYB3T0)EFAnb)ac&}ye3%h7UI13B6RdyBYe7ks$@x#kUG zWOcN8Egd>$NPos5zd$fPm`?92`u7W=Zw>c=lho6Mkr0r@d1bg~2Gl+%Ut4(vk7O1* zM|x=gKQ0aZ;hcY(03h#BW@bKOcdh~m~i0k;rk0RR91 literal 0 HcmV?d00001 diff --git a/theme/centreressources/desc.xml b/theme/centreressources/desc.xml new file mode 100644 index 0000000..b5f7ca5 --- /dev/null +++ b/theme/centreressources/desc.xml @@ -0,0 +1,6 @@ + + + + Theme for Centre Ressources + + diff --git a/theme/centreressources/logo-fepem.jpg b/theme/centreressources/logo-fepem.jpg new file mode 100644 index 0000000000000000000000000000000000000000..10e38ac6d5ed9588be416cfc7a68deefd7c8ded1 GIT binary patch literal 31082 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 MRF$ccGW+)b00+L%mH+?% literal 0 HcmV?d00001 diff --git a/theme/centreressources/navbar.jpg b/theme/centreressources/navbar.jpg new file mode 100644 index 0000000000000000000000000000000000000000..27366fc572c341e6cdeb784020714ec848bf4101 GIT binary patch literal 542 zcmex=R-tZb|xz{bJG4g_o*9GqNST$~&{V890g0&u{{ z%)-LP#>T_J!6U@S!zUyHk`WOE%L_s%0VoC6gTnZKfI*OhL5o3>nNg5|Nsy6Qkn#T! zhI|G_Mg~S!pfh2Bg^3v`z{bu90We_(1|~)ZW)^k<21aHkW)5aXm;@sO6SJUU;6^3K zi9$v}!Xm7R3ynnu(KRzL3JEeX|G&k+1JubR$SlZU&+z#HcSp9XXMAqWgB^JZo&(-?K@W{KI3H!P#(>~P&Uix@-&pG9)NjqPBiV@;CF5I_f z!oE|LA@`1MuL(U!LmwR$5?hOpeIB*$umI xhzmWpT5@Ok##4J{TKPTNc3r3XdhZR+{|xEH@mYmZ&EIn0Z_PUX-ZB3FO#l;Uksts7 literal 0 HcmV?d00001 diff --git a/theme/centreressources/template.ezt b/theme/centreressources/template.ezt new file mode 100644 index 0000000..49ee159 --- /dev/null +++ b/theme/centreressources/template.ezt @@ -0,0 +1,25 @@ + + + + [page_title] + [if-any more_css] + [for more_css] + [end][end][if-any script] + [script][end] + + +
+

[site_name]

[if-any breadcrumb] + [end] +
+

[if-any title][title][else][site_name][end]

+ [prelude] + [body] +
+ +
+ + diff --git a/theme/centreressources/template.iframe.ezt b/theme/centreressources/template.iframe.ezt new file mode 100644 index 0000000..b82d1db --- /dev/null +++ b/theme/centreressources/template.iframe.ezt @@ -0,0 +1,16 @@ + + + + + [script] + + +
+
+
+ [prelude] + [body] +
+ + From 10736e4e4b69e696f6a7cc745e3e2f0829aa9a11 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Fri, 17 Dec 2010 16:46:41 +0100 Subject: [PATCH 12/22] Add rule to ifef theme about the GCU box --- theme/ifef/authentic.css | 148 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) create mode 100644 theme/ifef/authentic.css diff --git a/theme/ifef/authentic.css b/theme/ifef/authentic.css new file mode 100644 index 0000000..7c36ed0 --- /dev/null +++ b/theme/ifef/authentic.css @@ -0,0 +1,148 @@ +#top h1 { + background: transparent url(logoportail.png) no-repeat; + height:86px; + width:439px; + text-indent:1000em; + overflow: hidden; +} + +div#page { + width: 600px; + margin: 40px auto 0px auto; + font: 80%/1.4 "Lucida Grande", Verdana, sans-serif; +} + +div#content { + background: #f6f6f6; + padding: 10px; + margin-top: 10px; + -webkit-border-radius: 8px; + -moz-border-radius: 8px; +} + +input { + font: 120% Arial, Helvetica, sans-serif; + padding: 5px; + border: 1px solid #cccccc; + color:#666666; +} + +textarea { + font: 120% Arial, Helvetica, sans-serif; + border: solid 1px #cccccc; + padding: 5px; + color:#666666; +} + +textarea:focus, input[type="text"]:focus, input[type="password"]:focus { + border: 1px solid #4690d6; + color:#333333; +} + +div.widget div.title { + font-size: 1.2em; + color:gray; +} + +span.required { + background: transparent url(/qo/css/required.png) 0px 0.5ex no-repeat; + padding: 0 0 0 24px; + margin-left: 1ex; + overflow: hidden; + color: white; +} + +div.widget { + margin-bottom: 1em; +} + +div.SubmitWidget input, input[type=submit] { + font: 12px/100% Arial, Helvetica, sans-serif; + color: #ffffff; + background:#4690d6 url(bg_button.gif) left top repeat-x; + border: 1px solid #4690d6; + width: auto; + height: 25px; + padding: 2px 8px 2px 8px; + margin:10px; + cursor: pointer; +} + +div.SubmitWidget input:hover, input[type=submit]:hover { + background: #405F90; + border-color: #FFF; +} + +input[type="submit"][name="submit"] { + font-weight: bold; +} + +#breadcrumb { + background:transparent url(navbar.jpg) repeat-x; + list-style:none; + height:29px; + overflow:hidden; + -moz-border-radius: 6px; + -webkit-border-radius:6px; + border-radius: 6px; + margin:0; + -moz-box-shadow: 0 0 4px rgba(0,0,0,0.75); + -webkit-box-shadow: 0 0 4px rgba(0,0,0,0.75); + border:1px solid #D2D2CD; + padding:0px; +} + +#breadcrumb p { + margin: 0 2em; + line-height: 29px; +} + +#breadcrumb p a { + color: white; + text-decoration: none; +} + +#footer { + text-align: center; + color: #666; + font-size: 80%; +} + +#content h1 { + margin-top: 0; + color: #305080; +} + +div.buttons div.SubmitWidget, +div.buttons div.SubmitWidget div.content { + display: inline; +} + +div.buttons br { + display: none; +} + +div.errornotice { + background: #fd6; + border: 1px solid #ffae15; + margin: 0em 1em 1em 1em; + padding: 5px; + -moz-border-radius: 6px; + -webkit-border-radius:6px; + border-radius: 6px; + -moz-box-shadow: 0 0 4px rgba(0,0,0,0.75); + -webkit-box-shadow: 0 0 4px rgba(0,0,0,0.75); +} + +div.error { + color: orange; + font-weight: bold; +} + +div#conditions { + background: white; + border: 1px solid #DDD; + height: 30em; + overflow-y: scroll; + padding: 1em; +} From 3f443e3f85c7f66f63ac37cdb971f5e226b39281 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Fri, 17 Dec 2010 16:53:22 +0100 Subject: [PATCH 13/22] Merge modification from live modification on identite.emploisdelafamille.fr --- theme/ifef/template.ezt | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 theme/ifef/template.ezt diff --git a/theme/ifef/template.ezt b/theme/ifef/template.ezt new file mode 100644 index 0000000..8b16774 --- /dev/null +++ b/theme/ifef/template.ezt @@ -0,0 +1,24 @@ + + + + [page_title] + [if-any more_css] + [for more_css] + [end][end][if-any script] + [script][end] + + +
+ [if-any breadcrumb][end] +
+

[if-any title][title][else][site_name][end]

+ [prelude] + [body] +
+ +
+ + From 277afcd654724fcf27bd4da66e53c9cd2d0a54af Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Fri, 17 Dec 2010 16:54:01 +0100 Subject: [PATCH 14/22] Add target to package themes --- Makefile | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 3ad98f8..4848e1e 100644 --- a/Makefile +++ b/Makefile @@ -19,10 +19,11 @@ upload: dput lupin-$(DISTRIBUTION) ../$(PACKAGE)_$(VERSION)_*.changes git push origin -ifef.zip: theme/* - ln -sf theme ifef - zip -r ifef ifef - rm ifef +ifef.zip: theme/ifef/* + cd theme; zip -r ../ifef ifef + +centreressources.zip: theme/centreressources/* + cd theme; zip -r ../centreressources centreressources clean: -rm -f ifef.zip From f047d7d7f65c09aa98c71cd135229a60d02753f1 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Fri, 17 Dec 2010 16:55:02 +0100 Subject: [PATCH 15/22] Add authentic_saml module to overload invoke_login --- extra/ifef.py | 1 + extra/modules/authentic_saml.py | 11 +++++++++++ 2 files changed, 12 insertions(+) create mode 100644 extra/modules/authentic_saml.py diff --git a/extra/ifef.py b/extra/ifef.py index c74ce54..6a594e3 100644 --- a/extra/ifef.py +++ b/extra/ifef.py @@ -8,6 +8,7 @@ import modules.afterjobs import modules.callback import modules.admin_settings import modules.qommon_template +import modules.authentic_saml get_publisher_class().register_translation_domain('ifef') authentic.admin.root.register_page('afterjobs', diff --git a/extra/modules/authentic_saml.py b/extra/modules/authentic_saml.py new file mode 100644 index 0000000..1ec7128 --- /dev/null +++ b/extra/modules/authentic_saml.py @@ -0,0 +1,11 @@ +from quixote import get_session +import authentic.liberty.saml2 +import qommon.misc as misc + +# Yeah another monkey patch +old_invoke_login = authentic.liberty.saml2.RootDirectory.invoke_login +def invoke_login(self, login, query): + print 'my invoke login' + get_session().service = misc.get_provider_key(login.remoteProviderId) + return old_invoke_login(self, login, query) +authentic.liberty.saml2.RootDirectory.invoke_login = invoke_login From 2036f657e673610b1820b14bb756dc66ad7c8ade Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Fri, 17 Dec 2010 16:55:46 +0100 Subject: [PATCH 16/22] remove saml.ptl --- extra/modules/saml.ptl | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 extra/modules/saml.ptl diff --git a/extra/modules/saml.ptl b/extra/modules/saml.ptl deleted file mode 100644 index c1d8fef..0000000 --- a/extra/modules/saml.ptl +++ /dev/null @@ -1,25 +0,0 @@ -from quixote import get_request, get_response, redirect, get_field, get_publisher -from qommon import get_cfg, get_logger -from qommon.misc import get_provider_key - -import authentic.login_token as login_token -import authentic.liberty.saml as saml -from authentic.misc import redirect_with_return_url - -class NewRootDirectory(saml.RootDirectory): - def invoke_login(self, login, query): - request_id = login.request.iD - provider_key = get_provider_key(login.remoteProviderId) - provider_cfg = get_cfg('providers').get(provider_key, {}) - - login_url = get_request().environ['SCRIPT_NAME'] + '/login' - token = login_token.LoginToken(request_id) - token.query = query - token.store() - args = [('okURL', '/saml/continueSSO?id=%s' % request_id), - ('cancelURL', '/saml/failSSO?id=%s' % request_id), - ('LoginToken', request_id), - ('service',provider_key)] - if provider_cfg.get('theme'): - args.append(('theme', providers_cfg['theme'])) - return redirect_with_return_url(login_url, args) From e321636527cee312cb914c99e3c672e6c21eb376 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Fri, 17 Dec 2010 16:56:21 +0100 Subject: [PATCH 17/22] Move ifef themes to its own subdirectory --- theme/authentic.css | 141 --------------------------- theme/{ => ifef}/bg_button.gif | Bin theme/{ => ifef}/desc.xml | 0 theme/{ => ifef}/logoportail.png | Bin theme/{ => ifef}/navbar.jpg | Bin theme/{ => ifef}/template.iframe.ezt | 0 theme/template.ezt | 25 ----- 7 files changed, 166 deletions(-) delete mode 100644 theme/authentic.css rename theme/{ => ifef}/bg_button.gif (100%) rename theme/{ => ifef}/desc.xml (100%) rename theme/{ => ifef}/logoportail.png (100%) rename theme/{ => ifef}/navbar.jpg (100%) rename theme/{ => ifef}/template.iframe.ezt (100%) delete mode 100644 theme/template.ezt diff --git a/theme/authentic.css b/theme/authentic.css deleted file mode 100644 index 24ddabf..0000000 --- a/theme/authentic.css +++ /dev/null @@ -1,141 +0,0 @@ -#top h1 { - background: transparent url(logoportail.png) no-repeat; - height:86px; - width:439px; - text-indent:1000em; - overflow: hidden; -} - -div#page { - width: 600px; - margin: 40px auto 0px auto; - font: 80%/1.4 "Lucida Grande", Verdana, sans-serif; -} - -div#content { - background: #f6f6f6; - padding: 10px; - margin-top: 10px; - -webkit-border-radius: 8px; - -moz-border-radius: 8px; -} - -input { - font: 120% Arial, Helvetica, sans-serif; - padding: 5px; - border: 1px solid #cccccc; - color:#666666; -} - -textarea { - font: 120% Arial, Helvetica, sans-serif; - border: solid 1px #cccccc; - padding: 5px; - color:#666666; -} - -textarea:focus, input[type="text"]:focus, input[type="password"]:focus { - border: 1px solid #4690d6; - color:#333333; -} - -div.widget div.title { - font-size: 1.2em; - color:gray; -} - -span.required { - background: transparent url(/qo/css/required.png) 0px 0.5ex no-repeat; - padding: 0 0 0 24px; - margin-left: 1ex; - overflow: hidden; - color: white; -} - -div.widget { - margin-bottom: 1em; -} - -div.SubmitWidget input, input[type=submit] { - font: 12px/100% Arial, Helvetica, sans-serif; - color: #ffffff; - background:#4690d6 url(bg_button.gif) left top repeat-x; - border: 1px solid #4690d6; - width: auto; - height: 25px; - padding: 2px 8px 2px 8px; - margin:10px; - cursor: pointer; -} - -div.SubmitWidget input:hover, input[type=submit]:hover { - background: #405F90; - border-color: #FFF; -} - -input[type="submit"][name="submit"] { - font-weight: bold; -} - -#breadcrumb { - background:transparent url(navbar.jpg) repeat-x; - list-style:none; - height:29px; - overflow:hidden; - -moz-border-radius: 6px; - -webkit-border-radius:6px; - border-radius: 6px; - margin:0; - -moz-box-shadow: 0 0 4px rgba(0,0,0,0.75); - -webkit-box-shadow: 0 0 4px rgba(0,0,0,0.75); - border:1px solid #D2D2CD; - padding:0px; -} - -#breadcrumb p { - margin: 0 2em; - line-height: 29px; -} - -#breadcrumb p a { - color: white; - text-decoration: none; -} - -#footer { - text-align: center; - color: #666; - font-size: 80%; -} - -#content h1 { - margin-top: 0; - color: #305080; -} - -div.buttons div.SubmitWidget, -div.buttons div.SubmitWidget div.content { - display: inline; -} - -div.buttons br { - display: none; -} - -div.errornotice { - background: #fd6; - border: 1px solid #ffae15; - margin: 0em 1em 1em 1em; - padding: 5px; - -moz-border-radius: 6px; - -webkit-border-radius:6px; - border-radius: 6px; - -moz-box-shadow: 0 0 4px rgba(0,0,0,0.75); - -webkit-box-shadow: 0 0 4px rgba(0,0,0,0.75); -} - -div.error { - color: orange; - font-weight: bold; -} - diff --git a/theme/bg_button.gif b/theme/ifef/bg_button.gif similarity index 100% rename from theme/bg_button.gif rename to theme/ifef/bg_button.gif diff --git a/theme/desc.xml b/theme/ifef/desc.xml similarity index 100% rename from theme/desc.xml rename to theme/ifef/desc.xml diff --git a/theme/logoportail.png b/theme/ifef/logoportail.png similarity index 100% rename from theme/logoportail.png rename to theme/ifef/logoportail.png diff --git a/theme/navbar.jpg b/theme/ifef/navbar.jpg similarity index 100% rename from theme/navbar.jpg rename to theme/ifef/navbar.jpg diff --git a/theme/template.iframe.ezt b/theme/ifef/template.iframe.ezt similarity index 100% rename from theme/template.iframe.ezt rename to theme/ifef/template.iframe.ezt diff --git a/theme/template.ezt b/theme/template.ezt deleted file mode 100644 index 49ee159..0000000 --- a/theme/template.ezt +++ /dev/null @@ -1,25 +0,0 @@ - - - - [page_title] - [if-any more_css] - [for more_css] - [end][end][if-any script] - [script][end] - - -
-

[site_name]

[if-any breadcrumb] - [end] -
-

[if-any title][title][else][site_name][end]

- [prelude] - [body] -
- -
- - From 99d15cc886d28681641d9dcc6c20b7ebd7819045 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Fri, 17 Dec 2010 16:56:40 +0100 Subject: [PATCH 18/22] Add by service cutomization of the registration fields --- extra/modules/admin_settings.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/extra/modules/admin_settings.py b/extra/modules/admin_settings.py index d7ef9af..ca6ef9c 100644 --- a/extra/modules/admin_settings.py +++ b/extra/modules/admin_settings.py @@ -3,10 +3,12 @@ from authentic.form import * import qommon.template from qommon import get_cfg from quixote import get_publisher, redirect +import authentic.identities as identities STYLESHEET_URL = 'stylesheet_url' REFERER_PREFIX_URL = 'referer_prefix_url' CUSTOM_DOMAIN = 'custom_domain' +REGISTRATION_FIELD = 'registration_fields' THEME = 'theme' class NewLibertyProviderUI(settings.LibertyProviderUI): @@ -27,10 +29,15 @@ class NewLibertyProviderUI(settings.LibertyProviderUI): hint=_('Theme to use when an interaction is initated by this service'), options=names) form.add(StringWidget, CUSTOM_DOMAIN, - title=_('Custom theme'), + title=_('Custom domain'), value=self.lp.get(CUSTOM_DOMAIN,None), hint=_('If the IdP is published on this domain, the used theme will the custom theme for this service.'), options=names) + form.add(identities.WidgetList, REGISTRATION_FIELD, + title=_('Registration fields'), + element_type=StringWidget, + value=self.lp.get(REGISTRATION_FIELD,[]), + hint=_('List of specific fields to show on the registration page.')) return form def edit_submit(self): return super(NewLibertyProviderUI, self).edit_submit() @@ -41,7 +48,7 @@ class NewLibertyProvidersDir(settings.LibertyProvidersDir): key_provider_id) if not error and form.get_widget(STYLESHEET_URL): v = {} - for k in (STYLESHEET_URL, REFERER_PREFIX_URL, THEME, CUSTOM_DOMAIN): + for k in (STYLESHEET_URL, REFERER_PREFIX_URL, THEME, CUSTOM_DOMAIN, REGISTRATION_FIELD): v[k] = form.get_widget(k).parse() get_cfg('providers').get(lpk).update(v) get_publisher().write_cfg() From 2ee261945551f87fe2bdf9f03ee87c0709c711c9 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Fri, 17 Dec 2010 16:57:26 +0100 Subject: [PATCH 19/22] Consult the session for the current service, also persist into the session the last selected service --- extra/modules/qommon_template.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/extra/modules/qommon_template.py b/extra/modules/qommon_template.py index 20e7931..d599ce4 100644 --- a/extra/modules/qommon_template.py +++ b/extra/modules/qommon_template.py @@ -1,5 +1,5 @@ import qommon.template as template -from quixote import get_request, get_publisher +from quixote import get_request, get_publisher, get_session from qommon import get_cfg from admin_settings import STYLESHEET_URL, REFERER_PREFIX_URL, THEME, \ CUSTOM_DOMAIN @@ -10,6 +10,7 @@ def decorate(body, response): request = get_request() referer = request.environ.get('HTTP_REFERER') domain = request.environ.get('SERVER_NAME') + session = get_session() more_css = [] body_class = [] @@ -21,13 +22,15 @@ def decorate(body, response): custom_domain = value.get(CUSTOM_DOMAIN) referer_prefix_url = value.get(REFERER_PREFIX_URL) if custom_domain == domain or \ - request.form.get('service') == key or \ - (referer_prefix_url and referer and referer.startswith(referer_prefix_url)): + (request.form and request.form.get('service') == key) or \ + (referer_prefix_url and referer and referer.startswith(referer_prefix_url)) or \ + getattr(session, 'service', None) == key: theme = value.get(THEME) requesting_service = key stylesheet_url = value.get(STYLESHEET_URL) break if requesting_service: + session.service = requesting_service body_class.append(requesting_service) if stylesheet_url: more_css.append(stylesheet_url) @@ -37,3 +40,5 @@ def decorate(body, response): return __old_decorate(body, response) template.decorate = decorate +import authentic.sessions +authentic.sessions.BasicSession._has_info_keys.append('service') From a9855ba738feb92cb8f3b52bae1c235f0eca6ae2 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Fri, 17 Dec 2010 16:59:06 +0100 Subject: [PATCH 20/22] Persist into the session the request the session the currently selected theme --- extra/modules/root.ptl | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/extra/modules/root.ptl b/extra/modules/root.ptl index c8135ef..029ac7a 100644 --- a/extra/modules/root.ptl +++ b/extra/modules/root.ptl @@ -1,5 +1,5 @@ from qommon import get_cfg, get_logger -from quixote import redirect +from quixote import redirect, get_session from authentic.form import * from quixote.html import htmltext import qommon.errors as errors @@ -53,6 +53,14 @@ def check_classification(classification): class IfefRootDirectory(authentic.root.RootDirectory): _q_exports = authentic.root.RootDirectory._q_exports + [ 'register2' ] + def _q_traverse(self, path): + request = get_request() + if request.form and request.form.get('service'): + service = request.form['service'] + get_session().service = service + request.service = service + return super(IfefRootDirectory, self)._q_traverse(path) + def register2 (self): return self.register(version=2) From 52d8f1b1c447763815ce48053b7a9069143c8ddf Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Fri, 17 Dec 2010 16:59:42 +0100 Subject: [PATCH 21/22] Use by service registration field list to setup the registration page --- extra/modules/root.ptl | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/extra/modules/root.ptl b/extra/modules/root.ptl index 029ac7a..10bf514 100644 --- a/extra/modules/root.ptl +++ b/extra/modules/root.ptl @@ -66,12 +66,21 @@ class IfefRootDirectory(authentic.root.RootDirectory): def register (self, version=1): identities_cfg = get_cfg('identities', {}) + session = get_session() if not identities_cfg.get('creation') in ('self', 'moderated'): raise errors.TraversalError() form = Form(enctype="multipart/form-data") - for field in identities.get_store_class().fields: + fields = identities.get_store_class().fields + keys = [] + if getattr(session, 'service', None): + keys = get_cfg('providers',{}).get(session.service, {}) \ + .get('registration_fields') + if keys: + fields = [ field for field in fields \ + if field.key in keys ] + for field in fields: if getattr(field, str('on_register'), True): field.add_to_form(form) and None From 2bf7d961f0118b7a58e7d8401482c9607f905b90 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Fri, 17 Dec 2010 17:00:46 +0100 Subject: [PATCH 22/22] Add the classfication field to the by service fields --- extra/modules/root.ptl | 73 +++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/extra/modules/root.ptl b/extra/modules/root.ptl index 10bf514..c9d4315 100644 --- a/extra/modules/root.ptl +++ b/extra/modules/root.ptl @@ -99,51 +99,58 @@ vous connecter au portail de l'IFEF. Il peut contenir seulement des lettres et d else: form.add(HtmlWidget, htmltext('

%s

') % _('A password will be mailed to you.')) - classification = get_request().form.get('classification') - if classification: - if not check_classification(classification): - classification = None - if version == 1: - s='' % _('Vous etes') + for q in schema: + if isinstance(q[1], tuple): + s += '' % q[0] + for r in q[1:]: + code, text = r + if code == classification: + selected = 'selected="1"' + else: + selected = '' + s += '\n' % (selected, code, text) + s += '\n' + + else: + code, text = q if code == classification: selected = 'selected="1"' else: selected = '' s += '\n' % (selected, code, text) - s += '\n' + s += '' + else: + s='' % _('Vous etes') + for i, q in enumerate(schema): + if isinstance(q, tuple): + s += '

%s' % q[0] + for j, q in enumerate(q[1:]): + s += '%s' % (i, j, i, j, q) + s += '

\n' - else: - code, text = q - if code == classification: - selected = 'selected="1"' else: - selected = '' - s += '\n' % (selected, code, text) - s += '' - else: - s='' % _('Vous etes') - for i, q in enumerate(schema): - if isinstance(q, tuple): - s += '

%s' % q[0] - for j, q in enumerate(q[1:]): - s += '%s' % (i, j, i, j, q) - s += '

\n' + s += '

%s

' % (i, i, q) + s+='
' - else: - s += '

%s

' % (i, i, q) - s+='
' - - if get_request().get_method() == 'POST' and not classification: + if add_classification and get_request().get_method() == 'POST' \ + and not classification: form.set_error('username', ' ') form.add(HtmlWidget, 'erreur', htmltext('
%s
') % _('Vous devez choisir une classification')) elif get_request().get_method() == 'GET': get_request().form = {} - form.add(HtmlWidget, 'classification', htmltext(s)) + if add_classification: + form.add(HtmlWidget, 'classification', htmltext(s)) # domain name: fepem re_captcha_public_key = '6LcIULoSAAAAAIbUohbBeHCcUeWAt74sjvjK6w5W' re_captcha_private_key = '6LcIULoSAAAAADfaFk2E9x9G2FgpSsnNIfUV1rlS'