|
1 # |
|
2 # Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net> |
|
3 # All Rights Reserved. |
|
4 # |
|
5 # This software is subject to the provisions of the Zope Public License, |
|
6 # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. |
|
7 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED |
|
8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS |
|
10 # FOR A PARTICULAR PURPOSE. |
|
11 # |
|
12 |
|
13 __docformat__ = 'restructuredtext' |
|
14 |
|
15 |
|
16 # import standard library |
|
17 from datetime import datetime |
|
18 |
|
19 # import interfaces |
|
20 from pyams_security.interfaces import IUsersFolderPlugin, ISecurityManager, ILocalUser, IUserRegistrationInfo |
|
21 from pyams_security.zmi.interfaces import ISecurityManagerToolbarAddingMenu, ISecurityManagerMenu |
|
22 from pyams_skin.interfaces.viewlet import IMenuItem, IToolbarViewletManager |
|
23 from pyams_skin.interfaces import IPageHeader |
|
24 from pyams_skin.layer import IPyAMSLayer |
|
25 from pyams_utils.interfaces.data import IObjectData |
|
26 from pyams_zmi.layer import IAdminLayer |
|
27 from z3c.form.interfaces import DISPLAY_MODE, IDataExtractedEvent |
|
28 from z3c.table.interfaces import IColumn |
|
29 from zope.component.interfaces import ISite |
|
30 from zope.dublincore.interfaces import IZopeDublinCore |
|
31 |
|
32 # import packages |
|
33 from pyams_form.form import AJAXAddForm, AJAXEditForm |
|
34 from pyams_form.search import SearchView, SearchResultsView |
|
35 from pyams_pagelet.pagelet import pagelet_config |
|
36 from pyams_security.plugin.userfolder import UsersFolder, User |
|
37 from pyams_security.zmi.utility import SecurityManagerPluginsTable |
|
38 from pyams_skin.skin import apply_skin |
|
39 from pyams_skin.viewlet.menu import MenuItem |
|
40 from pyams_skin.viewlet.toolbar import ToolbarMenuItem, ToolbarAction |
|
41 from pyams_utils.adapter import adapter_config, ContextRequestViewAdapter |
|
42 from pyams_utils.date import format_datetime |
|
43 from pyams_utils.registry import query_utility |
|
44 from pyams_utils.url import absolute_url |
|
45 from pyams_viewlet.viewlet import viewlet_config |
|
46 from pyams_zmi.form import AdminDialogAddForm, AdminDialogEditForm |
|
47 from pyams_zmi.view import AdminView |
|
48 from pyramid.events import subscriber |
|
49 from pyramid.view import view_config |
|
50 from z3c.form import field |
|
51 from z3c.table.column import GetAttrColumn |
|
52 from zope.interface import implementer, Interface, Invalid |
|
53 |
|
54 from pyams_security import _ |
|
55 |
|
56 |
|
57 @viewlet_config(name='add-users-folder.menu', context=ISite, layer=IAdminLayer, |
|
58 view=SecurityManagerPluginsTable, manager=ISecurityManagerToolbarAddingMenu, |
|
59 permission='system.manage', weight=10) |
|
60 class UsersFolderAddMenu(ToolbarMenuItem): |
|
61 """Local users folder add menu""" |
|
62 |
|
63 label = _("Add local users folder...") |
|
64 label_css_class = 'fa fa-fw fa-user' |
|
65 url = 'add-users-folder.html' |
|
66 modal_target = True |
|
67 |
|
68 |
|
69 @pagelet_config(name='add-users-folder.html', context=ISite, layer=IPyAMSLayer, |
|
70 permission='system.manage') |
|
71 class UsersFolderAddForm(AdminDialogAddForm): |
|
72 """Users folder plug-in add form""" |
|
73 |
|
74 title = _("System security manager") |
|
75 legend = _("Add local users folder plug-in") |
|
76 icon_css_class = 'fa fa-fw fa-user' |
|
77 |
|
78 fields = field.Fields(IUsersFolderPlugin).omit('__name__', '__parent__') |
|
79 ajax_handler = 'add-users-folder.json' |
|
80 edit_permission = None |
|
81 |
|
82 def create(self, data): |
|
83 return UsersFolder() |
|
84 |
|
85 def add(self, plugin): |
|
86 context = query_utility(ISecurityManager) |
|
87 context[plugin.prefix] = plugin |
|
88 |
|
89 def nextURL(self): |
|
90 return absolute_url(self.context, self.request, 'security-manager.html') |
|
91 |
|
92 |
|
93 @view_config(name='add-users-folder.json', context=ISite, request_type=IPyAMSLayer, |
|
94 permission='system.manage', renderer='json', xhr=True) |
|
95 class UsersFolderAJAXAddForm(AJAXAddForm, UsersFolderAddForm): |
|
96 """users folder plug-in add form, AJAX handler""" |
|
97 |
|
98 |
|
99 @pagelet_config(name='properties.html', context=IUsersFolderPlugin, layer=IPyAMSLayer, |
|
100 permission='system.view') |
|
101 class UsersFolderEditForm(AdminDialogEditForm): |
|
102 """Users folder plug-in edit form""" |
|
103 |
|
104 @property |
|
105 def title(self): |
|
106 return self.context.title |
|
107 |
|
108 legend = _("Edit local users folder plug-in properties") |
|
109 icon_css_class = 'fa fa-fw fa-user' |
|
110 |
|
111 fields = field.Fields(IUsersFolderPlugin).omit('__name__', '__parent__') |
|
112 ajax_handler = 'properties.json' |
|
113 edit_permission = 'system.manage' |
|
114 |
|
115 def updateWidgets(self, prefix=None): |
|
116 super(UsersFolderEditForm, self).updateWidgets() |
|
117 self.widgets['prefix'].mode = DISPLAY_MODE |
|
118 |
|
119 |
|
120 @view_config(name='properties.json', context=IUsersFolderPlugin, request_type=IPyAMSLayer, |
|
121 permission='system.manage', renderer='json', xhr=True) |
|
122 class UsersFolderAJAXEditForm(AJAXEditForm, UsersFolderEditForm): |
|
123 """Users folder plug-in edit form, AJAX handler""" |
|
124 |
|
125 |
|
126 @adapter_config(name='security.menu', context=(IUsersFolderPlugin, IAdminLayer, Interface, ISecurityManagerMenu), |
|
127 provides=IMenuItem) |
|
128 @implementer(IObjectData) |
|
129 class UsersFolderContentsMenu(MenuItem): |
|
130 """Users folder contents menu""" |
|
131 |
|
132 url = 'contents.html' |
|
133 object_data = {'ams-target': '#content'} |
|
134 |
|
135 @property |
|
136 def label(self): |
|
137 return self.context.title |
|
138 |
|
139 |
|
140 @pagelet_config(name='search.html', context=IUsersFolderPlugin, layer=IPyAMSLayer, permission='system.view') |
|
141 class UsersFolderSearchView(AdminView, SearchView): |
|
142 """Users folder search view""" |
|
143 |
|
144 def __init__(self, context, request): |
|
145 super(UsersFolderSearchView, self).__init__(context, request) |
|
146 |
|
147 |
|
148 @adapter_config(context=(IUsersFolderPlugin, IAdminLayer, UsersFolderSearchView), provides=IPageHeader) |
|
149 class UsersFolderSearchViewHeaderAdapter(ContextRequestViewAdapter): |
|
150 """Users folder search view header adapter""" |
|
151 |
|
152 icon_class = 'fa fa-fw fa-user' |
|
153 |
|
154 @property |
|
155 def title(self): |
|
156 return self.context.title |
|
157 |
|
158 subtitle = _("Search users") |
|
159 |
|
160 |
|
161 @view_config(name='search-results.html', context=IUsersFolderPlugin, request_type=IPyAMSLayer, |
|
162 permission='system.view') |
|
163 class UsersFolderSearchResultsView(AdminView, SearchResultsView): |
|
164 """Users folder search results view table""" |
|
165 |
|
166 id = 'users_folder_search_table' |
|
167 title = _("Search results") |
|
168 cssClasses = {'table': 'table table-bordered table-striped table-hover table-tight datatable'} |
|
169 |
|
170 def __init__(self, context, request): |
|
171 super(UsersFolderSearchResultsView, self).__init__(context, request) |
|
172 apply_skin(self.request, 'PyAMS admin skin') |
|
173 |
|
174 |
|
175 @adapter_config(name='login', context=(IUsersFolderPlugin, IAdminLayer, UsersFolderSearchResultsView), |
|
176 provides=IColumn) |
|
177 class LoginColumn(GetAttrColumn): |
|
178 """Users login column""" |
|
179 |
|
180 _header = _("Login") |
|
181 attrName = 'login' |
|
182 weight = 5 |
|
183 |
|
184 @property |
|
185 def header(self): |
|
186 return self.request.localizer.translate(self._header) |
|
187 |
|
188 |
|
189 @adapter_config(name='name', context=(IUsersFolderPlugin, IAdminLayer, UsersFolderSearchResultsView), |
|
190 provides=IColumn) |
|
191 class NameColumn(GetAttrColumn): |
|
192 """Users name column""" |
|
193 |
|
194 _header = _("Name") |
|
195 attrName = 'title' |
|
196 weight = 10 |
|
197 |
|
198 @property |
|
199 def header(self): |
|
200 return self.request.localizer.translate(self._header) |
|
201 |
|
202 |
|
203 @adapter_config(name='email', context=(IUsersFolderPlugin, IAdminLayer, UsersFolderSearchResultsView), |
|
204 provides=IColumn) |
|
205 class EmailColumn(GetAttrColumn): |
|
206 """Users email column""" |
|
207 |
|
208 _header = _("E-mail address") |
|
209 attrName = 'email' |
|
210 weight = 20 |
|
211 |
|
212 @property |
|
213 def header(self): |
|
214 return self.request.localizer.translate(self._header) |
|
215 |
|
216 |
|
217 @adapter_config(name='registration_date', context=(IUsersFolderPlugin, IAdminLayer, UsersFolderSearchResultsView), |
|
218 provides=IColumn) |
|
219 class RegistrationDateColumn(GetAttrColumn): |
|
220 """Users registration date column""" |
|
221 |
|
222 _header = _("Registration date") |
|
223 weight = 30 |
|
224 |
|
225 def getValue(self, obj): |
|
226 dc = IZopeDublinCore(obj, None) |
|
227 if dc is not None: |
|
228 return format_datetime(dc.created) |
|
229 else: |
|
230 return '--' |
|
231 |
|
232 @property |
|
233 def header(self): |
|
234 return self.request.localizer.translate(self._header) |
|
235 |
|
236 |
|
237 @adapter_config(name='activation_date', context=(IUsersFolderPlugin, IAdminLayer, UsersFolderSearchResultsView), |
|
238 provides=IColumn) |
|
239 class ConfirmationDateColumn(GetAttrColumn): |
|
240 """Users activation date column""" |
|
241 |
|
242 _header = _("Activation date") |
|
243 weight = 40 |
|
244 |
|
245 def getValue(self, obj): |
|
246 if obj.activation_date: |
|
247 return format_datetime(obj.activation_date) |
|
248 else: |
|
249 return '--' |
|
250 |
|
251 @property |
|
252 def header(self): |
|
253 return self.request.localizer.translate(self._header) |
|
254 |
|
255 |
|
256 @viewlet_config(name='users-folder.toolbar.adding', context=IUsersFolderPlugin, |
|
257 view=UsersFolderSearchView.search_form_factory, layer=IAdminLayer, |
|
258 manager=IToolbarViewletManager, permission='system.manage') |
|
259 class UsersFolderAddAction(ToolbarAction): |
|
260 """Users folder adding action""" |
|
261 |
|
262 label = _("Add user") |
|
263 url = 'add-user.html' |
|
264 modal_target = True |
|
265 |
|
266 |
|
267 @pagelet_config(name='add-user.html', context=IUsersFolderPlugin, layer=IPyAMSLayer, permission='system.manage') |
|
268 class LocalUserAddForm(AdminDialogAddForm): |
|
269 """Local user add form""" |
|
270 |
|
271 @property |
|
272 def title(self): |
|
273 return self.context.title |
|
274 |
|
275 legend = _("Add new local user") |
|
276 icon_css_class = 'fa fa-fw fa-user' |
|
277 label_css_class = 'control-label col-md-4' |
|
278 input_css_class = 'col-md-8' |
|
279 |
|
280 fields = field.Fields(IUserRegistrationInfo).select('login', 'email', 'firstname', 'lastname', 'company_name') + \ |
|
281 field.Fields(ILocalUser).select('password_manager') + \ |
|
282 field.Fields(IUserRegistrationInfo).select('password', 'confirmed_password') + \ |
|
283 field.Fields(ILocalUser).select('wait_confirmation') |
|
284 |
|
285 ajax_handler = 'add-user.json' |
|
286 edit_permission = 'system.manage' |
|
287 |
|
288 def updateWidgets(self, prefix=None): |
|
289 super(LocalUserAddForm, self).updateWidgets() |
|
290 self.widgets['password'].input_css_class = 'col-md-4' |
|
291 self.widgets['confirmed_password'].input_css_class = 'col-md-4' |
|
292 |
|
293 def create(self, data): |
|
294 return User() |
|
295 |
|
296 def update_content(self, user, data): |
|
297 user.login = data.get('login') |
|
298 user.email = data.get('email') |
|
299 user.firstname = data.get('firstname') |
|
300 user.lastname = data.get('lastname') |
|
301 user.company_name = data.get('company_name') |
|
302 user.password_manager = data.get('password_manager') |
|
303 user.password = data.get('password') |
|
304 user.self_registered = False |
|
305 if data.get('wait_confirmation'): |
|
306 user.generate_secret() |
|
307 else: |
|
308 user.activation_date = datetime.utcnow() |
|
309 user.activated = True |
|
310 user.wait_confirmation = data.get('wait_confirmation') |
|
311 |
|
312 def add(self, user): |
|
313 self.context[user.login] = user |
|
314 |
|
315 |
|
316 @subscriber(IDataExtractedEvent, form_selector=LocalUserAddForm) |
|
317 def handle_new_user_data_extraction(event): |
|
318 """Handle new user form data extraction""" |
|
319 data = event.data |
|
320 if not data.get('login'): |
|
321 data['login'] = data['email'] |
|
322 folder = event.form.context |
|
323 if not folder.check_login(data.get('login')): |
|
324 event.form.widgets.errors += (Invalid(_("Specified login can't be used!")),) |
|
325 |
|
326 |
|
327 @view_config(name='add-user.json', context=IUsersFolderPlugin, request_type=IPyAMSLayer, |
|
328 permission='system.manage', renderer='json', xhr=True) |
|
329 class LocalUserAJAXAddForm(AJAXAddForm, LocalUserAddForm): |
|
330 """Local user add form, AJAX view""" |
|
331 |
|
332 def get_ajax_output(self, changes): |
|
333 translate = self.request.localizer.translate |
|
334 return {'status': 'success', |
|
335 'message': translate(_("User was created successfully"))} |
|
336 |
|
337 |
|
338 @pagelet_config(name='properties.html', context=ILocalUser, layer=IPyAMSLayer, permission='system.view') |
|
339 class LocalUserEditForm(AdminDialogEditForm): |
|
340 """Local user edit form""" |
|
341 |
|
342 @property |
|
343 def title(self): |
|
344 return self.context.title |
|
345 |
|
346 legend = _("Edit user properties") |
|
347 icon_css_class = 'fa fa-fw fa-user' |
|
348 label_css_class = 'control-label col-md-4' |
|
349 input_css_class = 'col-md-8' |
|
350 |
|
351 fields = field.Fields(ILocalUser).select('login', 'email', 'firstname', 'lastname', 'company_name', |
|
352 'self_registered', 'activation_hash', 'activation_date') |
|
353 |
|
354 ajax_handler = 'properties.json' |
|
355 edit_permission = 'system.manage' |
|
356 |
|
357 def updateWidgets(self, prefix=None): |
|
358 super(LocalUserEditForm, self).updateWidgets() |
|
359 self.widgets['self_registered'].mode = DISPLAY_MODE |
|
360 self.widgets['activation_hash'].mode = DISPLAY_MODE |
|
361 self.widgets['activation_date'].mode = DISPLAY_MODE |
|
362 |
|
363 |
|
364 @view_config(name='properties.json', context=ILocalUser, request_type=IPyAMSLayer, |
|
365 permission='system.manage', renderer='json', xhr=True) |
|
366 class LocalUserAJAXEditForm(AJAXEditForm, LocalUserEditForm): |
|
367 """Local user edit form, AJAX view""" |