7 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED |
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 |
8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS |
9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS |
10 # FOR A PARTICULAR PURPOSE. |
10 # FOR A PARTICULAR PURPOSE. |
11 # |
11 # |
12 |
|
13 __docformat__ = 'restructuredtext' |
|
14 |
12 |
15 from datetime import datetime |
13 from datetime import datetime |
16 from uuid import uuid4 |
14 from uuid import uuid4 |
17 |
15 |
18 from pyramid.location import lineage |
16 from pyramid.location import lineage |
22 from zope.interface import Interface |
20 from zope.interface import Interface |
23 from zope.lifecycleevent import ObjectCreatedEvent |
21 from zope.lifecycleevent import ObjectCreatedEvent |
24 from zope.location import locate |
22 from zope.location import locate |
25 |
23 |
26 from pyams_content.features.review.interfaces import IReviewComments |
24 from pyams_content.features.review.interfaces import IReviewComments |
27 from pyams_content.interfaces import CREATE_CONTENT_PERMISSION, MANAGE_CONTENT_PERMISSION, MANAGE_SITE_ROOT_PERMISSION, \ |
25 from pyams_content.interfaces import CREATE_CONTENT_PERMISSION, MANAGE_CONTENT_PERMISSION, \ |
28 PUBLISH_CONTENT_PERMISSION |
26 MANAGE_SITE_ROOT_PERMISSION, PUBLISH_CONTENT_PERMISSION |
29 from pyams_content.shared.common.interfaces import IBaseSharedTool, IManagerRestrictions, ISharedContent, \ |
27 from pyams_content.shared.common.interfaces import IBaseSharedTool, IContributorRestrictions, \ |
30 IWfSharedContent |
28 IManagerRestrictions, ISharedContent, IWfSharedContent |
31 from pyams_form.form import AJAXAddForm, ajax_config |
29 from pyams_form.form import AJAXAddForm, ajax_config |
32 from pyams_form.interfaces.form import IFormContextPermissionChecker, IWidgetsPrefixViewletsManager |
30 from pyams_form.interfaces.form import IFormContextPermissionChecker, IWidgetsPrefixViewletsManager |
33 from pyams_form.schema import CloseButton |
31 from pyams_form.schema import CloseButton |
34 from pyams_i18n.interfaces import II18n, II18nManager, INegotiator |
32 from pyams_i18n.interfaces import II18n, II18nManager, INegotiator |
35 from pyams_i18n.widget import I18nSEOTextLineFieldWidget |
33 from pyams_i18n.widget import I18nSEOTextLineFieldWidget |
42 from pyams_skin.page import DefaultPageHeaderAdapter |
40 from pyams_skin.page import DefaultPageHeaderAdapter |
43 from pyams_skin.table import DefaultElementEditorAdapter |
41 from pyams_skin.table import DefaultElementEditorAdapter |
44 from pyams_skin.viewlet.breadcrumb import BreadcrumbAdminLayerItem |
42 from pyams_skin.viewlet.breadcrumb import BreadcrumbAdminLayerItem |
45 from pyams_skin.viewlet.toolbar import ToolbarMenuDivider, ToolbarMenuItem |
43 from pyams_skin.viewlet.toolbar import ToolbarMenuDivider, ToolbarMenuItem |
46 from pyams_template.template import template_config |
44 from pyams_template.template import template_config |
47 from pyams_utils.adapter import ContextAdapter, ContextRequestAdapter, ContextRequestViewAdapter, adapter_config |
45 from pyams_utils.adapter import ContextAdapter, ContextRequestAdapter, ContextRequestViewAdapter, \ |
|
46 adapter_config |
48 from pyams_utils.interfaces import FORBIDDEN_PERMISSION |
47 from pyams_utils.interfaces import FORBIDDEN_PERMISSION |
49 from pyams_utils.registry import get_utility |
48 from pyams_utils.registry import get_utility |
50 from pyams_utils.request import check_request |
49 from pyams_utils.request import check_request |
51 from pyams_utils.traversing import get_parent |
50 from pyams_utils.traversing import get_parent |
52 from pyams_utils.url import absolute_url, generate_url |
51 from pyams_utils.url import absolute_url, generate_url |
53 from pyams_viewlet.viewlet import Viewlet, viewlet_config |
52 from pyams_viewlet.viewlet import Viewlet, viewlet_config |
54 from pyams_workflow.interfaces import IWorkflow, IWorkflowCommentInfo, IWorkflowInfo, IWorkflowPublicationInfo, \ |
53 from pyams_workflow.interfaces import IWorkflow, IWorkflowCommentInfo, IWorkflowInfo, \ |
55 IWorkflowState, IWorkflowVersions |
54 IWorkflowPublicationInfo, IWorkflowState, IWorkflowVersions |
56 from pyams_workflow.versions import WorkflowHistoryItem |
55 from pyams_workflow.versions import WorkflowHistoryItem |
57 from pyams_zmi.form import AdminDialogAddForm |
56 from pyams_zmi.form import AdminDialogAddForm |
58 from pyams_zmi.interfaces.menu import ISiteManagementMenu |
57 from pyams_zmi.interfaces.menu import ISiteManagementMenu |
59 from pyams_zmi.layer import IAdminLayer |
58 from pyams_zmi.layer import IAdminLayer |
|
59 |
|
60 |
|
61 __docformat__ = 'restructuredtext' |
60 |
62 |
61 from pyams_content import _ |
63 from pyams_content import _ |
62 |
64 |
63 |
65 |
64 class SharedContentAddForm(AdminDialogAddForm): |
66 class SharedContentAddForm(AdminDialogAddForm): |
106 self.context[uuid] = content |
108 self.context[uuid] = content |
107 IWorkflowVersions(content).add_version(wf_content, None) |
109 IWorkflowVersions(content).add_version(wf_content, None) |
108 IWorkflowInfo(wf_content).fire_transition('init', comment=wf_content.notepad) |
110 IWorkflowInfo(wf_content).fire_transition('init', comment=wf_content.notepad) |
109 |
111 |
110 def nextURL(self): |
112 def nextURL(self): |
111 return absolute_url(self.context, self.request, '{0}/++versions++/1/admin#properties.html'.format(self.__uuid)) |
113 return absolute_url(self.context, self.request, |
|
114 '{0}/++versions++/1/admin#properties.html'.format(self.__uuid)) |
112 |
115 |
113 |
116 |
114 class SharedContentAJAXAddForm(AJAXAddForm): |
117 class SharedContentAJAXAddForm(AJAXAddForm): |
115 """Shared event add form, JSON renderer""" |
118 """Shared event add form, JSON renderer""" |
116 |
119 |
119 'status': 'redirect', |
122 'status': 'redirect', |
120 'location': self.nextURL() |
123 'location': self.nextURL() |
121 } |
124 } |
122 |
125 |
123 |
126 |
124 @viewlet_config(name='wf-create-message', context=Interface, layer=IPyAMSLayer, view=SharedContentAddForm, |
127 @viewlet_config(name='wf-create-message', context=Interface, layer=IPyAMSLayer, |
|
128 view=SharedContentAddForm, |
125 manager=IWidgetsPrefixViewletsManager, weight=20) |
129 manager=IWidgetsPrefixViewletsManager, weight=20) |
126 @template_config(template='templates/wf-create-message.pt') |
130 @template_config(template='templates/wf-create-message.pt') |
127 class SharedContentAddFormMessage(Viewlet): |
131 class SharedContentAddFormMessage(Viewlet): |
128 """Shared content add form info message""" |
132 """Shared content add form info message""" |
129 |
133 |
151 |
155 |
152 @property |
156 @property |
153 def edit_permission(self): |
157 def edit_permission(self): |
154 workflow = IWorkflow(self.context) |
158 workflow = IWorkflow(self.context) |
155 state = IWorkflowState(self.context).state |
159 state = IWorkflowState(self.context).state |
156 if state in workflow.readonly_states: # access forbidden to all for archived contents |
160 if state in workflow.readonly_states: |
|
161 # forbidden access to all for archived contents |
157 return FORBIDDEN_PERMISSION |
162 return FORBIDDEN_PERMISSION |
158 elif state in workflow.protected_states: # webmaster can update published contents |
163 elif state in workflow.protected_states: |
|
164 # webmaster can update published contents |
159 return MANAGE_SITE_ROOT_PERMISSION |
165 return MANAGE_SITE_ROOT_PERMISSION |
160 else: |
166 else: |
161 request = check_request() |
167 request = check_request() |
162 if request.has_permission(MANAGE_SITE_ROOT_PERMISSION, self.context): # webmaster access |
168 if request.has_permission(MANAGE_SITE_ROOT_PERMISSION, self.context): |
|
169 # webmaster access |
163 return MANAGE_SITE_ROOT_PERMISSION |
170 return MANAGE_SITE_ROOT_PERMISSION |
164 principal_id = request.principal.id |
171 principal_id = request.principal.id |
165 if state in workflow.manager_states: # restricted manager access |
172 if state in workflow.manager_states: |
|
173 # restricted manager access |
166 if principal_id in self.context.managers: |
174 if principal_id in self.context.managers: |
167 return PUBLISH_CONTENT_PERMISSION |
175 return PUBLISH_CONTENT_PERMISSION |
168 for parent in lineage(self.context): |
176 for parent in lineage(self.context): |
169 manager_restrictions = IManagerRestrictions(parent, None) |
177 manager_restrictions = IManagerRestrictions(parent, None) |
170 if manager_restrictions is not None: |
178 if manager_restrictions is not None: |
175 request=request): |
183 request=request): |
176 return PUBLISH_CONTENT_PERMISSION |
184 return PUBLISH_CONTENT_PERMISSION |
177 else: |
185 else: |
178 return FORBIDDEN_PERMISSION |
186 return FORBIDDEN_PERMISSION |
179 else: |
187 else: |
180 if principal_id in self.context.owner | self.context.contributors | self.context.managers: |
188 # access is granted to content's owner and designated contributors or managers |
|
189 if principal_id in self.context.owner | \ |
|
190 self.context.contributors | \ |
|
191 self.context.managers: |
181 return MANAGE_CONTENT_PERMISSION |
192 return MANAGE_CONTENT_PERMISSION |
|
193 # check if current principal can manage owner's contents |
|
194 for parent in lineage(self.context): |
|
195 contrib_restrictions = IContributorRestrictions(parent, None) |
|
196 if contrib_restrictions is not None: |
|
197 user_restrictions = contrib_restrictions.get_restrictions(principal_id) |
|
198 if user_restrictions: |
|
199 if user_restrictions.check_access(self.context, |
|
200 permission=MANAGE_CONTENT_PERMISSION, |
|
201 request=request): |
|
202 return MANAGE_CONTENT_PERMISSION |
|
203 restrictions = IContributorRestrictions(self.context).get_restrictions(principal_id) |
|
204 if restrictions and restrictions.check_access(self.context, |
|
205 permission=MANAGE_CONTENT_PERMISSION, |
|
206 request=request): |
|
207 return MANAGE_CONTENT_PERMISSION |
|
208 # check if current principal can manage content's due to manager restrictions |
182 for parent in lineage(self.context): |
209 for parent in lineage(self.context): |
183 manager_restrictions = IManagerRestrictions(parent, None) |
210 manager_restrictions = IManagerRestrictions(parent, None) |
184 if manager_restrictions is not None: |
211 if manager_restrictions is not None: |
185 user_restrictions = manager_restrictions.get_restrictions(principal_id) |
212 user_restrictions = manager_restrictions.get_restrictions(principal_id) |
186 if user_restrictions: |
213 if user_restrictions: |
187 if user_restrictions.check_access(self.context, |
214 if user_restrictions.check_access(self.context, |
188 permission=MANAGE_CONTENT_PERMISSION, |
215 permission=MANAGE_CONTENT_PERMISSION, |
189 request=request): |
216 request=request): |
190 return MANAGE_CONTENT_PERMISSION |
217 return MANAGE_CONTENT_PERMISSION |
191 else: |
|
192 return FORBIDDEN_PERMISSION |
|
193 restrictions = IManagerRestrictions(self.context).get_restrictions(principal_id) |
218 restrictions = IManagerRestrictions(self.context).get_restrictions(principal_id) |
194 if restrictions and restrictions.check_access(self.context, |
219 if restrictions and restrictions.check_access(self.context, |
195 permission=MANAGE_CONTENT_PERMISSION, |
220 permission=MANAGE_CONTENT_PERMISSION, |
196 request=request): |
221 request=request): |
197 return MANAGE_CONTENT_PERMISSION |
222 return MANAGE_CONTENT_PERMISSION |
247 # |
272 # |
248 # Duplication menus and views |
273 # Duplication menus and views |
249 # |
274 # |
250 |
275 |
251 @viewlet_config(name='duplication.divider', context=IWfSharedContent, layer=IPyAMSLayer, |
276 @viewlet_config(name='duplication.divider', context=IWfSharedContent, layer=IPyAMSLayer, |
252 view=Interface, manager=IContextActions, permission=CREATE_CONTENT_PERMISSION, weight=49) |
277 view=Interface, manager=IContextActions, permission=CREATE_CONTENT_PERMISSION, |
|
278 weight=49) |
253 class WfSharedContentDuplicationMenuDivider(ToolbarMenuDivider): |
279 class WfSharedContentDuplicationMenuDivider(ToolbarMenuDivider): |
254 """Shared content duplication menu divider""" |
280 """Shared content duplication menu divider""" |
255 |
281 |
256 |
282 |
257 @viewlet_config(name='duplication.menu', context=IWfSharedContent, layer=IPyAMSLayer, |
283 @viewlet_config(name='duplication.menu', context=IWfSharedContent, layer=IPyAMSLayer, |
258 view=Interface, manager=IContextActions, permission=CREATE_CONTENT_PERMISSION, weight=50) |
284 view=Interface, manager=IContextActions, permission=CREATE_CONTENT_PERMISSION, |
|
285 weight=50) |
259 class WfSharedContentDuplicateMenu(ToolbarMenuItem): |
286 class WfSharedContentDuplicateMenu(ToolbarMenuItem): |
260 """Shared content duplication menu item""" |
287 """Shared content duplication menu item""" |
261 |
288 |
262 label = _("Duplicate content...") |
289 label = _("Duplicate content...") |
263 label_css_class = 'fa fa-fw fa-files-o' |
290 label_css_class = 'fa fa-fw fa-files-o' |
324 source_state = IWorkflowState(self.context) |
351 source_state = IWorkflowState(self.context) |
325 state = IWorkflowState(new_version) |
352 state = IWorkflowState(new_version) |
326 state.history.clear() |
353 state.history.clear() |
327 history = WorkflowHistoryItem(date=datetime.utcnow(), |
354 history = WorkflowHistoryItem(date=datetime.utcnow(), |
328 principal=self.request.principal.id, |
355 principal=self.request.principal.id, |
329 comment=translate(_("Clone created from version {source} of {oid} " |
356 comment=translate( |
330 "(in « {state} » state)")).format( |
357 _("Clone created from version {source} of {oid} " |
|
358 "(in « {state} » state)")).format( |
331 source=source_state.version_id, |
359 source=source_state.version_id, |
332 oid=ISequentialIdInfo(self.context).get_short_oid(), |
360 oid=ISequentialIdInfo(self.context).get_short_oid(), |
333 state=translate(IWorkflow(self.context).get_state_label(source_state.state))) |
361 state=translate(IWorkflow(self.context).get_state_label( |
|
362 source_state.state))) |
334 ) |
363 ) |
335 state.history.append(history) |
364 state.history.append(history) |
336 history = WorkflowHistoryItem(date=datetime.utcnow(), |
365 history = WorkflowHistoryItem(date=datetime.utcnow(), |
337 target_state=workflow.initial_state, |
366 target_state=workflow.initial_state, |
338 principal=self.request.principal.id, |
367 principal=self.request.principal.id, |
369 item_dc = IZopeDublinCore(item) |
398 item_dc = IZopeDublinCore(item) |
370 if item_dc.modified and (item_dc.modified > IZopeDublinCore(content).created): |
399 if item_dc.modified and (item_dc.modified > IZopeDublinCore(content).created): |
371 translate = self.request.localizer.translate |
400 translate = self.request.localizer.translate |
372 value += '<i class="fa fa-fw fa-circle txt-color-orange hint align-middle padding-left-5" ' \ |
401 value += '<i class="fa fa-fw fa-circle txt-color-orange hint align-middle padding-left-5" ' \ |
373 'title="{0}" data-ams-hint-gravity="w"></i>'.format( |
402 'title="{0}" data-ams-hint-gravity="w"></i>'.format( |
374 translate(_("Created or modified in this version"))) |
403 translate(_("Created or modified in this version"))) |
375 return value |
404 return value |