# HG changeset patch # User Thierry Florac # Date 1444289313 -7200 # Node ID a6d994c608222962aad81d618b022b9f824c9fbe # Parent e4fc19ce77fad6b1df6b854b3d4c352422c12822 Added public profile diff -r e4fc19ce77fa -r a6d994c60822 src/pyams_security/interfaces/profile.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_security/interfaces/profile.py Thu Oct 08 09:28:33 2015 +0200 @@ -0,0 +1,36 @@ +# +# Copyright (c) 2008-2015 Thierry Florac +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# + +__docformat__ = 'restructuredtext' + + +# import standard library + +# import interfaces + +# import packages +from pyams_file.schema import ThumbnailImageField +from zope.interface import Interface + +from pyams_security import _ + + +PUBLIC_PROFILE_KEY = 'pyams_security.public_profile' + + +class IPublicProfile(Interface): + """User public profile preferences""" + + avatar = ThumbnailImageField(title=_("Profile's avatar"), + description=_("This picture will be associated to your user profile"), + required=False) + diff -r e4fc19ce77fa -r a6d994c60822 src/pyams_security/profile.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_security/profile.py Thu Oct 08 09:28:33 2015 +0200 @@ -0,0 +1,82 @@ +# +# Copyright (c) 2008-2015 Thierry Florac +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# + +__docformat__ = 'restructuredtext' + + +# import standard library + +# import interfaces +from pyams_security.interfaces import IPrincipalInfo +from pyams_security.interfaces.profile import PUBLIC_PROFILE_KEY, IPublicProfile +from pyams_utils.interfaces.site import ISiteRoot +from pyams_utils.interfaces.tales import ITALESExtension +from zope.annotation.interfaces import IAnnotations, IAttributeAnnotatable +from zope.traversing.interfaces import ITraversable + +# import packages +from persistent import Persistent +from pyams_file.property import FileProperty +from pyams_utils.adapter import adapter_config, ContextRequestAdapter +from pyams_utils.request import check_request +from pyams_utils.traversing import get_parent +from pyramid.threadlocal import get_current_registry, get_current_request +from zope.container.contained import Contained +from zope.lifecycleevent import ObjectCreatedEvent +from zope.interface import implementer, Interface +from zope.location import locate + + +@implementer(IPublicProfile, IAttributeAnnotatable) +class PublicProfile(Persistent, Contained): + """Public profile persistent class""" + + avatar = FileProperty(IPublicProfile['avatar']) + + +@adapter_config(context=Interface, provides=IPublicProfile) +def PublicProfileFactory(context): + request = check_request() + return IPublicProfile(request.principal) + + +@adapter_config(context=IPrincipalInfo, provides=IPublicProfile) +def PrincipalPublicProfileFactory(principal): + """Principal public profile factory adapter""" + annotations = IAnnotations(principal) + profile = annotations.get(PUBLIC_PROFILE_KEY) + if profile is None: + profile = annotations[PUBLIC_PROFILE_KEY] = PublicProfile() + get_current_registry().notify(ObjectCreatedEvent(profile)) + request = get_current_request() + if request is not None: + root = get_parent(request.context, ISiteRoot) + locate(profile, root, '++profile++') + return profile + + +@adapter_config(name='profile', context=(Interface, Interface), provides=ITraversable) +class ProfileTraverser(ContextRequestAdapter): + """++profile++ namespace traverser""" + + def traverse(self, name, furtherpath=None): + return IPublicProfile(self.request) + + +@adapter_config(name='public_profile', context=(Interface, Interface), provides=ITALESExtension) +class PublicProfileExtension(ContextRequestAdapter): + """public_profile TALES extension""" + + def render(self, request=None): + if request is None: + request = self.request + return IPublicProfile(request) diff -r e4fc19ce77fa -r a6d994c60822 src/pyams_security/zmi/profile.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/pyams_security/zmi/profile.py Thu Oct 08 09:28:33 2015 +0200 @@ -0,0 +1,90 @@ +# +# Copyright (c) 2008-2015 Thierry Florac +# All Rights Reserved. +# +# This software is subject to the provisions of the Zope Public License, +# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED +# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS +# FOR A PARTICULAR PURPOSE. +# + +__docformat__ = 'restructuredtext' + + +# import standard library + +# import interfaces +from pyams_form.interfaces.form import IInnerTabForm +from pyams_security.interfaces.profile import IPublicProfile +from pyams_skin.interfaces.viewlet import IShortcutsViewletManager +from pyams_skin.layer import IPyAMSLayer + +# import packages +from pyams_form.form import InnerEditForm, AJAXEditForm +from pyams_pagelet.pagelet import pagelet_config +from pyams_viewlet.viewlet import viewlet_config +from pyams_skin.viewlet.shortcuts import Shortcut +from pyams_utils.adapter import adapter_config +from pyams_zmi.form import AdminDialogEditForm +from pyramid.view import view_config +from z3c.form import field +from zope.interface import Interface + +from pyams_security import _ + + +@viewlet_config(name='profile', layer=IPyAMSLayer, manager=IShortcutsViewletManager, weight=10) +class UserProfileShortcut(Shortcut): + """User profile shortcut""" + + label = _("User profile") + bg_color_class = 'bg-color-greenLight' + icon_class = 'fa-user' + url = 'user-profile.html' + modal_target = True + checked = 'selected' + + +@pagelet_config(name='user-profile.html', layer=IPyAMSLayer) +class UserProfileEditForm(AdminDialogEditForm): + """User profile edit form""" + + legend = _("Edit user profile") + fields = field.Fields(Interface) + + ajax_handler = 'user-profile.json' + edit_permission = None + + dialog_class = 'modal-large' + + @property + def title(self): + return self.request.principal.title + + +@view_config(name='user-profile.json', request_type=IPyAMSLayer, renderer='json', xhr=True) +class UserProfileAJAXEditForm(AJAXEditForm, UserProfileEditForm): + """User profile edit form, JSON renderer""" + + +@adapter_config(name='public_profile', + context=(Interface, IPyAMSLayer, UserProfileEditForm), + provides=IInnerTabForm) +class PublicProfileTabForm(InnerEditForm): + """Public profile tab form""" + + tab_label = _("Public profile") + legend = None + + fields = field.Fields(IPublicProfile) + edit_permission = None + + label_css_class = 'control-label col-md-4' + input_css_class = 'col-md-8' + + weight = 10 + + def getContent(self): + return IPublicProfile(self.request.principal)